A while back, John sent me some code that he and Tom had been using to test the pulse-width modulation (PWM) capabilities of the hardware underlying the LogoChip. I read through the PIC18F2320 datasheet over the weekend and learned how it does PWM; and between John and Tom’s sample code and the information in the datasheet, tonight I got a motor running at variable speed under software control.
To use the PIC’s PWM, you configure the Timer 2 counter for the modulation frequency (period, actually), then set the output port into PWM mode and configure the duty cycle. Sounds simple; it’s really just a matter of finding all the control registers and the proper bits. Because the PWM is implemented in the chip’s hardware, it keeps running at the last-set duty cycle (== motor speed) even if you’re doing something else or your program stops running, so you can set and forget.
I’d like to measure the PWM output frequency and confirm what the Timer 2 counter and prescalar are doing, but my frequency counter is giving me weird results and I have no idea how far out of calibration my scope is, so that’ll have to wait for another day. When I hook a speaker to the output, it sounds well below 440Hz, but the frequency counter is trying to tell me it’s 2+kHz. Feh.
Huh, when I calculate the frequency (if I’m doing it right), I come up with ~2.4kHz. My speaker is a piezo and it sounded funny, so maybe that’s outside its range and the frequency counter was right on?
After I got over the excitement of just getting it to work at all, I wanted it to do more than let me manually set a speed for my motor, so there are a couple of extra procedures at the bottom (
pwm-slowdown) to ramp the speed from slow to fast and back down to slow. They add to the code length, but they’re not critical for implementing PWM.
Here’s the program:
- motor-pwm-demo.txt – Program to demo PWM speed control of DC motor
Here’s the code:
; Keith Neufeld ; 08-Mar-2006 ; ; LogoChip program to demo motor speed control with PIC PWM output. ; Uses PWM channel 1 on port C2. Duty cycle values determined with ; NPN transistor driver with 9V motor supply. constants [ [CCP1CON $fbd] ; capture/compare/PWM 1 control reg [T2CON $fca] ; timer 2 control register [TMR2ON 2] ; timer 2 enable bit [T2CKPS1 1] ; timer 2 prescale bit 1 [CCPR1L $fbe] ; C/C/P 1 register low byte [PR2 $fcb] ; timer 2 period register [pwm-port-ddr portc-ddr] ; PWM 1 outputs on port C2 [pwm-bit 2] [period 255] ; default timer period to use ;[low-duty 110] ; lowest duty cycle for my motor to [low-duty 10] ; start from a stop [high-duty 255] ; highest possible cycle ] ; Initialize PWM by configuring timer 2 for PWM period and enabling PWM mode. to pwm-init clearbit pwm-bit pwm-port-ddr ; set PWM port to output ;write T2CON 6 ; set timer 2 on, prescalar 16 setbit T2CKPS1 T2CON ; set timer 2 prescalar to 16 write PR2 period ; set timer period highest possible setbit TMR2ON T2CON ; turn on timer 2 write CCP1CON 12 ; set PWM mode ;write CCPR1L 128 ; set 50% duty cycle end ; Set PWM period and duty cycle. to pwm-set :period :duty write PR2 :period ; set timer period write CCPR1L :duty ; set PWM duty cycle end ; Disable PWM by shutting off timer 2. to pwm-off pwm-set 0 0 ; disable PWM by setting duty cyc to 0 clearbit TMR2ON T2CON ; and turn off timer 2 end ; Enable PWM and ramp up to highest speed at :increment / 255ths ; per 1/10 second. global [duty] to pwm-speedup :increment setduty low-duty pwm-set period duty repeat ((high-duty - low-duty) / :increment) [ wait 1 setduty duty + :increment pwm-set period duty ] setduty high-duty end ; Enable PWM and ramp down from highest speed at :increment / 255ths ; per 1/10 second. to pwm-slowdown :increment setduty high-duty pwm-set period duty repeat ((high-duty - low-duty) / :increment) [ wait 1 setduty duty - :increment pwm-set period duty ] setduty low-duty end