For silicium ideas

Hardware PWM with Raspberry Pi Zero

Hardware PWM with Raspberry Pi Zero

Purpose

The main idea was to generate a configurable Pulse Width Modulated (PWM) signal out of a Raspberry PI Zero v1.3.

The software solution

The first solution was to handle a regular GPIO pin by software in order to alternate one and zeros via a loop.
To do this, I implemented a bash script able to set and unset the GPIO pin value at the right frequency and duty cycle.
Although working, that solution was not optimal in term of CPU time.
In fact, it was wasting almost 10% of the little Pi Zero’s CPU core.

Thus, I started to look for hardware solution and I found exactly what I wanted.

Hardware recall

Detailed pinout

As you can see, we can count up to 4 hardware PWM output:

  • PWM 0
    • GPIO 12
    • GPIO 18
  • PWM 1
    • GPIO 13
    • GPIO 19

The hardware solution

The BCM2835 is able to generate a true PWM on some particular GPIO pins thanks to its DMA configuration.

Several software interfaces are available to manage directly these hardware components as :

Native C code. (direct register access)

BCM2835 library

– PIGPIO library

Many others might be existing but you need to make sure if it’s rather an hardware solution or a software solution (as Wiringpi, SysFs …).
In the embedded electronics world where CPU time is a scarce and precious ressource you always should ask yourself if there is not a more low level optimised function.
It is rarely a good news when you can see an infinite loop such as “while(true)” in your code.

 

The pigpio library

Finally I choose to use the pigio library because of its simplicity and nice documentation.

Example of code :

/*
 * pwm.c
 *
 *  Created on: May 23 2018
 *  Author: Codedubix.eu
 */


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#include <pigpio.h>


#define NUM_GPIO 32
#define GPIO_INUSE 21
#define MIN_WIDTH 1000
#define MAX_WIDTH 2000


int main(int argc, char *argv[])
{
   unsigned int user_gpio, freq, real_freq = 0, duty_cycle = 0;


   if (gpioCfgClock(5, 1, 0)) return -1;

   if (gpioInitialise() < 0) return -1;


   if (argc != 4)
   {
           perror("Bad arguments : (1) GpioNumber, (2) Freq, (3) DutyCycle \n");
           return -1;
   }
   else
   {
           user_gpio = atoi(argv[1]);

         if ((user_gpio>=0) && (user_gpio<NUM_GPIO)) used[user_gpio] = 1;
         else return -1;

         freq = atoi(argv[2]);
         duty_cycle = atoi(argv[3]);

      }


   int ret = gpioHardwarePWM(user_gpio, freq, duty_cycle);
   if (  ret != 0)
   {
       perror("PWM failed \n");
       return -1;
   }


   if( freq > 0 )
   {
           printf("Sending PWM to GPIO %d, Freq = %d, Duty Cycle = %d \n", user_gpio, freq, duty_cycle);
   }
   else
   {
           perror ("Bad pwm frequency \n");
   }


return 0;

}

Tests

Configuration A :

  • GPIO 19 / PWM 1
  • Duty Cycle : 50%
  • Frequency : 1 Hz

Configuration B :

  • GPIO 19 / PWM 1
  • Duty Cycle : 50%
  • Frequency : 10 kHz

Configuration C :

  • GPIO 19 / PWM 1
  • Duty Cycle : 50%
  • Frequency : 1 MHz

Result

  • A : CPU Time = 0%
  • B : CPU Time = 0%
  • C : CPU Time = 0%

Conclusion

It is quite easy to generate up to 2 different PWM signals out of 4 GPIO pins without wasting any time on the BCM2835 CPU core.

 

 

 



Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.