Notes on: Pulse Width Modulation (PWM) Discussion - RichardChambers/raspberrypi GitHub Wiki
Pulse Width Modulation (PWM)
Pulse Width Modulation (PWM) is a technique for generating a signal that has characteristics of an analogue signal using digital equipment and digital voltage levels (on or off e.g. 3.3v or 0.0v) and varying the time that the voltage level is high or on and the time the voltage level is low or off.
The basic idea is to set a time period for a cycle from beginning of high voltage to end of low voltage and to then specify the time period for the high voltage. There are several measurements or quantities used when discussing Pulse Width Modulation.
The Duty Cycle, the percentage of time the voltage is high, is calculated by dividing the time period when the voltage is high by the time period of a complete single cycle (time period when voltage is high plus the time period when the voltage is low). Duty Cycle is measured in percent.
The Frequency is the number of times per second a complete single cycle (beginning of high voltage time period to end of low voltage time period) is performed. Frequency is measured in Hertz (cycles per second).
For controlling physical phenomena such as the amount of illumination of a light or the speed a servo motor travels we need to have a high enough Frequency such that the phenomena appears to be continuous.
Pulse Width Modulation and wiringPi library
The Raspberry Pi GPIO board has a pin that provides hardware level Pulse Width Modulation, GPIO #18. This pin can also be used for general Input or Output however it also has the necessary hardware support for PWM. The Raspberry Pi 3 has two of these hardware PWM pins unlike earlier versions of the Pi.
See the following postings in Raspberry Pi at stackexchange:
-
Control Hardware PWM frequency: https://raspberrypi.stackexchange.com/questions/4906/control-hardware-pwm-frequency/9725#9725
-
Driving PWM output frequency: https://raspberrypi.stackexchange.com/questions/53854/driving-pwm-output-frequency/53855
The wiringPi library has four functions that are used with setting Pulse Width Modulation frequency. These functions are:
- pwmSetMode() set the operating mode of the PWM hardware
- pwmSetRange() set the range counter register
- pwmSetClock() set the clock divisor register (clock runs at 19.2 Mhz), how often range counter is decremented
- pwmWrite() set the Duty Cycle using a value of 0, 0%, to 1023, 100%
To calculate the Frequency or cycles per second use the formula: Frequency = 19.2 Mhz / pwmSetClock() / pwmSetRange().
So if you want to have a Frequency of 1 Khz (1,000 hertz) you would create a formula of: 1 Khz = 19200 Khz / setClock / setRange
pwmSetMode()
The wiringPi function pwmSetMode() function sets the Pulse Width Modulation mode of the Broadcom hardware. There are two modes for hardware PWM: Balanced Mode (PWM_MODE_BAL) and Mark Space Mode (PWM_MODE_MS). The difference between these two modes is the timing sequence of voltage high and low values that are transmitted.
With Balanced Mode the hardware sends out a series of high and low voltages so that the high voltages are evenly distributed over the time range of a single cycle.
With Mark Space Mode the hardware sends out a high voltage and then a low voltage to complete a single cycle.
The difference between these two operating modes is how the high voltage time period is distributed over a single cycle. With Mark Space Mode the high voltage time is a single, continuous time at the beginning of a single cycle. With Balanced Mode the high voltage time is multiple separate times that are distributed over an entire single cycle.
Mark Space Mode is the same as the classic pulses at a given frequency model. If the frequency is 100 hertz (cycles per second) and the Duty Cycle is 50% then watching the output of the PWM GPIO pin on an oscilloscope you would see 100 times per second a high voltage for 50% of one hundredth of a second (5 milliseconds or 0.005 seconds) followed by a low voltage for 50% of one hundredth of a second (5 milliseconds or 0.005 seconds).
Balance Mode spreads the high voltage time across the cycle time. If the frequency is 100 hertz (cycles per second) and the Duty Cycle is 50% then watching the output of the PWM GPIO pin on an oscilloscope you would see 100 times per second a series of 50 high voltage followed by low voltage pulses. In other words rather than seeing a high voltage pulse of 5 milliseconds every 10 milliseconds you would instead see a high voltage pulse of 2.5 milliseconds (2500 microseconds) every 5 milliseconds.
pwmSetClock()
The pwmSetClock() function sets the divisor for the standard clock frequency of 19.2 Mhz. The allowable range of values seems to be from 2 to 4095. This means the range specified by pwmSetRange() can be divided up into 9.6 Mhz to .004688 Mhz or 4.688 Khz.
A couple of nice values to use are 1,920 which evenly divides 19.2 Mhz to give 10 Khz or 3,840 which evenly divides 19.2 Mhz to give 5 Khz.
pwmSetRange()
The pwmSetRange() function sets the range over which the the clock value is spread. The allowable range of values seems to be from 2 to 4096. This means the range of values specified by pwmSetClock() can be further divided up. For instance if pwmSetClock() is used with a value of 1000 (0.0192 Mhz or 19.2 Khz) we can further reduce the Frequency to 1.92 Khz by using a value of 10 with pwmSetRange().
This range is actually a value loaded into a range counter. For each tick, where a tick is the value of 19.2 Mhz divided by the clock divisor set with the pwmSetClock() function, the range counter is decremented until it reaches a value of zero. The range counter is then reset with the range value as set by function pwmSetRange() and once again the counter is decremented until zero.
pwmWrite()
The pwmWrite() function sets the Duty Cycle which is how long during each complete cycle of beginning of high voltage to end of low voltage the high voltage is applied. The value used is a value between 0 and the range set with pwmSetRange(). A value of 0 is a Duty Cycle of 0% and a max value is a Duty Cycle of 100%. To calculate the value multiply the Duty Cycle desired times the pwmSetRange() value e.g. if pwmSetRange(1000) then for a Duty Cycle of 50% you would multiply 1000 * 0.5 for a pwmWrite() value of 500.
Additional reading
Introduction to Pulse Width Modulation: https://barrgroup.com/Embedded-Systems/How-To/PWM-Pulse-Width-Modulation
Introduction to Pulse Width Modulation: https://www.embedded.com/electronics-blogs/beginner-s-corner/4023833/Introduction-to-Pulse-Width-Modulation
Introduction to Pulse Width Modulation: https://learn.sparkfun.com/tutorials/pulse-width-modulation
Technical introduction to Pulse Width Modulation circuits: https://www.engineersgarage.com/tutorials/diy-circuit-design-pulse-width-modulation-pwm
Using Pulse Width Modulation with motors: http://www.electronics-tutorials.ws/blog/pulse-width-modulation.html
How to make a PWM circuit without a micro-controller: http://www.robotroom.com/PWM.html
Raspberry Pi and the IoT In C -- Pulse Width Modulation, Servos and More: https://www.iot-programmer.com/index.php/books/22-raspberry-pi-and-the-iot-in-c/chapters-raspberry-pi-and-the-iot-in-c/60-raspberry-pi-and-the-iot-in-c-pulse-width-modulation-servos-and-more?showall=&start=1