list p=16C64,t=ON,c=132,n=80 title "An all software PWM with 32 states" radix dec ;******************************************************************************** ; ; T. Scott Dattalo ; ; 25APR97 - Initial version ; ; The purpose of this program is to generate a Pulse Width Modulated ;square wave with single instruction cycle resolution. This version only ;supports 32 PWM levels. 14 cycles are available every iteration for ;other tasks. ; ; The algorithm is a state machine based on indirect goto's and tables. ;The 32 PWM levels are divided into 4 groups of 8. For each group, ;there are three states through which the state machine will pass. The ;states contain the addresses to where the goto's branch. The tables ;provide a convenient way to create a variable delay. ; ; As an example, consider the execution sequence required to generate a ;10-cycle wide pulse. The state initialization is: ; ;pw10 MOVLW pulse_on2off+7 ; MOVWF state_3 ; MOVLW variable_delay+1 ; MOVWF state_1 ; MOVLW pw9_16 ; MOVWF state_2 ; nop ; goto pw9_16 ; ;This puts the addresses of the start of the three tasks into the three ;state variables. Then code like this: ; ; MOVF state_n,W ; MOVWF PCL ; ; will branch to that state. The offsets added to the program lables ;provide the variable delay needed to adjust the pulse width. For ;example, when the contents of state_3 are placed into PCL, execution ;will begin 7 instructions after the label 'pulse_on2off'. ; ; The sequence of execution for the pw10 pulse is this: ; ; pulse_on2off+7 -> variable_delay+1 -> pw9_16 ---+ ; ^-------<-----------------<---------------------------| ; ;This sequence executes in exactly 32 cycles. On the 0'th the PWM ;output is driven high and on the 10'th cycle it's driven low. ;The "0'th" cycle is 'BSF' instruction in the pw9_16 code segment. ;the 10'th cycle is the 'BCF' instruction in the pulse_on2off code ;segment. ; include "P16C64.INC" __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF& _HS_OSC START_OF_RAM_LO equ 0x20 END_OF_RAM_LO equ 0x7f START_OF_RAM_HI equ 0xa0 END_OF_RAM_HI equ 0xbf ;*********************************************************************** ;PWM Equates PWM_PORT EQU PORTA PWM_BIT EQU 0 cblock START_OF_RAM_LO state_1,state_2,state_3 endc ;*********************************************************************** ORG 0 ;Reset Vector GOTO Main ;*********************************************************************** ;This segment will drive the PWM output high (or keep it high if it ;was driven earlier) and then 1 to 8 cycles later drive it low. It then ;branches to the address contained in state_1. pulse_on2off BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT MOVF state_1,W MOVWF PCL ;This segment will drive the PWM output low (or keep it low if it ;was driven earlier) and then 1 to 8 cycles later drive it high. It then ;branches to the address contained in state_1. pulse_off2on BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BCF PWM_PORT,PWM_BIT BSF PWM_PORT,PWM_BIT NOP MOVF state_1,W MOVWF PCL variable_delay NOP NOP NOP NOP NOP NOP NOP MOVF state_2,W MOVWF PCL delay16 ;(actually, a delay of 17 cycles...) goto $+1 ;These 14 free cycles will be executed goto $+1 ;when the PW is between 0 and 7 cycles goto $+1 ;or between 25 and 31 cycles goto $+1 goto $+1 goto $+1 goto $+1 MOVF state_3,W MOVWF PCL pw9_16 goto $+1 ;These 9 free cycles will be executed goto $+1 ;when the PW is between 9 and 16 cycles goto $+1 goto $+1 NOP BSF PWM_PORT,PWM_BIT goto $+1 ;And here are 5 more free cycles goto $+1 ; NOP MOVF state_3,W MOVWF PCL pw17_24 NOP ;one free cycle BSF PWM_PORT,PWM_BIT goto $+1 ;These 13 free cycles will be executed goto $+1 ;when the PW is between 17 and 24 cycles goto $+1 ; goto $+1 ; goto $+1 goto $+1 NOP MOVF state_3,W MOVWF PCL Main BSF STATUS,RP0 ;Point to BANK 1 MOVLW 1 << PWM_BIT ;Make the PWM I/O bit an output MOVWF PWM_PORT ;Actually, this is the TRIS for the PWM_PORT BCF STATUS,RP0 ;Point to BANK 0 goto pw0 pw0 MOVLW delay16 ;A zero wide pulse is always low MOVWF state_3 ;So this special case will clear the MOVWF state_1 ;PWM output and endlessly loop in the nop ;delay16 state nop nop nop goto pulse_on2off+8 pw1 MOVLW pulse_on2off+7 MOVWF state_3 MOVLW variable_delay+0 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 pw2 MOVLW pulse_on2off+6 MOVWF state_3 MOVLW variable_delay+0 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 pw3 MOVLW pulse_on2off+6 MOVWF state_3 MOVLW variable_delay+1 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 pw4 MOVLW pulse_on2off+5 MOVWF state_3 MOVLW variable_delay+2 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 pw9 MOVLW pulse_on2off+8 MOVWF state_3 MOVLW variable_delay+0 MOVWF state_1 MOVLW pw9_16 MOVWF state_2 nop goto pw9_16 pw10 MOVLW pulse_on2off+7 MOVWF state_3 MOVLW variable_delay+1 MOVWF state_1 MOVLW pw9_16 MOVWF state_2 nop goto pw9_16 pw16 MOVLW pulse_on2off+1 MOVWF state_3 MOVLW variable_delay+7 MOVWF state_1 MOVLW pw9_16 MOVWF state_2 nop goto pw9_16 pw17 MOVLW pulse_on2off+8 MOVWF state_3 MOVLW variable_delay+0 MOVWF state_1 MOVLW pw17_24 MOVWF state_2 nop goto pw17_24 pw24 MOVLW pulse_on2off+1 MOVWF state_3 MOVLW variable_delay+7 MOVWF state_1 MOVLW pw17_24 MOVWF state_2 nop goto pw17_24 pw25 MOVLW pulse_off2on+25-24 MOVWF state_3 MOVLW variable_delay+32-25 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 pw31 MOVLW pulse_off2on+31-24 MOVWF state_3 MOVLW variable_delay+32-31 MOVWF state_1 MOVLW delay16 MOVWF state_2 nop goto delay16 END