Software PWM on a Raspberry Pi

If you want to control the brightness of a LED, the speed of a DC motor or the direction of a servo, you will need PWM.

The video shows PWM being used to control the brightness of some LEDs.

Pulse-width modulation (PWM) is used to control the amount of power supplied to electrical devices, especially to DC motors, servos and LEDs.

PWM is able to achieve this by quickly turning off and on the power to the device. The measurement for this is duty cycle.

Duty cycles describes the proportion of ‘on’; a low duty cycle corresponds to low power, because the power is off for most of the time.  A high duty cycle corresponds to high power, because the power is on most of the time.

Duty cycle is expressed in percent, 50% is when the power is on half the time and 100% being fully on.

pwm

PWM can be performed in a number of ways on the Raspberry Pi.

Inbuilt hardware;
The Pi can perform PWM in hardware, but this can only be done on one pin (GPIO18) and when enabled it interferes with the audio jack. It is also hard to get working. Last time I looked, you had to recompile the kernel.

Software;
PWM can be performed in software. This is a very easy option.  It isn’t as precise  as hardware PWM however in most instances software PWM will do.

External hardware;
There are external components that can be used to perform PWM.  Eg  Adafruit 16-Channel 12-bit PWM/Servo Driver – I2C interface – PCA96855

Software PWM with pi-blaster

In this post I will demonstrate how to use  a modified version of Pi-Blaster, a software based PWM to control the brightness of some LEDs. I modified it to allow the pins for PWM to be specified at startup.

1. Download and compile pi-blaster.

pi@raspberrypi ~ $ git clone https://github.com/mwilliams03/pi-blaster.git
pi@raspberrypi ~ $ cd pi-blaster
pi@raspberrypi ~ $ make pi-blaster

2. Start Pi-Blaster.

We are going to use pi-blaster in the user space. Once started, it runs as a background process.
If you start pi-blaster without any parameters, it will enable PWM on the default pins.

pi@raspberrypi ~ $ sudo ./pi-blaster

The default pins are;

Channel number    GPIO number   Pin in P1 header
0               4             P1-7
1              17             P1-11
2              18             P1-12
3              21             P1-13
4              22             P1-15
5              23             P1-16
6              24             P1-18
7              25             P1-22

You can also specify the pins at start that you only want to use for PWM. To enable PWM only on pins 22, 24, 17 and 16;

pi@raspberrypi ~ $ sudo ./pi-blaster 22 24 17 16

3. Configure PWM on a pin.

Pi-blaster creates a special file (FIFO) in /dev/pi-blaster. Any application on your Raspberry Pi can write to it (this means that only pi-blaster needs to be root, your application can run as a normal user).

  • To set pin1 to a PWM of 20%
    echo "1=0.2" > /dev/pi-blaster
  • To completely turn off pin0:
    echo "0=0" > /dev/pi-blaster
  • To completely turn on pin1:
    echo "1=1" > /dev/pi-blaster

4. Stop pi-blaster

Use killall to kill the pi-blaster process

pi@raspberrypi ~ $ sudo killall pi-blaster

5. Sample program used in the above video

PWM-LED

You can write your own programs to control PWM just by writing to /dev/pi-blaster.
The code below was used to control the LEDs in the above video. And which is also shown to the right

