Generating a PWM Signal
Generating a PWM Signal on the Servo Pi or Servo PWM Pi Zero
Created: 17/07/2017 | Last Updated: 22/05/2021
The Servo PWM Pi Zero and Servo Pi are 16-channel, 12-bit PWM controllers for the Raspberry Pi, suitable for driving LEDs and radio control servos. The boards are based around the PCA9685 PWM I2C LED controller from NXT and can drive each of the 16 outputs with a 12-bit (4096 steps) duty cycle. For this tutorial, we will be using the Servo PWM Pi Zero and our Servo Pi Python library.
What is PWM?
PWM or Pulse Width Modulation is a method of generating a digital signal where the on and off periods can be set in the controller. Pulse Width Modulation has many uses but the most common are for controlling motors and generating the equivalent of an analogue voltage using digital means.
The duration of the on time in a PWM signal is called the pulse width and the percentage of the on time compared to the off time is called the duty cycle. By altering the duty cycle, you can change the portion of the time the signal spends at 5V or 0V. If you connect an LED to one of the PWM outputs you can vary the brightness of the LED by adjusting the duty cycle. This happens because the LED receives the same amount of energy as it would with a lower analogue voltage so a duty cycle of 20% on a 5V signal would be the same as supplying the LED with a continuous 1V from an analogue power supply. This technique of generating an analogue voltage from digital pulses has a wide variety of uses within electronic circuits and is the method used to create a stable voltage in modern switch-mode power supplies like the ones found in computers and mobile phone chargers.
The graphic below shows five different duty cycles for a PWM signal from 0% to 100%.
Each of the green vertical lines in the graph represents a clock cycle. The frequency of the clock cycles on the Servo PWM Pi Zero is programmable and can be set between 40Hz to 1000Hz. When driving LEDs, you want to pick a frequency which is high enough for the human eye not to notice the flickering. Anything above 200Hz should be suitable.
Using the Servo PWM Pi Zero with Python
We will use the AB Electronics python library to talk to the Servo PWM Pi Zero, to download the library visit our Python Library and Demos knowledge base article.
You will need to enable i2c on your Raspberry Pi, see our other tutorial on i2c: I2C, SMBus and Raspbian Linux.
The AB Electronics python library uses two libraries called python-smbus and RPi.GPIO, you can install both libraries using apt-get with the following commands.
sudo apt-get update
sudo apt-get install python-smbus python-rpi.gpio
With the libraries installed and the Raspberry Pi configured to use i2c we can begin.
Generating a PWM signal
If you haven’t done so already install your Servo PWM Pi Zero onto your Raspberry Pi by connecting it to the GPIO header.
Open your favourite text editor and create a new file called tutorial1.py
The first thing we need to do is import the PWM class from the ServoPi module.
from ServoPi import PWM
If you have installed the Python library onto your Raspberry Pi using the setup.py file or PIP then your program should import the ServoPi module from the system path otherwise you will need to copy ServoPi.py into the same folder as tutorial1.py.
To access the Servo PWM Pi Zero you will create an instance of the PWM class and call it pwm with the I2C address for the board. The I2C address can be changed using the solder bridges on the board but by default it is 0x40.
pwm = PWM(0x40)
With the pwm object created we will set the frequency for the output pulses. We do this with the set_pwm_freq() function. The frequency can be any value between 40Hz and 1KHz so for this tutorial we will use the upper limit and set the value as 1000Hz or 1Khz.
The Servo PWM Pi Zero includes an output enable function which allows you to turn all the PWM channels on or off at the same time using GPIO4 or pin 7 of your Raspberry Pi GPIO header. This function is useful if you have several devices connected to your board which you want to switch on or off simultaneously.
If you have enabled the output enable function by bridging the OE jumper then you will need to call output_enable() to turn the PWM channels on. You can turn them off again by using output_disable().
Now we have the frequency set and the outputs enabled we can create a pulse on the outputs using the set_pwm() function.
set_pwm(channel, on, off) takes three parameters, channel, on time and off time.
The channel parameter sets the channel you want to update. This parameter is takes a number between 1 and 16.
The on time and off time parameters are 12-bit values which means they take a number between 0 and 4095. The on parameter is the time when you want the pulse to turn on and the off position is the time when you want it to turn off plus the on time.
Each clock cycle is divided up into 4096 segments. We set the clock frequency for this project at 1Khz which means each clock cycle is 1 millisecond long. 1ms divided by 4096 equals approximately 0.000,000,244 seconds or 244ns so each pulse can be between 244ns and 1ms.
If you wanted a pulse length of 100 μs which would be a 10% duty cycle of 1ms you would use the calculation: 0.0001 / 0.000000244 = 409.83 or rounded up to 410.
The following formula can be used when calculating the value for the on and off parameters.
x = 4096pf
p = length of the pulse in seconds
f = frequency of the clock in hertz
If you want to create a pulse on channel 1 with a length of 410 starting at the beginning of the clock cycle you would set the channel to 1, use an on time of 0 and an off time of 410.
pwm.set_pwm(1, 0, 410)
Save the python program and run it with the following command.
If you have an oscilloscope or logic analyser connected to channel 1 on your Servo PWM Pi Zero you should see something like the following image.
By changing the ‘on’ parameter you can change when the pulse begins during each clock cycle. Add the following lines of code to your program.
pwm.set_pwm(2, 410, 820)
pwm.set_pwm(3, 820, 1230)
pwm.set_pwm(4, 1230, 1640)
Save the program and run it again. If you have a four-channel oscilloscope or logic analyser connected to channels 1 to 4 you should see something like the following image.
Notice how the pulse on channel 2 begins as the pulse on channel 1 ends and so on for channels 3 and 4. This is because we changed the on time for each channel to match the off time for the previous channel.
On channel 2 the on time was set to 410 which is 100μs after the start of the clock cycle. As we wanted the pulse length to be 100μs we had to add 410 to the off time to make it 810. This was repeated for channels 3 and 4. The off time should always equal the on time plus the duration of the pulse. It should also never be more than 4095.
So why would you want to set the pulses to start at different times?
Let’s say you have a circuit with 10 LEDs connected to 10 channels on the Servo PWM Pi Zero. Each LED draws 20mA when switched on and you want to light the LEDs with a 10% duty cycle. During the on period of the pulse each LED will draw 20mA so if all the pulses start at the same point in the duty cycle you will have 10 LEDs all drawing power at the same time with 200mA being used in total.
If we offset the pulses so that channel 2 starts 10% into the cycle, channel 3 starts 20% in, etc, each LED will still be turning on for the same amount of time but only one LED will ever be powered at the same time so the total current being drawn on your circuit will only be 20mA. As well as reducing the size of the power supply you need to light the LEDs, by staggering the pulses you can minimize current surges.
For a more detailed explanation on the inner workings of the PCA9685 PWM controller used on the Servo PWM Pi Zero please read the component datasheet.
Note: documents in Portable Document Format (PDF) require Adobe Acrobat Reader 5.0 or higher to view, download Adobe Acrobat Reader or other PDF reading software for your computer or mobile device.