Contents
This is the second article in a series that explores GPIO programming on a Raspberry Pi 3B+. This article provides some details about hardware and software based PWM on the Raspberry Pi, specifically the 3B+ with the Broadcomm BCM2835 board. This article has an accompanying application, PWM Explorer1, that can be used to experiment with PWM settings and capabilities.
Overview
I was writing an article as a companion to the SunFounder RGB LED project to cover some areas that were missing in that project such as what is PWM, how does it work, and why the code was written as it was. I had no idea what PWM was or what I was doing or why. To fill the gap I started researching PWM and found numerous articles about PWM2 3 4, and also software libraries like WiringPi (C)5, go-rpio (Go)6, BCM2835 by Mike McCalley (C)7 and pigpio (Python & C)8. So with my focus still on controlling an RGB LED via GPIO and PWM I started writing programs in C and Go. I quickly had difficulty in several areas:
- Terminology is used inconsistently across articles and the libraries. For example, some used the terms Cycle and Range for the same thing.
- The libraries were documented to varying degrees. Coupled with a lack of common terminology it was difficult to figure out how to use them.
- Going hand in hand with figuring out how to use the libraries, it was difficult to understand how different parameters affected the behavior of the hardware, in this case an RGB LED.
My intent in writing this is to:
- Provide an overview of PWM and its main concepts.
- Provide a glossary that links the various PWM terms to a consistent definition.
- Explain how the various PWM settings interact with each other (not just duty cycle).
- Provide the information needed (sometimes via external references) to install a GPIO test bed on a Raspberry Pi 3B+.
- Provide programs, written in Go and C, that demonstrate how to effectively use WiringPi5 and go-rpio6.
- Provide an application that can be used to experiment with the various PWM parameters in real time.
- Provide hard to find information on some of the concepts and PWM settings (e.g., minimum/maximum PWM frequencies).
Lest anyone think I’m presenting myself as an expert in GPIO and PWM, I’m not. Despite my best attempts to summarize what I’ve learned, I’m sure I’ve made mistakes or made things more confusing. Any mistakes I’ve made are my own. If anyone comes across any mistakes topics that could be clarified, I’d appreciate comments so I can modify this article.
I’m not writing this to be a definitive source about all things PWM. There are better resources for that. This article lists those resources that I found particularly helpful.
Now to the outline of this article. The sections are as follows:
- Prerequisites covers areas like how to get the software libraries used in this article as well as things like the hardware needed by the PWM Explorer application to illustrate behavior.
- What is PWM? provides a very basic introduction to PWM. It provides definitions of common terms and the aliases used in the articles and software libraries.
- Overview of PWM on a Raspberry Pi 3B+ provides an overview of how GPIO and PWM are implemented on a Raspberry Pi.
- Exploring PWM on a Raspberry Pi provides a description of the various PWM settings, how they interact with each other, some hard to find tidbits about the PWM settings, and an overview of the application, PWM Explorer, that can be used to drive PWM on a Raspberry Pi to see how the settings interact with each other in real time.
- A Summary to wrap things up.
- And a set of useful References.
Prerequisites
If you don’t have one, you’ll need a Raspberry Pi. I used a Raspberry Pi 3B+ with the ‘Stretch’ version of the Raspbian OS. The Raspberry Pi website has instructions on how to setup a new Raspberry Pi from scratch9 if you decide to go that way vs. buying a complete kit.
Other items you’ll need include:
- a breadboard (You may find this tutorial on breadboards helpful10),
- some jumper wires
- a 220 Ohm resistor, and a RGB LED.
- You should also consider getting a 40 pin female to female with a T-Type adapter to attach the GPIO outputs to the breadboard. You can use only jumper wires, but the T-Type adapter will make things easier and will help prevent damage to the GPIO pins on the Raspberry Pi. If you elect not to buy the 40 pin cable with T-Type adapter you’ll need to buy male-to-female jumper wires. Buying all these things separately will cost more than a kit however.
Here’s a simple kit that has all of the above. I’m finding the Sunfounder Raspberry Pi Ultimate Starter Kit especially useful. NOTE: The Ultimate Starter Kit and the Raphael Kit are the same product.
You will also need some basic C and Go programming knowledge as well as familiarity with logging on to a Raspberry Pi terminal, or into the desktop GUI that comes with some OS versions. Depending on the approach you take, you may need to connect a keyboard and monitor to the Raspberry Pi. I simply SSH into the Pi. You’ll also need familiarity with how to use an editor like vim or nano.
To compile and run the C program you’ll need the WiringPi5 11 library. It’s easy to get:
sudo apt-get install wiringpi
Then test the installation using:
pi@pi-node1:~/go/src/github.com/youngkin/gpio/rgbled $ gpio -v
gpio version: 2.50
Copyright (c) 2012-2018 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty
Raspberry Pi Details:
Type: Pi 3B+, Revision: 03, Memory: 1024MB, Maker: Sony
* Device tree is enabled.
*--> Raspberry Pi 3 Model B Plus Rev 1.3
* This Raspberry Pi supports user-level GPIO access.
In the above you’ll notice gpio version: 2.50
. If you’re using a Rasberry Pi 4, use the instructions given in the Sunfounder Checking the WiringPi.
WiringPi is unique in that it includes a command line tool, gpio
, as shown above, that can be used to manage, control, and query the GPIO board. This can be very handy. See the gpio reference for more information on what it can do and how to use it.
If you’re interested in Go development on a Raspberry Pi you’ll need to install the development environment onto the Raspberry Pi. Here’s a simple source that explains how to accomplish this. This source is a little dated, but the only significant issue is with the version of Go to install. The source shows installing Go 1.14.4.linux-arm64.tar.gz and 1.14.4.linuxarmv6l.tar.gz. The current versions are 1.17.1.linux-arm64.tar.gz and 1.17.1.linuxarmv6l.tar.gz. For the Raspberry Pi 3B+ the correct choice will be 1.17.1.linuxarmv6l.tar.gz. The other is intended for 64 bit systems like the Raspberry Pi 4 series. You can find current ARM versions of Go at the Golang download site.
For Go development you’ll also need the go-rpio6 library.
If you want to veer away from the cookbook style of the Sunfounder docs you’ll need some basic knowledge of Linux . For example, I won’t be explaining what root privileges are.
Finally, I wrote an application that supports experimenting with the various PWM settings on the Raspberry Pi 3B+. There are 2 options for getting the code. First, you can clone or fork the project from GitHub. If you do that you’ll need to have experience with git and have it installed on the Raspberry Pi. See this article on installing git for more details. After installing git
you can download the project by running git clone https://github.com/youngkin/gpio.git
. If you would like to contribute the project please fork the respository instead. As an alternative to using git you can also download a zip file of the project by navigating to the project’s URL, clicking on the Code button above and to the right of the file listing, and selecting Download ZIP.
What is PWM?
At it’s most basic, PWM is used to simulate an analog signal using a digital source (like a GPIO pin). As an example, an LED’s brightness and/or color can be modified by varying the voltage supplied to the LED. A variety of analog devices, such as a motor’s speed, can be controlled in the same way. PWM simulates varying voltages by varying the length of the digital power pulse within a given duration.
In addition to what’s presented here, Introduction to PWM: How Pulse Width Modulation works describes some of the same concepts described here as well as a few more examples of how PWM can be used. The References section has links to more sources of PWM information.
The follow sections cover the main points:
- Terminology defines terms that will be used throughout the document
- Primary concepts describes the major concepts
Terminology
My research into PWM involved reading several articles2 3 4 as well as examining the code of several PWM software libraries in various languages 5 6 7. These various sources aren’t completely consistent in the terminology they use. Here are some of the common terms, their aliases, and definitions. See the diagram below for a visual representation of the terms.
- Frequency - Per Wikipedia 12, frequency is the number of occurrences of a repeating event per unit of time. In electronics an event is the peak of a wave to the next peak of the wave (analog). In digital terms an event is from the leading edge of one pulse to the leading edge of the next pulse. Frequency is measured in Hertz, which is the number of repeating events per second.
- Period - Also from Wikipedia 12, period is the duration of time of one cycle in a repeating event. Period is measured in seconds. So period is how long something takes vs. frequency which is how many times an event occurs in a given duration of time. This makes period the reciprocal of the frequency13 and vice versa, ie., unit of time per event vs. events per unit of time. In the above diagram the period is 10 milliseconds (ms). So the frequency in the above diagram is
1/.01
or 100. Since frequency is measured in Hertz, or events/second, the frequency in the above diagram is 100Hz. - Clock source14 - Also called a clock, it sets the rate at which the clock advances.
- PWM clock - Sometimes also called a clock so this gets a bit confusing. The PWM clock’s underlying input is the clock source. Different hardware devices, such as motors and servos, only work within specific period ranges. Often the source clock is too fast for these devices. The PWM clock is created by dividing the clock source’s frequency with a number that will result in the PWM clock operating at a frequency that’s appropriate for a given device. Different devices will need different PWM clock speeds. The number used as the denominator in this calculation is frequently called the divisor in software libraries and the BCM2835 Data Sheet15.
- Pulse - From the “P” in PWM. This is the minimum length of time a PWM pin’s output is set to high or low. Its minimum length is governed by the speed of the PWM clock. I’ll use pulse throughout this document, mostly because it’s in the name.
- Range - Range can be thought of as a counter that counts PWM clock pulses. The ratio of range to PWM clock frequency can be thought of as the frequency of the signal sent to a PWM pin. I’ve also seen the term cycle length used as as an alias for range6.
- Pulse width - Pulse width is the duration of a pulse. In the various software libraries I’ve seen it called width3, value5, data7 14, and duty length6.
Primary concepts
- Duty cycle is the ratio of Pulse Width to Range, i.e.,
Pulse-Width/Range
. For a range of 10 and a pulse of 5, the duty cycle is 5/10 or 50%. Duty cycle regulates the output voltage of a PWM device. For a 50% duty cycle and an input voltage of 5 volts, the output voltage will be 2.5 volts. - Software vs. Hardware PWM - For the purposes of this article there are 2 ways to generate a PWM signal, software-based and hardware-based16. Hardware-based PWM is generated by a dedicated hardware PWM device that can be configured to generate a PWM signal as described above16. Hardware-based PWM produces a very uniform signal with regard to timing. A uniform signal is required, for example, to produce a flicker-free light source such as an LED. Software-based PWM is directly implemented in the executing program using a
while(true)
for loop that never ends which controls the amount of time a pin is allowing current to flow (pulse) vs. the amount of time the pin isn’t allowing current to flow. In this case the uniformity of the signal is determined by the accuracy of a language’ssleep()
function and the OS (Linux) scheduler. A less uniform signal, for example, may result in a flickering light source. There is a more complete description of the difference between soft PWM and PWM and associated pros and cons on the Raspberry Pi Stack Exchange site. - Balanced vs. Mark/Space refers to the method used to determine how the PWM output signals are to be generated. There are 2 algorithms, balanced and mark/space. Balanced indicates that the duty cycle will be evenly spread across the range. That is to say, the pulse width will be split into a set of shorter pulses that are distributed across the range. In contrast, in mark/space, the pulse is generated as a single signal called a “mark”. The time remaining in the range,
range-pulseWidth
, is called the “space”. No signal is present in the space duration. Mark/Space is often good enough, but as periods get longer so does the absolute time difference between the mark and space durations. For large ratios ofrange/PWMClockFrequency
, e.g., 1 (which equates to 1Hz since the denominator unit is frequency) and a duty-cycle of 50%, the space will be 500 milliseconds and the mark will be 500 milliseconds. This difference is large enough to be discernable in the behavior of the device. For example a motor might surge or a light flicker. In contrast, balanced mode will smooth out these differences. For the same 1Hz range and 50% duty-cycle, balanced mode might produce 500 1 millisecond signals every 2 milliseconds (1/(500 * .002) = 1 = 1Hz
). The same 50% duty-cycle is produced, but the output signal is much smoother. Hardware implementations like the BCM2835 support both algorithms. It is possible to support both algorithms in software, but depending on the algorithms are implemented they may consume a significant amout of CPU.
Overview of PWM on a Raspberry Pi 3B+
Physically PWM is implemented via the BCM2835’s GPIO pins. The BCM2835 board has 40 pins, a subset of which are GPIO pins. Of the GPIO pins17 there are 4 hardware PWM pins, 13, 19, 12, and 18. The remaining GPIO pins, as well as the hardware GPIO pins, can be used for software PWM.
There are several clock sources available on the BCM2835 board. The clock source used by the GPIO libraries in this article is called the oscillator. It’s frequency as documented in several references5 6 7 is 19.2MHz with a period of about 52 nanoseconds (1 / 19,200,000 = ~0.000000052 seconds).
The BCM2835 board also implements something called a channel14. A hardware PWM pin is controlled by a channel. The PWM Clock, range, and pulse width are specified for a channel. All hardware pins connected to that channel will share the same range, pulse-width, and duty-cycle. The BCM2835 board has 2 channels. GPIO pins17 18 and 12 on one channel, 13 and 19 on the other. This means that a signal that’s sent to either pin that share a channel will go to both pins. For example, sending a signal on GPIO12 will also be shared with GPIO18 and vice versa14 15 13.
Exploring PWM on a Raspberry Pi
The setup for this exercise is similar to a combination of the SunFounder Blinking LED and Sunfounder RGB LED projects. If you’re familiar with wiring a breadboard the diagrams below may be all you need to get started. Otherwise it may be worth while looking at the Sunfounder LED projects and an introduction to breadboards10. The resistor used in both diagrams is 220 Ohms.
LED | Breadboard setup |
---|---|
This setup will be used to demonstrate software PWM on a non-PWM pin. The black wires are the ground. The upper black wire connects the board GND pin to the ground bus on the breadboard. The lower black wire connects the ground bus to the negative leg on the red LED (negative is the shorter leg). The red wire is for positive current. It connects GPIO6 to the positive leg on the red LED (positive is the longer leg). Note the 220 Ohm resistor bridging the red wire and the positive pin on the LED. This is required to avoid burning out the LED.
RGB LED | Breadboard setup |
---|---|
This setup will be used to demonstrate hardware and software PWM on a hardware PWM pin. Looking down at the the breadboard the RGB LED the pins are set up as follows:
- The red pin is at the top of the RGB LED. It’s connected to GPIO pin 13 with the red wire.
- The ground is the next one down. It’s connected to the ground bus with the black wire.
- The green pin is 1 down from the ground. It’s connected to GPIO pin 18 with the white wire.
- The blue pin is at the bottom of the RGB LED. It’s connected to GPIO pin 19 with the blue wire.
As with the previous diagram, a 220 Ohm resistor bridges the GPIO pins to the red, green, and blue pins of the LED.
This setup shows the green pin on the LED (white wire) connected to the GPIO hardware PWM pin 18. A different hardware PWM pin may be used such as 12, 13, and 19. Also, the use of an RGB LED isn’t required. Any type of LED will work. It will have to be wired up in a similar manner however, i.e., the LED positive lead should be connected to a hardware PWM pin (white wire) and the LED’s ground lead (the longest lead) to the board’s ground.
If you do choose to use an RGB LED pin it’s important to note that only 2 of the colors can be controlled by hardware PWM. The pins need to be on different PWM channels. In the diagram above the red and blue pins attached to GPIO pins 13 and 19 (blue and red) to demonstrate this limitation. Recall that GPIO pins 13 and 19 share a channel. As a result, a signal sent to one pin be propagated to the other. To get the true RGB LED colors software PWM is required, along with a maximum of 2 hardware pins on different channels. While hardware pins can be used, it’s also possible to use all non-hardware pins.
It’ll be easier to play with the different combinations of PWM settings when both of the above setups are wired up at the same time. It allows for any of the GPIO pins to be used by PWM Explorer, with the exception that for hardware PWM the LED(s) must be installed on hardware PWM pins (GPIO pins 13, 19, 18, and 12). If more than one hardware pin is used they must be on different channels.
Driving PWM using PWM Explorer
PWM Explorer supports C and Go. Choosing C you can experiment with both Mark/Space and Balanced PWM Modes. The Go library I used, go-rpio6 only supports Mark/Space.
As described in the Prerequisites section above, I wrote an application, called PWM Explorer, to experiment with PWM on a Raspberry Pi. This software can be used to drive PWM on both PWM and non-PWM pins. It also supports changing the various PWM parameters like divisor, range, and pulse width to provide visual feedback on the effect of these parameters on the behavior of the LEDs connected to the pins. This software is available on my gpio GitHub repository1.
The diagram above is a screenshot of the main program and has labels with brief descriptions of the various sections of the user interface. It’s a text based UI so that it can be run on a Raspberry Pi that doesn’t have the desktop capability installed. It can be run at the command line by navigating to the installation directory under the gpio/pwmdemo/pwmexplorer
directory and running sudo /usr/local/go/bin/go run main.go
. sudo
is needed because some of the GPIO access requires root
permissions. In addition to the main UI application there are 2 supporting programs in the gpio/pwmdemo/pwmexplorer/apps
directory, freqtest.go
and freqtest.c
. These are the actual implementations of the code required to interact with the PWM capabilities on the Raspberry Pi 3B+ using the go-rpio and WiringPi libraries. They are written in Go and C respectively. The C program must be built prior to running PWM Explorer. This is accomplished by changing to the gpio/pwmdemo/pwmexplorer/apps
directory and running gcc -o freqtest freqtest.c -lwiringPi -lpthread
.
There are a variety of PWM parameters supported. These are:
- PWM Pin - this item allows you to choose a PWM hardware pin to use. The pins available in the dropdown are specific to the language chosen. C uses the WiringPi7 library which uses its own pin numbering scheme. Go uses the standard GPIO pin numbers
- Non-PWM Pin - this item allows you to specify a PWM pin to use, even a hardware pin. PWM Pin and Non-PWM Pin are mutually exclusive and the program will prevent you from specifying both. As with PWM Pin above, the numbering scheme is specific to the language, C or Go, chosen. The program offers no protection against using the wrong pin numbering scheme so be careful what you specify. If the pin chosen doesn’t behave as expected it may be that you used the wrong pin numbering scheme.
- Clock Frequency/Clock Divisor - this item is used to set the PWM clock frequency. The go-rpio library supports specifying the PWM clock frequency directly. The C WiringPi library uses the concept of divisor defined above to set the PWM clock frequency. You can calculate the divisor to use by dividing the Raspberry Pi 3B’s oscillator clock’s frequency of 19,200,000 Hertz by the desired PWM clock frequency. For example. to get a 100kHz PWM clock frequency divide 19,200,000 by 100,000. This calculation gives the Clock Divisor to use, 192 in this case. To avoid confusion, when C is the chosen language the label will be Clock Divisor. When Go is the chosen language this items label will be Clock Frequency.
- PWM Mode - this item is used to specify whether Mark/Space or Balanced modes will be used. Note: some combinations of language, pin type (PWM vs. non-PWM), and PWM Type (hardware/software) don’t support Balanced mode. When this is the case a message will be displayed in the Messages area. The Go go-rpio library doesn’t support balanced mode.
- Range - the desired range as defined in the Terminology section above.
- Pulse Width - the desired pulse width as defined in the Terminology section above.
- PWM Type - this item is used to specify whether hardware or software PWM is to be used.
Depending on the terminal size some Help and Code text may not be visible. To address this, both the Help and Code sections are scrollable.
Experimenting with PWM Parameters
As was stated earlier, the WiringPi and go-rpio libraries both use the Raspberry Pi 3B’s Oscillator clock which has a frequency is 19.2MHz. This is fixed and cannot be changed. But besides the pins chosen, the Clock Frequency/Divisor, PWM Mode, Range, Pulse Width, and PWM Type can all be modified. All of these interact with one another either directly or indirectly. In this section I will explain these relationships and how they interact. You can use the PWM Explorer to follow along and directly see the effects that I’ll explain.
This section uses an LED to demonstrate the effect of the various parameters on a device. There is a property of the human eye that needs to be understood. The human eye perceives linear changes in in brightness in a logarithmic fashion. Specifically, at the lower end of a pulse width setting (lower voltage) changes in a given setting will produce what looks like a more significant result than the same change at the higher end (higher voltage)18. The PWM Explorer doesn’t compensate for this.
PWM Clock Frequency / Clock Divisor
The first thing to decide is what frequency you want the PWM clock to run at. It’s frequency is specified directly when using Go and via the divisor when using C. Choosing this frequency is impacted by the type of PWM device being used, e.g., an LED or a motor. This article doesn’t cover how to calculate this frequency, but there are sources that do3 4. Instead I’ll focus on the general impact of clock frequency on LED devices.
You may or may not be aware that the human eye can detect flickering starting at about 60Hz and below. Flickering is more apparent using peripheral vision. Given this, a PWM clock frequency to Range, PWMClockFrequency / Range
, below 60Hz isn’t ideal unless you’re trying to create a blinking LED. Assuming the range can’t be modified, choosing the right Clock Frequency/Divisor will directly impact whether an LED appears to be a steady light source, flickering, or blinking.
Here are some settings to try this out (these assume the LED is wired up to GPIO pin 18):
Setting | Go | C |
---|---|---|
PWM Pin | 18 | 1 |
Non-PWM Pin | N/A | N/A |
Clock Frequency/Clock Divisor | 5000 (10000) | 3840 (1920) |
PWM Mode | markspace | markspace |
Range | 167 | 167 |
Pulse Width | 2 | 2 |
PWM Type | hardware | hardware |
Using the above settings will result in a 30Hz frequency at the pin. As this is well below 60Hz flickering will be apparent. Changing the PWM Clock Frequency/Clock Divisor will result in a pin frequency of 62.5Hz. The result will be that there is no apparent flickering.
NOTE: The “General Purpose Clock Divisors” on the BCM2835 have a register width of 12 bits (see15, page 108, DIVI
field bits 23 thru 12). This means the maximum value of the Clock Divisor is 4095 (0 to 2^12-1). The go-rpio6 further states that PWM Clock frequencies below 4688Hz will result in “unexpected behavior” (rpio.go, see comments for SetFreq()
function). Other sources19 state that 9.6Mhz is the highest available PWM Clock frequency (19.2Mhz/2). I’ve seen unexpected results with both frequencies below 4688Hz and above 9.6Mhz.
Range
Range effectively determines the frequency of the signal at the GPIO pin. This means that the frequency at the pin is defined by the ratio of PWM Clock Frequency/Range
. Another thing range determines is the resolution of the signal going to the device. Recall that Duty Cycle is the ratio of Pulse Width to Range. Starting with a lower value of range, say 4, limits duty cycle to 0%, 25%, 50%, 75%, or 100%. To say it a different way, with a Range of 4 the only values for Pulse Width that make sense are 0, 1, 2, 3, 4. This in turn limits things like the range of LED brightness or blinking that’s available.
Since range impacts frequency at the pin and the resolution, it is important to choose the correct PWM clock frequency, Range, and Pulse Width in combination. Starting with a low PWM Clock frequency limits the choice of range which in turn limits the available duty cycles.
As an example let’s choose 2 extremes. For the first let’s choose a PWM Clock frequency of 5kHz and a range of 5. The resolution is 5. This means are only 5 available duty cycles including full on and full off. As described above this limits the available brightness settings for a light like an LED.
Here are the settings to try this example (the settings for the second example are in parentheses):
Setting | Go | C |
---|---|---|
PWM Pin | 18 | 1 |
Non-PWM Pin | N/A | N/A |
Clock Frequency/Clock Divisor | 5000 (600000) | 3840 (32) |
PWM Mode | markspace | markspace |
Range | 5 (10000) | 5 (10000) |
Pulse Width | 1 (5, 2, 100, 1000, 10000) | 1 (5, 2, 100, 1000, 10000) |
PWM Type | hardware | hardware |
Then change to Pulse Width to 5. This will be the brightest setting of the LED. While it is noticably brighter than a Pulse Width of 1, due to the narrow range it’s not that much brighter.
At the other extreme let’s configure an LED for an extremely smooth transition from off to full brightness. This will require a higher resolution. Let’s go crazy and decide that we’d like to have 10,000 steps of brightness available. This translates to a range of 10,000. To avoid visible flickering we need to have at least a 60Hz signal at the GPIO pin. Since the ratio of PWM Clock Frequency/Range
determines the signal frequency at the pin, we will need a PWM Clock frequency of at least 600kHz. A range of 10000 times a minimum pin signal frequency of 60Hz is 600kHz (10,000 * 60 = 600,000). To check our work, PWM Clock Frequency / Range
, 600,000 / 10,000 = 60 (Hz).
Pulse width
At higher GPIO pin frequencies pulse width effects the brightness of an LED. At lower GPIO pin frequencies pulse width is visible as the length of time the LED is on vs. the blink rate. Let’s again use extreme examples to illustrate this.
For the first example let’s use a PWM clock frequency of 1MHz and a range of 10,000. The frequency at the GPIO pin will be 100Hz 1,000,000/10,000), fast enough that no blinking or flickering will be visible. The range of 10,000 is likewise high enough that we won’t be able to discern discrete steps in the brightness of the LED.
Here are the settings for the first example:
Setting | Go | C |
---|---|---|
PWM Pin | 18 | 1 |
Non-PWM Pin | N/A | N/A |
Clock Frequency/Clock Divisor | 1000000 (5000) | 19 (3840) |
PWM Mode | markspace | markspace |
Range | 10000 | 10000 |
Pulse Width | 100 (5000) | 100 (5000) |
PWM Type | hardware | hardware |
For the second example change the Clock Frequency (Go) to 5000, Clock Divisor (C) to 3840, the Range to 10000 and the Pulse Width to 5000. The LED will flash for a very short period of time and will repeat flashing about once every 2 seconds (Clock Frequency/Range, 5000/10000 = 0.5Hz or once every 2 seconds). Now change the Pulse Width to 5000. Now the LED will be on for 1 second and off for 1 second. This changed the duty cycle from 2% to 50% with the corresponding change in the duration of the LED flash. This example shows how to create a blinking light using PWM, just choose a PWM Clock Frequency and range that result in a very low GPIO pin frequency, well below 60Hz.
PWM Mode
The available PWM modes are balanced and mark/space. Recall that with mark/space the signal is either on or off (high or low) for a fixed duration within the range. For example, with a duty cycle of 50% and a range of 10, the signal will be on for 5 consecutive seconds and off for 5 consecutive seconds. As explained above, balanced mode will spread this 50% duty cycle evenly across the range which will make the effects of the duty cycle less apparent, except for a dimming of the LED (e.g., a 10ms pulse, every 10ms, 500 times over 10 seconds).
To illustrate this behavior set the Clock Frequency to 4688 (Go), Clock Divisor to 4095 (C), the PWM Mode to markspace, the Range to 1000, the Pulse Width to 10, and the PWM Type to hardware. This results in a blinking LED at a pin frequency of about 4.69Hz, or almost 5/second. Now change the PWM Mode to balanced. The language must be C to use balanced mode as go-rpio only supports mark/space. Now the blinking is no longer apparent.
Setting | Go | C |
---|---|---|
PWM Pin | 18 | 1 |
Non-PWM Pin | N/A | N/A |
Clock Frequency/Clock Divisor | 4688 | 4095 |
PWM Mode | markspace ( |
markspace (balanced) |
Range | 1000 | 1000 |
Pulse Width | 10 | 10 |
PWM Type | hardware | hardware |
PWM Type
The available PWM Types are hardware and software. The examples above used hardware PWM only. This is because true balanced mode is only available on hardware. In addition, the software PWM as implemented in Go and C is more akin to balanced mode and definitely not mark/space. Both the Go and C versions have a hardcoded 100MHz clock. Some of the examples in previous sections rely on mark/space behavior, especially the PWM Mode section.
Other things to try
To see how the above works with non-PWM pins, try some or all of the above on non-hardware PWM pins using a PWM Type of software. Monitoring CPU usage during these tests might show some differences between CPU usage for hardware vs. software PWM.
PWM Explorer code
The code in the PWM Explorer GitHub respository is extensively commented. To avoid repeating what is best illustrated directly in the code there’s not much to say here. The structure of both programs is substantially similar although there are differences is how the Go and C libraries are used to accomplish the same task.
Key takeaways
- On the Raspberry Pi there are only 4 hardware PWM pins and 2 channels. There are 2 pins per channel. This reduces the number of effective hardware PWM pins to 2.
- Care must be taken when specifying the Clock Frequency or Clock Divisor. The acceptable range for PWM Clock frequency is 4688Hz to 9.6Mhz.
- Range determines both the frequency at the GPIO pin and the resolution of the signal to a device like an LED or motor. The first, frequency, is significant because at lower pin frequencies the on/off states of the pin become very apparent, as in a blinking LED light. The second, resolution, is important because it defines how fine grained the control of the device will be. For example, at low resolutions there are only a limited set of LED brightnesses that can be chosen and the differences are very apparent. At higher resolutions small changes in pulse width will result in a very small, hardly discernable, change in brightness. This is important when implementing something like a dimmer where the change in brightness from off to fully on should not change in large, discernable steps of brightness. I.e., a smoother transition through the range is desired.
- For higher resolutions (Range) a higher PWM clock frequency should be chosen. This ensures that the GPIO pin frequency won’t drop too low, e.g., below the 60Hz frequency needed to avoid a flickering LED light.
- At higher GPIO pin frequencies (e.g., above 60Hz) changing the pulse width will cause the LED to appear dimmer or brighter.
- At lower GPIO pin frequencies (e.g., below 60Hz, and especially below 10Hz) pulse width can be used to cause obvious pulsing, as in a blinking LED light. The length of time the light is on is directly proportional to the pulse width.
- Choice of PWM mode also impacts the “smoothness” of the signal. Even at low GPIO frequencies, which would normally cause an LED to blink, balanced mode will reduce that effect, often to the point of rendering it unnoticable.
- PWM type (software vs. hardware) can affect the consistency of the period of a PWM signal to the point that even above 60Hz an LED might noticably flicker. It can also result in an inordinate amount of CPU load needed to support the desired clock rate.
- Software PWM can be used, but the resulting signal can be too ragged to be used in some devices.
- Setting a range that is greater than the PWM Clock frequency may seem odd, but it can be used effectively to create a low frequency, pulsing signal that can be used for, among other things, causing an LED to blink.
Summary
Hopefully by now you have enough knowledge to start effectively using PWM on a Raspberry Pi 3B+, or perhaps on other Raspberry Pi models or even on other platforms entirely. Specifically:
- We’ve gone through the various terms so you should now be able to read most of the literature on PWM and understand the concepts and terms used in those sources.
- If you followed along setting up and experimenting with PWM on your Raspberry Pi you now have a full, working, GPIO test bed installed on your Raspberry Pi.
- You should have an understanding of the various PWM settings and how they interact with each other.
- If you used the PWM Explorer application you have valuable hands-on experience with using various combinations of PWM settings. You can continue to use the PWM Explorer to experiment with various settings when you have questions or to test assumptions.
- By reading the Go and/or C code you now know how to effectively use the go-rpio and/or WiringPi libraries. Even if you decide to use other libraries, like pigpio8, you should understand the terminology well enough to use them effectively, or at least have a good start on learning how to use them.
References
-
PWM Explorer is an application that accompanies this article that can be used to experiment with various PWM settings. It demonstrates PWM using both Go and C libraries. ↩︎
-
Introduction to Microcontroller Timers: Periodic Timers is a good general introduction to timers. PWM hardware is one type of periodic timer. It is useful to read this introduction. ↩︎
-
Pulse-width Modulation (PWM) Timers in Microcontrollers is a good detailed discussion about PWM timers. It’s an excellent read if you want more detail than presented here. ↩︎
-
Raspberry Pi And The IoT In C - - Pulse Width Modulation, Servos And More is a detailed book about programming GPIO on the Raspberry Pi. There is a chapter devoted to PWM. ↩︎
-
bcm2835 by Mike McCalley is another C library for GPIO programming. ↩︎
-
pigpio is primarily a Python library. It also has a C library. It has the distinction of performing hardware PWM on any GPIO pin. I’m not sure how it does this. ↩︎
-
Setting up your Raspberry Pi is the official documentation for getting started with a Raspberry Pi including things like required hardware as well as how to install the OS and other important details. ↩︎
-
Breadboard Basics - Types is a useful introduction to breadboards and how to use them. ↩︎
-
WiringPi library reference guide documents the main WiringPi functions. The rest of the website also has some useful information. ↩︎
-
Frequency page on Wikipedia discusses frequency and period ↩︎
-
A Frequency to Period Calculator is handy for quick calculations, or validation, of frequency to period conversion (period = 1/frequency). ↩︎
-
The full Broadcom spec for the BCM2835, starting at page 138, for more details about how PWM is implemented on the BCM2835. ↩︎
-
There’s a detailed discussion about Which pin(s) on RPi 3B is PWM capable. Specifically regarding the effect of sharing 2 PWM channels for 4 PWM pins. The Broadcom spec14 also discusses this in section 9.4 on page 139, but in a less obvious way. ↩︎
-
There is a 3rd way to generate PWM signals, DMA or Direct Memory Access. It won’t be discussed here in this article. ↩︎
-
RaspberryPPi Pinout is a good source that describes each pins’ role, physical pin number, and for GPIO pins the GPIO pin number for the BCM2835 board. It has tabs that can be used to highlight which pins serve which purpose, e.g., PWM pins. ↩︎
-
Linear LED PWM provides guidelines/formulas for getting linear scaling when changing the brightness of an LED via PWM. Googling “LED PWM linear brightness” brings up several other articles as well. ↩︎
-
Driving PWM output frequency provides some interesting information and discussion on Raspberry Pis with 26 and 40 GPIO pins. ↩︎