PWM Intro - Pulse Width Modulation

Image icon pwm_freq.png1.24 KB
Image icon pwm_duty.png1.5 KB
Author: S├ębastien Lelong | Jallib Group

In the following tutorials, we're going to (try to) have some fun with PWM. PWM stands for Pulse Width Modulation, and is quite weird when you first face this (this was at least my first feeling). So here's a brief explanation of what it is about.

How does PWM look like ?...

PWM is about switching one pin (or more) high and low, at different frequencies and duty cycles. This is a on/off process. You can either vary:
  • the frequency,
  • or the duty cycle, that is the proportion where the pin will be high
Figure 1. PWM: same duty cycle, different frequencies.

Both have a 50% duty cycle (50% on, 50% off), but the upper one's frequency is twice the bottom

Figure 2. PWM: same frequency, different duty cycles

Three different duty cycle (10%, 50% and 90%), all at the same frequency

But what is PWM for ? What can we do with it ? Many things, like:
  • producing variable voltage (to control DC motor speed, for instance)
  • playing sounds: duty cycle is constant, frequency is variable
  • playing PCM wave file (PCM is Pulse Code Modulation)
  • ...

That said, we're now goind to experiment these two major properties.

PWM (Part 1) - Dimming a led with PWM

One PWM channel + one LED = fun

For now, and for this first part, we're going to see how to control the brightness of a LED. If simply connected to a pin, it will light at its max brightness, because the pin is "just" high (5V).

Now, if we connect this LED on a PWM pin, maybe we'll be able to control the brightness: as previously said, PWM can be used to produce variable voltages. If we provide half the value (2.5V), maybe the LED will be half its brightness (though I guess the relation between voltage and brightness is not linear...). Half the value of 5V. How to do this ? Simply configure the duty cycle to be 50% high, 50% low.

But we also said PWM is just about switching a pin on/off. That is, either the pin will be 0V, or 5V. So how will we be able to produce 2.5V ? Technically speaking, we won't be able to produce a real 2.5V, but if PWM frequency is high enough, then, on the average, and from the LED's context, it's as though the pin outputs 2.5V.

Building the circuit

Enough theory, let's get our hands dirty. Connecting a LED to a PWM pin on a 16f88 is quite easy. This PIC has quite a nice feature about PWM, it's possible to select which pin, between RB0 and RB3, will carry the PWM signals. Since I use tinybootloader to upload my programs, and since tiny's fuses are configured to select the RB0 pin, I'll keep using this one (if you wonder why tinybootloader interferes here, read this post).

Figure 1. Connecting a LED to a PWM pin

On a breadboard, this looks like this:

The connector brings +5V on the two bottom lines (+5V on line A, ground on line B).

LED is connected to RB0

Writing the software

For this example, I took one of the 16f88's sample included in jallib distribution (16f88_pwm_led.jal), and just adapt it so it runs at 8MHz, using internal clock. It also select RB0 as the PWM pin.

So, step by step... First, as we said, we must select which pin will carry the PWM signals...

pragma target CCP1MUX      RB0     -- ccp1 pin on B0

and configure it as output

var volatile bit pin_ccp1_direction is pin_b0_direction
pin_ccp1_direction = output
-- (simply "pin_b0_direction = output" would do the trick too)

Then we include the PWM library.

include pwm_hardware

Few words here... This library is able to handle up to 10 PWM channels (PIC using CCP1, CCP2, CCP3, CCP4, ... CCP10 registers). Using conditional compilation, it automatically selects the appropriate underlying PWM libraries, for the selected target PIC.

Since 16f88 has only one PWM channel, it just includes "pwm_ccp1" library. If we'd used a 16f877, which has two PWM channels, it would include "pwm_ccp1" and "pwm_ccp2" libraries. What is important is it's transparent to users (you).

OK, let's continue. We now need to configure the resolution. What's the resolution ? Given a frequency, the number of values you can have for the duty cycle can vary (you could have, say, 100 different values at one frequency, and 255 at another frequency). Have a look at the datasheet for more.

What we want here is to have the max number of values we can for the duty cycle, so we can select the exact brightness we want. We also want to have the max frequency we can have (ie. no pre-scaler).


If you read the jalapi documentation for this, you'll see that the frequency will be 7.81kHz (we run at 8MHz).

PWM channels can be turned on/off independently, now we want to activate our channel:


Before we dive into the forever loop, I forgot to mention PWM can be used in low or high resolution. On low resolution, duty cycles values range from 0 to 255 (8 bits). On high resolution, values range from 0 to 1024 (10 bits). In this example, we'll use low resolution PWM. For high resolution, you can have a look at the other sample, 16f88_pwm_led_highres.jal. As you'll see, there are very few differences.