#include
#include
#include
#define DELAY 70000
#define LEVELS 7
#define INCREMENT  0.15
FILE *fp;
void  INThandler(int sig)
{
int pin;
signal(sig, SIG_IGN);
//turn all pins off
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,0.0);
fflush(fp);
}
fclose(fp);
exit(0);
}
int main(void)
{
signal(SIGINT, INThandler);
// arrays used to control brightness of a row of LEDs. left then right animation.
float left[] = {0,0,0,0,0,0,0,0,1,0.75,0.375,0.18,0.09,0.04,0.005,0,0,0,0,0,0,0,0};
float right[] = {0,0,0,0,0,0,0,0,0.005,0.04,0.09,0.18,0.375,0.75,1,0,0,0,0,0,0,0,0};
int pin = 0;
float pwm = 0.0;
// open the pi-blaster device file. If it fails, confirm that pi-blaster has been started
fp = fopen("/dev/pi-blaster", "w");
if (fp == NULL) {
printf("Error opening file\n");
exit(0);
}
// create the PWM values which are used for the animation going in one direction
float pwdPower[LEVELS*4];
float i;
int j;
int count = 7;
for (j = 0; j < LEVELS ;  j++){
pwdPower[j] = 0.0;
}
for (i = 0.0; i < 1 ; i +=INCREMENT){ 		if (i>1.0)i=1.0;
pwdPower[count] = i;
count++;
}
for (i = 1; i >0.00001 ; i -=INCREMENT){
if (i<0.0)i=0.0;
pwdPower[count] = i;
count++;
}
for (j = 0; j < (LEVELS) ;  j++){
pwdPower[count] = 0.0;
count++;
}
///////////////////////////////////////////////////////////////////
//This section illustrates different brightness levels
// using PWM with a 2 second puase between levels.
// Enable all pins using the value 0.002 for PWM
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,0.002);
fflush(fp);
}
sleep(2);
// Enable all pins using the value 0.03 for PWM
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,0.03);
fflush(fp);
}
sleep(2);
// Enable all pins using the value 0.05 for PWM
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,0.5);
fflush(fp);
}
sleep(2);
///////////////////////////////////////////////////////////////////
// This section will cycle through the power levels 5 times.
for (j = 0; j < 5 ; j++){
for (i = 0.0; i < 1 ; i +=INCREMENT){
for (pin = 0; pin < 8; pin++){                 		fprintf(fp, "%i=%f\n", pin,i ); 	                        fflush(fp); 			} 		usleep(DELAY); 		}        		for (i = 1; i >0.00001 ; i -=INCREMENT){
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,i );
fflush(fp);
}
usleep(DELAY);
}
}
///////////////////////////////////////////////////////////////////
// The below section performs the animations
int c = 0;
int g;
for(g = 0; g < 2; g++){ 		for(c = 16; c>= 0; c--){
for (pin = 0; pin < 8  ; pin++){
fprintf(fp, "%i=%f\n", pin, right[pin+c]);
fflush(fp);
}
usleep(DELAY);
}
for(c = 0; c < 16; c++){
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin, left[pin+c]);
fflush(fp);
}
usleep(DELAY);
}
}
for(g = 0; g < 4; g++){
for(c = 0; c < (LEVELS*3); c++){
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin, pwdPower[pin+c]);
fflush(fp);
}
usleep(DELAY);
}
}
//turn all pins off
for (pin = 0; pin < 8; pin++){
fprintf(fp, "%i=%f\n", pin,0.0);
fflush(fp);
}
fclose(fp);
return 0;
}

14 thoughts on “Software PWM on a Raspberry Pi”

    1. I think I have found out what my issue was. After guessing it was C, I found out that the “#include” were empty. I filled this in then performed the following.

      gcc -o program markspwmdemo.c
      chmod +x program
      ./program

      :)

    1. Use ./pi-blaster to view the current frequency;
      sudo ./pi-blaster
      Using hardware: PWM
      Number of channels: 8
      PWM frequency: 100 Hz
      PWM steps: 1000
      Maximum period (100 %): 10000us
      Minimum period (0.100%): 10us

      You can adjust these by changing a few defines at the top of the source code:

      NUM_SAMPLES: The number of steps
      SAMPLE_US: The time of one step (minimum period)

      If you do not neet a resolution of 1000 steps (approximately equivalent to a 10 bit DAC), then you can reduce the number of samples or increase the duration of the steps.

      1. After retrying and deleting everything again the “nothing to make” error dissepeared. So it’s working!
        I did all the time the same, so I still don’t know what went wrong…

  1. Hi I’ve been playing with motor control in Python with my pi. I was formerly using GPIO direct pin addressing, but wanted to try pi-blaster to reduce CPU usage.

    I have pi-blaster installed, and I can see that it’s running, as it reports its details, as per mwilliams03′s post above.

    Also, when I execute my Python code, I can “cat /dev/pi-blaster” and see the appropriate values written to the file that I expect from my code. The values don’t seem to generate output on the designated pins though.

    That is, the output voltages for my motor control pins, according to my multimeter, appear to be zero, where I would expect them to be corresponding voltage values.

    So, pi-blaster seems to be writing to it’s file correctly, but no corresponding outputs. Anyone have any thoughts?

    I’d post the code here, but I’m not sure if WordPress supports “code” tags. ;)

  2. Do oyu have some test result, on how exact the timings are? Are there any timing varities if you’ve got more clients on it? e.g 20 servos?
    greetings

Leave a Reply