PDM library (Pulse Density Modulation)
Software PDM
Software PDM (Pulse Density Modulation) is a viable alternative to software PWM where mcu resources are limited because we only need one timer to control the output (whereas PWM requires two timers).
The code presented here is based on an article and source code released by Ken Wada from Aurium Technologies.
Additional info on Pulse Density Modulation is available here.
Keep in mind that the dynamic range is limited when we bit-bang PDM as following limitations apply:
- A very low pulse width value will inevitably break (and even stop) other code. This value depends on the complexity of the additional user code. A decent start value for a single instance PDM is
100us
or more. Multiple PDM instances will require a higher value. - Keep the maximum level (DMAX) within reasonable limits. To get a higher precision, we would be tempted to set this as high as possible. As a result, to realize the highest and lowest states (near zero, and near maximum), we need to wait at least for the DAC's maxlevel number of updates in order for those states to manifest properly into the output.
Both the pulse width and DMAX values determine the highest frequency we can reproduce :
1 max. frequency = ----------------- PulseWidth x DMAX Example ------- pulse width = 10us DMAX = 256 counts 1 max. frequency = ------------- = 390.625Hz 0.00001 x 256
Applications
- Low frequency analog output for mcu's without DA converters.
- LED brightness control.
- ...
Examples
Basic use
// mbed-shiny #include "mbed.h" #include "SoftPdmOut.h" SoftPdmOut pdm(LED1); int main() { float pdmSet = 0.0f; float pdmAdd = 0.01f; // Continuously cycle the output while(1) { pdm = pdmSet; wait_ms(10); if(pdmSet >= 1.0f) pdmAdd = -0.01f; if(pdmSet <= 0.0f) pdmAdd = 0.01f; pdmSet += pdmAdd; } }
Demonstrate library functions
#include "mbed.h" #include "SoftPdmOut.h" Serial pc(USBTX, USBRX); // optional parameters, when omitted, their default library value will be used. #define PULSEWIDTH 100 #define DMAX 64 #define STARTLEVEL 0.5f // PinName and optional pulse width, dmax, start level // When SoftPdmOut pdm(LED1); is used, all optional parameters revert to their default values (defined in the library). SoftPdmOut pdm(LED1, PULSEWIDTH, DMAX, STARTLEVEL); int main() { pc.baud (38400); float pdmVal; printf("PDM test\r\n"); pdmVal = 0.1f; // Stop the PDM with pin idle value = 0 printf("Stop the PDM with pin idle value = 0\r\n"); pdm.stop(0); wait(1); pdm.start(); // Stop the PDM with pin idle value = 1 printf("Stop the PDM with pin idle value = 1\r\n"); pdm.stop(1); wait(1); pdm.start(); // Set PDM level, read it back and print it, can be done in two ways : // using pdm.write(nn) and pdm.read(nn) OR pdm = nn and <variable> = pdm. printf("Set the current PDM level to %f\r\n", pdmVal); // Write the current PDM level using pdm.write() pdm.write(pdmVal); // Write the current PDM level using the shorthand notation instead of pdm.write() pdm = pdmVal; printf("Get the current PDM level : %f\r\n", pdm.read()); // Read the current PDM level using the shorthand notation instead of pdm.read() float dummy = pdm; printf("Get the current PDM level : %f\r\n", dummy); wait(1); // Change the pulse width and DMAX values. pdm.PulseWidth(100); pdm.Dmax(128); float pdmSet = 0.0f; float pdmAdd = 0.01f; // Cycle the output while(1) { pdm = pdmSet; wait_ms(10); if(pdmSet >= 1.0f) pdmAdd = -0.01f; if(pdmSet <= 0.0f) pdmAdd = 0.01f; pdmSet += pdmAdd; } }
History
Dmax : Rescale level when DMAX changes.; only stop/start PulseWidth when running.
2014-12-28, by frankvnk [Sun, 28 Dec 2014 18:01:50 +0000] rev 6
Dmax : Rescale level when DMAX changes.; only stop/start PulseWidth when running.
add delay to start()
2014-12-28, by frankvnk [Sun, 28 Dec 2014 15:57:53 +0000] rev 5
add delay to start()
fixed NULL mistakes, changed default pulsewidth to 500us
2014-12-27, by frankvnk [Sat, 27 Dec 2014 11:52:58 +0000] rev 4
fixed NULL mistakes, changed default pulsewidth to 500us
added NULL to Dmax and PulseWidth
2014-12-27, by frankvnk [Sat, 27 Dec 2014 09:28:57 +0000] rev 3
added NULL to Dmax and PulseWidth
fixed PulseWidth and Dmax
2014-12-27, by frankvnk [Sat, 27 Dec 2014 09:16:52 +0000] rev 2
fixed PulseWidth and Dmax
PulseWidth and Dmax functions : added read current value
2014-12-27, by frankvnk [Sat, 27 Dec 2014 09:13:41 +0000] rev 1
PulseWidth and Dmax functions : added read current value
Initial release
2014-12-25, by frankvnk [Thu, 25 Dec 2014 10:47:55 +0000] rev 0
Initial release