Now let's dive into the loop...

forever loop
   var byte i
   i = 0
   -- loop up and down, to produce different duty cycle
   while i < 250 loop
      i = i + 1
   end loop
   while i > 0 loop
      i = i - 1
   end loop
   -- turning off, the LED lights at max.

end loop

Quite easy right ? There are two main waves: one will light up the LED progressively (0 to 250), another will turn it off progressively (250 to 0). On each value, we set the duty cycle with pwm1_set_dutycycle(i) and wait a little so we, humans, can see the result.

About the result, how does this look like ? See this video:

"I wanna try, where are the files ?"

To run this sample, you'll need latest jallib pack (at least 0.2 version). You'll also find the exact code we used here.

PWM (Part 2) - Sound and Frequency with Piezo Buzzer

Image icon pwm_sound.jpg23.19 KB
Image icon pwm_sound_piezo.jpg24.73 KB

In previous tutorial, we had fun by controlling the brightness of a LED, using PWM. This time, we're going to have even more fun with a piezo buzzer, or a small speaker.

If you remember, with PWM, you can either vary the duty cycle or the frequency. Controlling the brightness of a LED, ie. produce a variable voltage on the average, can be done by having a constant frequency (high enough) and vary the duty cycle. This time, this will be the opposite: we'll have a constant duty cycle, and vary the frequency.

What is a piezo buzzer ?

It's a "component" with a material having piezoelectric ability. Piezoelectricity is the ability for a material to produce voltage when it get distorted. The reverse is also true: when you produce a voltage, the material gets distorted. When you stop producing a voltage, it gets back to its original shape. If you're fast enough with this on/off voltage setting, then the piezo will start to oscillate, and will produce sound. How sweet...

Constant duty cycle ? Why ?

So we now know why we need to vary the frequency. This will make the piezo oscillates more and less, and produces sounds at different levels. If you produce a 440Hz frequency, you'll get a nice A3.

But why having a constant duty cycle ? What is the role of the duty cycle in this case ? Remember: when making a piezo oscillate, it's not the amount of volts which is important, it's how you turn the voltage on/off1:
  • when setting the duty cycle to 10%: during a period, piezo will get distorted 10% on the time, and remain inactive 90% on the time. The oscillation proportion is low.
  • when setting the duty cycle to 50%: the piezo is half distorted, half inactive. The oscillation proportion is high, because the piezo oscillates at the its maximal amplitude, it's half and equally distorted and inactive.
  • when setting the duty cycle to 90%: the piezo will get distorted during 90% of a period, then nothing. The oscillation proportion is low again, because the proportion between distortion and inactivity is not equal.

So, to summary, what is the purpose of the duty cycle in our case ? The volume ! You can vary the volume of the sound by modifying the duty cycle. 0% will produce no sounds, 50% will be the max volume. Between 50% and 100% is the same as between 0% and 50%. So, when I say when need a constant duty cycle, it's not that true, it's just that we'll set it at 50%, so the chances we hear something are high :)

Let's produce sounds !

The schematics will use is exactly the same as on the previous post with the LED, except the LED is replaced with a piezo buzzer, like this:

By the way, how to observe the "duty cycle effect" on the volume ? Just program your PIC with the previous experiment one, which control the brightness of a LED, and power on the circuit. I wanted to show a video with sounds, but the frequency is too high, my camera can't record it...

Anyway, that's a little bit boring, we do want sounds...

Writing the software

The software part has a lot of similarities with the previous experiment. The initialization is the same, I let you have a look. Only the forever loop has changed:
var dword counter = 0
forever loop

  for 100_000 using counter loop
     -- Setting @50% gives max volume
     -- must be re-computed each time the frequency
     -- changes, because it depends on PR2 value
  end loop

end loop
Quite straightforward:
  • we "explore" frequencies between 0 and 100 000 Hz, using a counter
  • we use pwm_set_frequency(counter) to set the frequency, in Hertz. It takes a dword as parameter (ie. you can explore a lot of frequencies...)
  • finally, as we want a 50% duty cycle, and since its value is different for each frequency setting, we need to re-compute it on each loop.
Note: jallib's PWM libraries are coming from a "heavy refactoring" of Guru Stef Mientki's PWM library. While integrating it to jallib, we've modified the library so frequencies can be set and changed during program execution. This wasn't the case before, because the frequency was set as a constant.

So, how does this look like ? Hope you'll like the sweet melody :)

"Where can I download the files ?"

As usual, you'll need the latest jallib pack (at least 0.2 version). You'll also find the exact code we used here.

1 I guess this is about energy or something like that. One guru could explain the maths here...