SP.710, Spring 2001: "Intro to Microcontrollers"
Oscillators, Buzzers, and Dimmers: The PIC PWM generator
What is PWM? It's "Pulse Width Modulation". It will be
explained further today in class.
With that explanation out of the way, here's a quick guide to doing
PWM on the PIC. You could do it by hand with delay_us commands, or by
using the Timers, but there is a much easier way:
The PIC has a "hardware PWM generator" that can automatically
generate a PWM signal at a duty cycle you choose (up to 10 bits
of resolution, which is roughly 0.1% increments), and at
one of three frequencies (roughly 1.25 kHz, 5 kHz, and 20 kHz,
with a 20MHz crystal). There are ways to vary these frequencies more
precisely, but we will get to that later.
The C commands to use this PWM generator are:
(commands you would usually only run once, at the beginning of main():)
SETUP_CCP1(CCP_PWM); // setup pin CCP1 (RC2) to do PWM
SETUP_TIMER_2(T2_DIV_BY_16,255,1) // set the PWM frequency to
// 20 MHz / 4 clocks-per-instr / 16 (DIV_BY_16) / 256 (ticks/rollover)
// = 1.25 kHz (change to T2_DIV_BY_4 for 5 kHz, T2_DIV_BY_1 for 20 kHz)
(and then, to actually use the PWM generator:)
set_PWM1_duty(#); // # = 0 (full off) --> 255 (100% on)
// (128 = 50 % duty cycle, etc.)
If you do this, the PIC will automatically turn pin RC2 on and off at
the specified duty cycle and frequency in the background, without you
having to keep track of any of the timing yourself!
- Write code, using delay_us(), to flash the LED at a
frequency of 50kHz (20us period), with a 50% duty cycle.
Verify on an oscilloscope that the LED is flashing at the correct
- Write code using the PWM functions mentioned above to do the same
thing. Verify the LED's behavior using an oscilloscope.
- Write code to make the LED fade in and out, from off to full
- (extra) As in part (3), but make the LED's brightness linear with
time (ask us to explain this in more detail).
- Use the PWM pin to drive a buzzer. You'll probably want to run the
buzzer at 50% duty cycle, so you're just using the PWM generator
as an easy way to generate a particular frequency, to make
different tones. Hook up the buzzer using this sketchy schematic:
PIC ----| |-----[ BUZZER ]--+
RC2 | | |
100 uF |
- (extra) Tired of generating only three notes? (And high-pitched
squeals, at that). Here are some things you can change:
- Replace the 20 MHz crystal with a slowed one, perhaps 4
MHz. Remember to change the #USE (DELAY=) line in your code to reflect
this. With this lower frequency oscillator, the base PWM frequencies
will also all be a factor of 5 lower, so you can generate a 250 Hz
signal with the PWM generator, a reasonably low pitch (though you
still only have three tones you can easily generate). You may also
need to change the #fuses option HS (high-speed crystal) to something
else to get this to work... look in the manual and ask us if you have questions.
- You could implement your own oscillations using delay_us or some
similar method to get a full range of frequencies, but this is
annoying to program and integrate with other code.
- You can actually
vary the frequency of the PWM generator in much finer increments by
changing the "255" in the SETUP_TIMER_2 command mentioned
above. The drawback of this is that you lose duty cycle resolution.
If you change the 255 to 128, the frequency will double, but you can
only pass SET_PWM1_duty numbers between 0 and 128, where 128 will mean
100% on... so now you only have 7 bits of duty cycle resolution
instead of 8. But if you're just using this to drive a buzzer at 50%
duty cycle, this is no problem, and you now have access to a much
wider range of frequencies (all 1.25 kHz and above, though, unless you
combine this with the low-frequency-crystal modification mentioned