NEW! 416028 |
This High Voltage PWM Prototyping tool generates a 6-bit PWM on 1 channel with a Duty Cycle of 0/64th to 64/64th, settable with pot at a Frequency of 100Hz to 279Hz, settable with pot and any Voltage from 1.25V - 32V, settable with pot.
A standard 2x16 LCD display shows the current Duty Cycle, Frequency and Voltage.
Although developed for a specific application (excitation of a particular sensor), the principle can be used to generate general low-frequency PWM for motor control and other high power applications. Could also be used as a LED dimmer. Even a mains dimmer if synched to the 60Hz/50Hz mains (eg run at 120 or 100Hz), with the IRQ count as the turn-on timer. See http://home.clear.net.nz/pages/joecolquitt/txless.html for a simple synch circuit.
Pot readings and LCD display are updated during TMR0 count and are transparent to the timing. 1 * IRQ may be long enough to separate LCD writes (40us), 2 * IRQ are allowed here (112us)
Several functions are currently de-activated, hence the unused "Burst Length", "Ext Trigger" and 4017 "interval/burst" display. When activated, and included into the pr_irq section, they will not affect timing at all because of above transparency. Again, this project is being published to share the commonly useful parts of a device that was developed for a specific application.
As only half the cycle of 64 TMR0 IRQs are used, it would be easy to add pots for more independently settable PWM outputs. Include in the pr_irq section. Also, the bin2bcd & ascii16 calls are short enough that the s/w could be re-arranged to make better use of IRQ time.
; HV_PWM_proto.asm ; ; 22nd October 2007, rev 07/08 ; ;joecolquitt@clear.net.nz ;www.piclist.com list P = 18F2520 include "P18F2520.inc" ;RAM_START EQU H'0000' ;START_VECTOR EQU H'00000' ;PROG_START EQU H'00020' ;LOW_IRQ_VECTOR EQU H'00018' ; ;add to P18F2520.inc or add to program errorlevel -305, -302, -306 ;MPLAB error suppression ;Program purpose - ; ;6-bit PWM, 1 channel ;Duty Cycle, 0/64th to 64/64th, settable with pot ;Frequency, 100Hz to 279Hz, settable with pot ;Voltage, 1.25V - 32V, settable with pot ;LCD display ;Output - logic level on LATC,7 ; ;Although developed for a specific application, the principle ;can be used to generate general low-frequency PWM. Lower than ;that achievable by using the PIC's PWM hardware ; ;Pot readings and LCD display are updated during TMR0 count ;and are transparent to the timing. 1 * IRQ may be long enough ;to separate LCD writes (40us), 2 * IRQ are allowed here (112us) ; ;Several functions are currently de-activated. When activated, ;and included into the pr_irq section, they will not affect ;timing at all because of above transparency ;As only half the cycle of 64 TMR0 IRQs are used, it would be ;easy to add pots for more independently settable PWM outputs ;Include in the pr_irq section ; ;bin2bcd & ascii16 calls are short enough that the s/w could be ;re-arranged to make better use of IRQ time ; ;================================================= CONFIG OSC=HSPLL ;10MHz * 4, IC = 100ns CONFIG BOREN=OFF, PWRT=ON CONFIG WDT=OFF CONFIG CCP2MX=PORTC CONFIG PBADEN = OFF CONFIG STVREN=ON, LVP=OFF, DEBUG=OFF CONFIG CP0=OFF, CP1=OFF, CP2=OFF, CP3=OFF CONFIG CPB=OFF, CPD=OFF CONFIG WRT0=OFF, WRT1=OFF, WRT2=OFF, WRT3=OFF CONFIG WRTC=OFF, WRTB=OFF,WRTD=OFF CONFIG EBTR0=OFF, EBTR1=OFF, EBTR2=OFF, EBTR3=OFF CONFIG EBTRB=OFF CONFIG MCLRE=ON ;================= clrc macro bcf status,0 endm skpc macro btfss status,0 endm skpnc macro btfsc status,0 endm skpnz macro btfsc status,2 endm wr_space macro ;write space to LCD movlw 0x20 call write_d ;display data endm clrscrn macro ;clear LCD movlw 0x01 call write_c ;command data call ms01 ;processing delay call ms01 endm mov macro litval,file movlw litval movwf file endm movfw macro litval movf litval,w endm lcd_pos macro litval ;set print position movlw litval-1 call address endm disp macro litval ;display character movlw litval call write_d endm dispw macro ;display WREG contents call write_d endm display macro var ;load TBLPTR with string address movlw upper(var) movwf tblptru movlw high(var) movwf tblptrh movlw low(var) movwf tblptrl tblrd*+ call get_txt endm usec macro ;1us delay movff temp0,temp0 movff temp0,temp0 movff temp0,temp0 movff temp0,temp0 movff temp0,temp0 endm ;================= ;A2D channel selection + set go/done anch0 macro movlw b'00000001' movwf adcon0 endm anch1 macro movlw b'00000101' movwf adcon0 endm anch2 macro movlw b'00001001' movwf adcon0 endm anch3 macro movlw b'00001101' movwf adcon0 endm anch4 macro movlw b'00010001' movwf adcon0 endm ;================= cblock ram_start dg1 ;display dg2 dg3 dg4 dg5 d100 d010 d011 kzero dl_cnt1 ;delay dl_cnt2 lo ;scratch hi temp0 temp1 temp2 w_temp s_temp b_temp flags char_lo char_hi adch_sel ;ADC select irq_cnt ;IRQ counter reload1_lo ;timer reloads reload1_hi reload2_lo reload2_hi duty_on ;duty cycle duty_lo ;printable characters duty_hi freq_lo freq_hi burst_lo burst_hi ival_lo ival_hi rand_lo rand_hi div_in_hi ;/5.7 division div_in_lo print_buffer:12 ;0 duty cycle print position ;1-3 Duty Cycle % ;4 frequency print position ;5-7 Frequency ;8 volt print position ;9-11 Volts endc ;================= ; 1 14 ;LCD pins 0V V+ VO RS R/W En DB0-DB7 ;PortB 0-7 - LCD data #define trig1 latc,0 ;trigger input 1 - de-activated #define trig2 latc,1 ;trigger input 2 - de-activated #define strobe latc,2 ;strobe drive - de-activated #define dr_4017 latc,3 ;4017 drive - de-activated #define rs latc,4 ;LCD #define rw latc,5 ;LCD - not strictly necessary #define en latc,6 ;LCD #define pulse latc,7 ;output #define dc status,1 ln1 = 0x00 ;LCD line1 address ln2 = 0x40 ;LCD line2 address show_dc = .2+ln1 ;duty cycle print position show_hz = .8+ln1 ;frequency print position show_volt = .2+ln2 ;voltage display position show_mode = .9+ln2 ;mode display position ad_in_n = -.5 ;number of ADC channels to read (-ve) ad_in_p = .5 ;number of ADC channels to read (+ve) strings = 0x800 ;display string storage address ;================================================ org start_vector goto init org low_irq_vector goto isr org prog_start init clrf lata clrf latb clrf latc clrf porta clrf portb clrf portc clrf intcon clrf intcon2 clrf intcon3 call clr_ram ;clear all RAM mov b'00111111',trisa ; -- OSC1/OSC2 ; 1 AN4 - volts ; 1 random switch ; 1 AN3 - duty ; 1 AN2 - frequency ; 1 AN1 - interval ; 1 AN0 - burst mov b'00000000',trisb ;LCD data mov b'00000011',trisc ; 0 LCD EN ; 0 LCD RW ; 0 LCD RS ; 0 LED ; 0 4017 drive ; 0 strobe drive ; 1 trigger2 ; 1 trigger1 mov b'00001010',adcon1 ; -- n/a ; 0 Vref- = Vss ; 0 Vref+ = Vdd ; 1010 AN0-AN4 as analogue i/p mov b'10110110',adcon2 ; 1 right-justified ; - n/a ; 110 acquisition time select, 16 Tad ; 110 clock select, Fosc/64 (40MHz max) mov b'00000111',cmcon ;comparators off mov b'00001000',t0con ; 0 timer off ; 0 16-bit ; 0 internal clock ; 0 ; 1 no pre-scaler ; 000 pre-scaler division call ms100 ;LCD power up delay ;---------------- mov ad_in_n,adch_sel ;AD channel select index clrf irq_cnt ;IRQ counter ;update duty cycle every 64 IRQs mov show_dc,print_buffer+.0 ;LCD print positions mov show_hz,print_buffer+.4 mov show_volt,print_buffer+.8 mov 0x00,reload1_lo ;initial TMR0 re-load value mov 0xfd,reload1_hi ;---------------- movff reload1_hi,tmr0h movff reload1_lo,tmr0l call lcd_init call ms100 ;Title screen clrscrn display title1 call line2 call us100 display title2 call one_sec ;Working screen clrscrn display working1 call line2 call us100 display working2 call one_sec lfsr fsr1,print_buffer ;initialise clrf irq_cnt bcf intcon,tmr0if bsf intcon,tmr0ie ;enable TMR0 IRQ bsf intcon,gie bsf t0con,tmr0on ;TMR0 on bsf adcon0,adon ;ADC on ;================================================ ; Main loop ;================================================ main nop bra main ;================================================ ; Timer interrupt ;================================================ isr bcf intcon,tmr0if movwf w_temp movff status,s_temp movff bsr,b_temp movff reload1_hi,tmr0h ;reload timer movff reload1_lo,tmr0l incf irq_cnt ;update duty cycle if IRQ count = 64 btfss irq_cnt,6 ;if 64 bra tst_on ;else update other channels clrf irq_cnt ;reset IRQ counter lfsr fsr1,print_buffer ;reset display data pointer bra duty ;get duty pot reading ;test if this is an 'on' slice tst_on bcf pulse ;pin low default tstfsz duty_on ;keep off if not 'on' slice decf duty_on ;else decrement 'on' slices tstfsz duty_on ;skip if not 'on' bsf pulse ;o/p on btfsc irq_cnt,0 ;no action at odd IRQs bra isr_exit movlw ad_in_p*2 cpfslt irq_cnt bra pr_irq ;if irq_cnt > pots*2, update LCD inc_cnt incfsz adch_sel ;increment ADC channel counter bra test_ch mov ad_in_n,adch_sel ;reset if 0 ;================================================ ; Read analogue inputs on rotation ;================================================ test_ch movlw ad_in_n+.0 xorwf adch_sel,w skpnz bra volts ;measure volts movlw ad_in_n+.1 xorwf adch_sel,w bz freq ;check frequency pot movlw ad_in_n+.2 xorwf adch_sel,w bz brst ;check burst length pot movlw ad_in_n+.3 xorwf adch_sel,w skpnz goto ival ;check interval length pot movlw ad_in_n+.4 xorwf adch_sel,w skpnz goto random ;check random interval pot ;update display ; ;eg if number of pots = 5 ; ;IRQ10, set display position ;IRQ12,14,16 Duty Cycle characters. Could be just 00 - 64 ;IRQ18, set display position ;IRQ20,22,24 Frequency characters ;IRQ26, set print position ;IRQ28,30,32 Voltage characters. 2 for whole volts < 100, more if DP added ;> 32, exit pr_irq movlw (ad_in_p*2)+.0 ;= 10 if pots = 5 xorwf irq_cnt,w bnz pirq12 movfw postinc1 ;set print position call address bra isr_exit pirq12 movlw (ad_in_p*2)+.2 xorwf irq_cnt,w bnz pirq14 movfw postinc1 ;duty cycle characters dispw bra isr_exit pirq14 movlw (ad_in_p*2)+.4 xorwf irq_cnt,w bnz pirq16 movfw postinc1 dispw bra isr_exit pirq16 movlw (ad_in_p*2)+.6 xorwf irq_cnt,w bnz pirq18 movfw postinc1 dispw bra isr_exit pirq18 movlw (ad_in_p*2)+.8 xorwf irq_cnt,w bnz pirq20 movfw postinc1 ;IRQ#18, set print position call address bra isr_exit pirq20 movlw (ad_in_p*2)+.10 xorwf irq_cnt,w bnz pirq22 movfw postinc1 ;frequency characters dispw bra isr_exit pirq22 movlw (ad_in_p*2)+.12 xorwf irq_cnt,w bnz pirq24 movfw postinc1 dispw bra isr_exit pirq24 movlw (ad_in_p*2)+.14 xorwf irq_cnt,w bnz pirq26 movfw postinc1 dispw bra isr_exit pirq26 movlw (ad_in_p*2)+.16 xorwf irq_cnt,w bnz pirq28 movfw postinc1 ;IRQ#26, set print position call address bra isr_exit pirq28 movlw (ad_in_p*2)+.18 xorwf irq_cnt,w bnz pirq30 movfw postinc1 ;voltage characters dispw bra isr_exit pirq30 movlw (ad_in_p*2)+.20 xorwf irq_cnt,w bnz pirq32 movfw postinc1 dispw bra isr_exit pirq32 movlw (ad_in_p*2)+.22 xorwf irq_cnt,w bnz isr_exit movfw postinc1 dispw bra isr_exit ;================== ;Pot varies TMR0 IRQ from 56us to 156us ; ;156us * 64 = 9984 = 1000000/9984 = 100Hz ; 56us * 64 = 3584 = 1000000/3584 = 279Hz ;Set frequency freq anch2 ;frequency pot bsf adcon0,go_done btfsc adcon0,go_done bra $-2 clrc movff adresl,reload1_lo ;ADC result low -> timer low movlw 0xfa ;TMR0H offset (ie minimum value) addwf adresh,w ;add movwf reload1_hi ;ADC result high -> timer high ;divide pot reading by 5.7, add 100, convert to ASCII ;conversion time 23us movff adresl,div_in_lo movff adresh,div_in_hi call div5pt7 movff div_in_lo,lo movff div_in_hi,hi call ascii16 ;convert to 5-digit ASCII movff dg3,print_buffer+.5 ;use 3 LSD (ignore leading '00') movff dg4,print_buffer+.6 movff dg5,print_buffer+.7 bra isr_exit ;================== brst bra isr_exit ;burst length, de-activated anch0 btfsc adcon0,go_done bra $-2 movff adresh,burst_lo movff adresl,burst_hi bra isr_exit ;================== ival bra isr_exit ;burst interval, de-activated anch1 btfsc adcon0,go_done bra $-2 movff adresh,ival_lo movff adresl,ival_hi bra isr_exit ;================== random bra isr_exit ;random burst timing, de-activated anch4 btfsc adcon0,go_done bra $-2 movff adresh,rand_lo movff adresl,rand_hi bra isr_exit ;================== ;Set on:off duty cycle duty anch3 ;duty cycle pot bsf adcon0,go_done btfsc adcon0,go_done bra $-2 ;ADC/16 (= X 64ths on), result in duty_on ;execution time 6us movff adresl,duty_on movff adresh,temp2 swapf duty_on,w ;low nybble temp2 + high nybble duty_on andlw 0x0f movwf duty_on swapf temp2,w iorwf duty_on ;number of slices on out of 64 movff duty_on,temp1 ;approximate * 1.5 -> percent display clrc rrcf temp1,w addwf duty_on,w call bin2bcd ;convert to 3-digit ASCII mov "0",print_buffer+.1 ;digit1 always '0' movff d010,print_buffer+.2 ;use 2 LSD (ignore leading '000') movff d011,print_buffer+.3 bra isr_exit ;ADC result is the measured voltage, ascii16 routine converts ;it to ASCII ;Manipulate ADC result and ASCII string (eg insert decimal point) ;to get desired display output ;Maths here depends on resistor divider on high voltage and ;high voltage upper limit ;eg ;high voltage = 30.72V (30720mV) ;resistor divider = 5k:1k -> 5V max at PIC ADC ;ADC reading = 0x0200 (half-way to 0x03FF) ;* 3 = 0x0600 ;Convert to ASCII = 01536 (dg1....dg5) ;Take dg2 and dg3 = 15 ;Similarly 0x0100 -> 00768 (round up to 8 or insert DP) volts anch4 bsf adcon0,go_done btfsc adcon0,go_done bra $-2 movff adresl,lo movff adresh,hi clrc rlcf lo rlcf hi movfw adresl addwf lo movfw adresh addwfc hi call ascii16 ;convert to 5-digit ASCII mov "0",print_buffer+.9 ;digit1 always '0' movff dg2,print_buffer+.10 ;use only 2 digits for whole volts movff dg3,print_buffer+.11 isr_exit movff b_temp,bsr movfw w_temp movff s_temp,status retfie ;================================================ ; LCD commands (8-bit) ;================================================ line1 movlw 0x00 ;line 1, column 0 call address return line2 movlw 0x40 ;line 2, column 0 call address return address addlw 0x80 ;set high bit of address command call write_c return write_c bcf rs ;write command bra d_out write_d bsf rs ;write data usec d_out movwf latb ;write to port, no Busy test usec ;separate writes with 2 * IRQs bcf rw ;typical write completion time is 40us usec bsf en usec bcf en usec return ;================================================ ; Initialise LCD screen (8-bit) ;================================================ lcd_init bcf rs ;command usec call write30 call ms01 ;delay > 4.1ms call ms01 call ms01 call ms01 call ms01 call write30 call ms01 ;delay > 100us call write30 call ms01 ;delay > 100us movlw 0x38 call wr_c_del ;include 100us delay movlw 0x0c call wr_c_del movlw 0x01 call wr_c_del movlw 0x06 call wr_c_del return write30 movwf latb usec bsf en usec bcf en usec return wr_c_del call write_c call us100 return ;================================================ ; Convert 8-bit data to ASCII for LCD ;================================================ ;execution time 4us ;data in W bin2bcd movwf temp1 clrf d100 swapf temp1,w addwf temp1,w andlw b'00001111' btfsc dc addlw 0x16 btfsc dc addlw 0x06 addlw 0x06 btfss dc addlw -0x06 btfsc temp1,4 addlw 0x16 - 1 + 0x06 btfss dc addlw -0x06 btfsc temp1,5 addlw 0x30 btfsc temp1,6 addlw 0x60 btfsc temp1,7 addlw 0x20 addlw 0x60 rlcf d100 btfss d100,0 addlw -0x60 movwf d011 btfsc temp1,7 incf d100 swapf d011,w andlw 0x0f movwf d010 movlw 0x0f andwf d011,w movwf d011 movlw 0x30 ;convert to ASCII addwf d100 addwf d010 addwf d011 return ;================================================ ; Convert 16-bit data to ASCII for LCD ;================================================ ;execution time 18us ;data in hi:lo ascii16 nop radix dec ;base 10 clrf kzero clrf temp0 lfsr fsr0,dg1 bra $+4 sub10k incf temp0 movlw 10000 & 255 subwf lo IFNDEF known_zero movlw 10000 >> 8 skpc movlw (10000>>8)+1 subwf hi ELSE rlcf kzero,w sublw (10000>>8)+1 subwf hi ENDIF bc sub10k call out_temp mov 10,temp0 add1K decf temp0 movlw 1000 & 255 addwf lo IFNDEF kzero movlw 1000 >> 8 skpnc movlw (1000>>8)+1 addwf hi ELSE rlcf kzero,w addlw 1000 >> 8 addwf hi ENDIF bnc add1k call out_temp clrf temp0 movlw 100 bra $+4 sub100 incf temp0 subwf lo skpnc bra sub100 decf hi btfss hi,7 bra sub100 call out_temp mov 10,temp0 add10 decf temp0 addwf lo bnc add10 call out_temp ;convert and store call out_lo ;convert and store radix hex return ;convert to ASCII and store out_temp movfw temp0 addlw 0x30 ;add 0x30 to convert to ASCII movwf postinc0 return out_lo movfw lo addlw 0x30 movwf indf0 return ;================================================ ; Divide frequency pot by 5.7, add 100 ; to get 100Hz to 279Hz display reading ;================================================ ;execution time 4.7us div5pt7 movff div_in_hi,temp0 movf div_in_lo,w movwf temp1 clrc rrcf div_in_hi rrcf div_in_lo clrc rrcf div_in_hi rrcf div_in_lo addwf div_in_lo movf temp0,w skpnc incfsz temp0,w addwf div_in_hi rrcf div_in_hi rrcf div_in_lo movf temp1,w addwf div_in_lo movf temp0,w skpnc incfsz temp0,w addwf div_in_hi rrcf div_in_hi rrcf div_in_lo clrc rrcf div_in_hi rrcf div_in_lo movf temp1,w addwf div_in_lo movf temp0,w skpnc incfsz temp0,w addwf div_in_hi rrcf div_in_hi rrcf div_in_lo clrc rrcf div_in_hi rrcf div_in_lo clrc rrcf div_in_hi rrcf div_in_lo movlw .100 addwf div_in_lo clrf wreg addwfc div_in_hi return ;================================================ ; Fetch string for LCD ;================================================ ; LCD text strings get_txt movfw tablat ;get characters until btfsc wreg,7 ;W > 0x7f (ie FF terminator) return dispw ;print W call us100 ;processing time tblrd*+ bra get_txt ;================================================ ; Clear all RAM ;================================================ clr_ram lfsr fsr0,0x000 lfsr fsr1,0x100 lfsr fsr2,0x200 clear1 clrf postinc0 clrf postinc1 clrf postinc2 btfss fsr0h,0 bra clear1 lfsr fsr0,0x300 lfsr fsr1,0x400 lfsr fsr2,0x500 clear2 clrf postinc0 clrf postinc1 clrf postinc2 btfss fsr0h,2 bra clear2 return ;================================================ ; Delays @ 40MHz ;================================================ one_sec movlw -.10 ;1 second delay (approx) movwf temp1 seclp call ms100 incfsz temp1 bra seclp return ms100 movlw -.100 ;100ms delay (approx) movwf temp0 ms100lp call ms01 incfsz temp0 bra ms100lp return ms01 movlw -.10 ;1ms delay movwf dl_cnt1 ms01lp call us100 incfsz dl_cnt1 bra ms01lp return us100 movlw -.90 ;100us delay movwf dl_cnt2 us100lp movff temp0,temp0 movff temp0,temp0 movff temp0,temp0 movff temp0,temp0 incfsz dl_cnt2 bra us100lp nop return us10 movlw .15 movwf temp0 decfsz temp0 bra $-2 return ;================================================ ; LCD redefiniton data ;================================================ ;CHR$ 0 - 7 reserved for redefined characters ;================================================ ; LCD text strings ;================================================ org strings title1 db "* t1---------- *",0xff title2 db "* t2---------- *",0xff working1 db "* 000% 000 Hz *",0xff working2 db "* 000V Cont *",0xff end