![]() NEW! |
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
| file: /Techref/microchip/HV_PWM_Proto.htm, 28KB, , updated: 2009/10/1 10:11, local time: 2025/10/24 08:48,
216.73.216.53,10-2-207-162:LOG IN
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://www.massmind.org/techref/microchip/HV_PWM_Proto.htm"> PWM Generator, Motor Control, 2x16 LCD Display, Potentiometer</A> |
| Did you find what you needed? |
Welcome to massmind.org! |
|
Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232! |
.