Explanation of a bit-banging PWM example

I received a question about a snippet of code in one of the bit-banging PWM examples. I thought I’d post the explanation here in case anyone else is wondering about it.

The complete example program is:

int main()
{
    int n;
    TRISD = 0;
  
    while(1)
    {
        for (n=0 ; n<100 ; ++n)  pulse(1 + (n/100.0), 19 - (n/100.0));
    }
  
    return 0;
}
 
void pulse(double mark, double space)
{
    _LATD0 = 1;
    __delay32(mark * 30000.0);
    _LATD0 = 0;
    __delay32(space * 30000.0);
}

These are the lines I’m going to explain:

while(1)
{
    for (n=0 ; n<100 ; ++n)  pulse(1 + (n/100.0), 19 - (n/100.0));
}

There are a couple of things to explain here:

  1. Firstly, there is the pulse() function. Each time you call this function, it generates a single pulse on RD0. Since it only generates one pulse, it needs to be called over and over again. Each time it’s called it actually generates not only the pulse (what we call the “mark” – the time when the pin is high) but also the pause after it (what we call the “space” – the time when the pin is low). The two arguments (input values) to the pulse function are the mark and space in milliseconds. The way the two arguments are specified here, they will always add up to 20ms. However, depending on the value of n (between 0 and 100) the mark (i.e. pulse width) can be anywhere between 1ms and 2ms. When n=0, the mark is 1ms and the space is 19ms. When n=100, the mark is 2ms and the space is 18ms. As the pulse gets longer, the space shrinks to keep the total time equal to 20ms.
  2. The for loop is there to call the pulse function 100 times in a row, but with a slightly different pulse width (due to different n value) each time. Each pulse in the 100-long sequence is 0.01ms longer than the previous one. The first pulse is 1ms long and the 100th pulse is 1.99ms long. The whole sequence of 100 pulses should take 100 x 20ms = 2 seconds. Basically, the servo will move steadily from one end of it’s range to the other end over a period of 2 seconds.
  3. The while(1) loop is just there to keep the waveform repeating. At the end of the 2 second long sequence of 100 pulses, the while loop repeats, starting over at the shortest pulse again. So, the servo moves slowly from one end of its range to the other, then jumps back to where it started and begins again.

By the way, I only used a for loop here because it can be written very concisely. The following two code snippets are completely equivalent:

for loop version:

for (n=0 ; n<100 ; ++n)  pulse(1 + (n/100.0), 19 - (n/100.0));

while loop version:

n=0;
while (n<100)
{
    pulse(1 + (n/100.0), 19 - (n/100.0));
    ++n;
}

Finally, I’ll just point out that for really simple bit-banging PWM, you don’t necessarily need to use a separate function to generate the pulses. For example, to generate PWM with constant 1.5ms pulse width and 20ms period, this would do the trick:

int main()
{
    TRISD = 0;
  
    while(1)
    {
        _LATD0 = 1;        // set RD0 high
        __delay32(45000);  // 1.5ms
        _LATD0 = 0;        // set RD0 low
        __delay32(555000); // 18.5 ms
    }
  
    return 0;
}
Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s