Software PWM 1.1
Software PWM for any digital pin
Any pin PWM Library

This library is designed to provide 'software' PWM output for any digital pin.

Important Notes:

  • This library uses AVR TIMER1 or TIMER2 to implement the interrupt driven clock. TIMER0 is used by the Arduino millis() clock, TIMER1 is commonly used by the Servo library and TIMER2 by the Tone library. Change USE_TIMER (defined at the top of the header file) to select which timer is enabled in the library code.
  • This library has been tested on Arduino Uno and Nano (ie, 328P processor). It should work on other AVR chips (eg, MEGA) with slight modifications but it will not work on non-AVR architectures without some extensive rework.

Why use Software PWM?

The Uno and Nano have 6 hardware PWM pins (3, 5, 6, 9, 10, 11). However, of these pins, the following also have alternative functions:

Pin Alternate Use
3 External Interrupt
5 -
6 -
9 -
10 SPI SS (default)
11 SPI MOSI

So, if the application requires 2 external interrupts and an SPI interface, there are really only 3 PWM hardware signals available for additional control. As an example, for 2 DC motors with a PWM controller, 4 PWM signals are required, so we don't have enough hardware PWM pins to get the job done.

One solution is to change hardware to a processor with more PWM pins. Another is to create a PWM solution that uses software to drive the pins.

This second option is feasible, especially when the PWM signal needed is relatively low frequency. The downside is that the CPU is used to process the timer interrupt and toggle the PWM digital output, reducing the processing time available for other tasks.

The original use case for this library was for PWM speed control of brushed DC motors. The default Arduino Uno/Nano PWM frequency is 490.2 Hz for pins 3, 9, 10, 11 and 976.56 Hz for pins 5 and 6. These frequencies are actually too high to properly drive DC motors at low duty cycles, as the current through the motor coils does not rise fast enough to provide motive force through the motors. See this excellent summary from Adafruit https://learn.adafruit.com/improve-low-speed-performance-of-brushed-dc-motors/pwm-frequency.

Implementation

This library implements user defined frequency PWM output for any digital pin software limited to MAX_FREQUENCY Hz.

The TIMERn is set for 255 times this frequency (eg, 200Hz becomes 51kHz). This causes the TIMERn interrupt routine to be called 255 times for each PWM cycle and, depending on where it is in the cycle, allows the software to set the digital output pin to LOW or HIGH, thus creating the desired PWM signal. This is illustrated below.

The duty cycle can be changed very smoothly by changing the set point at which the digital transition occurs. The new duty cycle takes effect at the start of the next PWM transition.

TIMERn is a global resource, so each object instance of class is driven from the same TIMERn interrupt. The constant MAX_PWM_PIN is used to set limits the global maximum for instances allowed to be processed by the interrupt.

See Also