Article Index

PWM Functions

The the library provides PWM in an easy to use form. They do allow you to set the parameters of both PWM channels even though you can't use both on Pi 1s. 

The fundamental frequency of the PWM is set by:

void bcm2835_pwm_set_clock (uint32_t divisor)

You can't set the clock speed directly. Instead you have to specify a divisor to reduce the 19.2MHz clock to a lower rate. The library provides some standard settings:

BCM2835_PWM_CLOCK_DIVIDER_2048  9.375kHz
BCM2835_PWM_CLOCK_DIVIDER_1024  18.75kHz
BCM2835_PWM_CLOCK_DIVIDER_512   37.5kHz
BCM2835_PWM_CLOCK_DIVIDER_256   75kHz
BCM2835_PWM_CLOCK_DIVIDER_128   150kHz 
BCM2835_PWM_CLOCK_DIVIDER_64    300kHz 
BCM2835_PWM_CLOCK_DIVIDER_32    600.0kHz 
BCM2835_PWM_CLOCK_DIVIDER_16    1.2MHz
BCM2835_PWM_CLOCK_DIVIDER_8     2.4MHz
BCM2835_PWM_CLOCK_DIVIDER_4     4.8MHz
BCM2835_PWM_CLOCK_DIVIDER_2     9.6MHz
BCM2835_PWM_CLOCK_DIVIDER_1     4.6875kHz 

The largest divider you can specify is 0xFFF or 4096 which gives the same frequency as specifying 1 i.e. 4.6875kHz

An important point to remember is that the clock rate is not the PWM repeat rate.

How the PWM pulses are created depends on the selected mode:

void bcm2835_pwm_set_mode (uint8_t channel, uint8_t markspace, uint8_t enabled)

channel has to be 0 or 1, markspace is 1 for markspace mode and 0 for balanced and enabled is 1 to start the PWM pulses running. 

Finally you can set the range using:

void bcm2835_pwm_set_range (uint8_t channel, uint32_t range)

And you can set data using 

void bcm2835_pwm_set_data (uint8_t channel, uint32_t data)

The largest value of range that seems to work in practice is 0xFF FFFF which is over 268 million clock pulses which at the highest clock rate is around 30 seconds per repeat.

Selecting A Clock and Range

One of the basic problems in using the Pi's PWM is selecting a clock divider and range value. There is a fairly standard way of doing this. 

The best way to work out a configuration that gives a particular repeat rate is to start at the highest clock rate, i.e. 9.6MHz with a divider of 2, and work out the range needed.

If you need a particular repeat rate given in seconds then: 

max range=repeat time*clock frequency

                = repeat time* 9.6 *1000

if the repeat time is in milliseconds. 

Similarly if the repeat rate is specified as a frequency then the max range is

max range =clock frequency/ repeat frequency

                  = 9.6/repeat frequency

This gives you the maximum range you can use.

If you actually want a smaller range you can reduce the clock to a lower frequency and the divider you need is:

divider =2* max range/desired range

The two is because the smallest divider you can use is 2. 

If after rounding the divider and range don't give you an accurate enough signal you have no choice but to try multiples of the range - i.e. try range*2, range*3 with the corresponding dividers i.e. divider/2, divider/3 and so on.  Of course this means you have to work with data*2, data*3 and so on but this isn't difficult. 

Using PWM

So now you know how to make use of the PWM lines. All you have to do is set the frequency, range and data.  

You also have to set the mode of the GPIO pin that you are using the correct Alt mode. 

As we only have two PWM pins you set GPIO 18 to ALT5 and GPIO 13 to ALT0:

 bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 );    
 bcm2835_gpio_fsel(13,BCM2835_GPIO_FSEL_ALT0 );

The simplest PWM program you can write is:

#include <bcm2835.h>
int main(int argc, char** argv) {
  if (!bcm2835_init())
        return 1;
 bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 );    
 bcm2835_pwm_set_clock(2);
 bcm2835_pwm_set_mode(0, 1, 1);
 bcm2835_pwm_set_range(0,2);
 bcm2835_pwm_set_data(0,1);
 return (EXIT_SUCCESS);
}

The clock is set to its fastest 9.6MHz and PWM0 is set to mark/space mode with range 2 and data 1. 

This produces the fastest pulse train with one clock pulse high and one low:

 

You have to set the value first and then set the period - otherwise the PWM line isn't setup properly. You also have to set the period for each pin you are using even if it does set the same period.

Notice that there is no need to put the program into an infinite loop. Once the PWM line has been set up and enables it just gets on with generating the pulse train no matter what the processor does. In this case the pulse generation continues long after the program has ended.

 

Of course this may be fast but as a PWM signal it is completely useless because you cannot change its duty cycle to anything useful. In practice you need to set range to something greater than one. 

Just to demonstrate that two PWM lines can be used independently of one another here is a program that sets each one of the PWM GPIO lines - to a different duty cycle and repeat rate:

#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.h>
int main(int argc, char** argv) {
  if (!bcm2835_init())
        return 1;
 bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 );    
 bcm2835_gpio_fsel(13,BCM2835_GPIO_FSEL_ALT0 );    
 
 bcm2835_pwm_set_clock(2);
 
 bcm2835_pwm_set_mode(0, 1, 1);
 bcm2835_pwm_set_range(0,2);
 bcm2835_pwm_set_data(0,1);
   
 bcm2835_pwm_set_mode(1, 1, 1);
 bcm2835_pwm_set_range(1,8);
 bcm2835_pwm_set_data(1,2);
 return (EXIT_SUCCESS);
}

 Y​you can see the result in the following logic analyzer display: