Pulse Width Modulation on the Raspberry Pi
Hardware PWM vs Software PWM
The Raspberry Pi exposes two PWM channels from the Broadcom chip. These appear on lines which also supply GPIO connections and so the Broadcom ALT functions need to be called.
PWM0 appears on board pins 12 (GPIO18) and 32 (GPIO12).
PWM1 appears on board pins 33 (GPIO13) and 35 (GPIO19).
This causes a confusing situation in two ways:
- Don’t confuse board pin 12 with GPIO12 (board pin 32). Both are connected to BCM PWM0
- If you want to use two PWM channels, there are only certain combinations of pins that will provide the two channels. These combinations are board pins (12,33), (32,33), (12,35), or (32,35)
rpi_pwm()
handles these issues. If you stick to board
pin numbers, it will all work out. If you go astray,
rpi_pwm()
will stop
and warn you.
The Raspberry Pi analog audio output uses both PWM channels. You can’t use hardware PWM and audio output at the same time.
Although it is possible to create software PWM, this isn’t advised
due to latency of linux and R. rpigpior
does not supply any
software PWM channels.
Enabling hardware PWM on the Raspberry Pi
Raspberry Pi OS uses the device tree to enable hardware PWM. You’ll need to do a bit of behind-the-scenes editing to set this up.
To do this, edit /boot/config.txt as root.
On the Raspberry Pi, open a terminal window
Use the command
sudo nano /boot/config.txt
. This will open the nano text editor and display the contents of /boot/config.txt.Add the
dtoverlay
command at the end of the file. The easiest way to determine the command to add to/boot/config.txt
is to askrpi_pwm()
for help. Setpwm_debug=TRUE
then select the pins you want to use. If they aren’t enabled,rpi_pwm()
will stop and provide you with the correctdtoverlay
string. This looks something likedtoverlay=pwm,pin18,func-2
but will change depending on the pins you have chosen.
If you want to do the math yourself, there is an excellent guide available at github.com/dotnet. There is also linux documentation that discusses Enabling one PWM channel or Enabling a dual pwm channelUse control-o to write the file, then control-x to exit
nano
Reboot your Raspberry Pi to enact this change.
Note that if you rebuild your SD card or reinstall Raspberry Pi OS on the OS card, you’ll need to repeat this step.
How to use rpi_pwm()
Here’s an example:
rpi_pwm(c(12, 33), pwm_period = 50000, pwm_dutycycle = 25000)
# This will enable PWM0 and PWM1 with a period (frequency) of 50000 and a duty cycle of 25000. That is to say, 50% power.
Or…
rpi_pwm(12, pwm_period = 50000, pwm_dutycycle = 10000)
# This will turn PWM0 down to 20%
Handling Errors:
If you set pwm_debug = TRUE
then rpi_pwm()
will run diagnostics as it goes.
rpi_pwm(12, pwm_period = 50000, pwm_dutycycle = 10000, pwm_debug = TRUE)
Invalid PWM pin:
You’ve called rpi_pwm(pin_number)
where the pin (or
pins) specified don’t supply hardware Pulse Width Modulation. On the
Raspberry Pi, this is only board pins 12, 32, 33, or 35. Keep in mind
rpi_pwm()
only specifies pins as board pins - not GPIO or
BCM pins. Lots of documentation will refer to these other pin numbering
schemes.
Invalid PWM pin combination:
As you know by now, the Raspberry Pi only has two hardware PWM channels, but each channel is connected to two pins:
PWM0 appears on board pins 12 (GPIO18) and 32 (GPIO12).
PWM1 appears on board pins 33 (GPIO13) and 35 (GPIO19).
So…if you are going to use two PWM channels, you need to use pins
connected to different channels. Since board pin 12 and 32 are both
connected to PWM0, it would be nonsense to use
rpi_pwm(pin_number = c(12,32)
. Instead, these are the valid
combinations:
rpi_pwm(pin_number = c(12,33)
rpi_pwm(pin_number = c(12,35)
rpi_pwm(pin_number = c(32,33)
rpi_pwm(pin_number = c(32,35)
If you try to select an invalid combination and
pwm_debut = TRUE
you will receive an error.
PWM not enabled:
The PWM channel (or channels) you have selected are not yet enabled.
Enabling PWM is described in the above section titled Enabling hardware PWM
on the Raspberry Pi . The error message from rpi_pwm
supplies the string you’ll need to paste into
/boot/config.txt
.
Notes:
The Linux driver implementer’s API guide has a chapter on PWM : https://www.kernel.org/doc/html/v5.10/driver-api/pwm.html