Library that allows for higher resolution and speed than standard mbed PWM library using same syntax (drop-in replacement).

Dependents:   PwmOscillator FastStepDriver TLC5940 CameraTest ... more

FastPWM is a library that unlocks alot more of the potential of the mbed's PWM units than the normal PWM library. It is currently available for the LPC1768, LPC11u24, KLxxZ, K20D50M and most STM32 targets (those lacking are fairly easy to add). (Since I generally forget to update this list, if you want to know if your target is supported see if it compiles).

The two main points it allows for compared to the regular PwmOut library is clock cycle precision PWM and (automated) changing prescalers. It isn't perfect yet, but for now it will have to do ;). For those familiar with version 1, version 2 is almost completely rewritten to add more functions.


FastPWM is largely a drop-in replacement for the normal PwmOut library. All the same functions are available, with some extras.



All prescaler options are disabled for 32-bit PWM units currently, the prescaler is fixed at 1


With this function you can set the value of the prescaler. Aditionally the second argument of the constructor is used for the same to directly set it from the constructor. It returns the actual prescaler which is set. If the requested one isn't available it is always rounded up (unless it is larger than the maximum prescaler).

There are three options for this function. Any value larger than zero will simply be set. (Yes it is signed, so yes you cannot use the full 32-bit prescaler if your device supports it, I cannot imagine why you possibly would want that). If the value is zero dynamic prescaling is disabled and the current prescaler is returned. If the value is -1 dynamic prescaling is enabled and the current prescaler is returned.

So what is dynamic prescaling? This is the default option for FastPWM, don't use any prescaler option and it is enabled. To start with the negative, it adds quite some processing cycles, so changing the period takes longer. Luckily generally the PWM period is constant. The good part is that it automatically adapts the prescaler unit to give as much accuracy as possible: It gives highest accuracy for the duty-cycle, and also allows you to generate a wide range of periods. For example you can now create a LED blinking at 1Hz with FastPWM on the LPC11u24/Nucleo 16-bit PWM units. (On the KL25Z this isn't possible due to limitted value of the prescaler).

As the nice warning message above says, this is currently only implemented for 16-bit PWM units, simply because normally you won't need it for 32-bit PWM units. For those it is automatically disabled, and you cannot enable it. However for example the majority of the PWM units of the LPC11u24 can't be used to make servo signals with PwmOut, with FastPWM they can.

TL;DR, by default it uses dynamic prescaling. Unless period is changed very often just keep it on default and enjoy your larger range of possible periods and higher accuracy.



These two functions allow you to directly write the pwm period and pulsewidth in clock ticks. This is useful if you need to have very little overhead. It is dependent on which device you use, since they have different clock rates. You can get the current clock speed of your device with SystemCoreClock.


PwmOut uses floats for setting the time in seconds, and ints for milliseconds and microseconds. All three of those don't give enough accuracy to fully use the PWM units. Which is why FastPWM uses besides int for milliseconds and microseconds, it uses doubles for seconds and also for microseconds. Generally it is adviced to use these doubles, sometimes you might need to explicitly cast your variables to doubles.

Currently setting pulsewidth in microseconds with an int is a risk with some prescaler values (not on the 32-bit timers). See known-issues.

Adding other microcontrollers

Look at the other device files on how to add other microcontrollers. Functions that need to be implemented:

  • initFastPWM(): Any setups required can be done here. Must set the number of bits of the PWM unit.
  • pulsewidth_ticks( uint32_t ticks ): Set the pulsewidth in ticks
  • period_ticks( uint32_t ticks ): Set the period in ticks
  • getPeriod(): Return the period in ticks
  • setPrescaler(uint32_t reqScale): Set the prescaler. If reqScale is zero, return the current prescaler value only. Otherwise set the requested prescaler, if needed round up to first available prescaler, return the actually set prescaler. If the PWM unit is 32-bit (or for another reason), you can set dynamicPrescaler as false, disabling the dynamic prescaler.

Known Issues

  • Changing the prescaler manually does not adapt periods/pulsewidth
    • Manually re-set the period of each FastPWM object on that PWM unit, this should also set the duty cycle.
  • Changing the period of one FastPWM object does not keep the duty cycle of other PWM objects on that PWM unit constant, but the pulsewidth.
    • Manually re-set the duty cycle of other PWM objects.
  • PwmOut objects run at wrong speed when you use FastPWM
    • Don't use PwmOut objects.
  • On certain prescaler values setting period/pulsewidth in especially microsecond integers, also to lesser extend also millisecond integers, can result in wrong values.
    • The problem is that the number of clock ticks per microsecond/millisecond as integers are pre-calculated for improved speed. However if it isn't an integer number that gives an error.
    • Solution is to preferably use doubles (or ticks). On the 32-bit pwm units this is not an issue, so for them it doesn't matter.
    • I am planning to have a further look into it, but I expect it to stay an issue.

Here the TL;DR is: Preferably set the period/prescaler once at the beginning before setting the duty-cycle/pulsewidth. If that isn't possible, take into account duty cyles need to be set again. And preferably use doubles.


Some of the ideas are 'loaned' from Jochen Krapf's fork of the original FastPWM:

Mon Mar 17 22:12:58 2014 +0000
Changing period now keeps pulsewidth the same instead of duty cycle
; KLXX now doesn't give glitches when changing pulsewidth

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors 1:1aed61747ed6 1 /*
Sissors 1:1aed61747ed6 2 .---. _....._
Sissors 1:1aed61747ed6 3 / p `\ .-""`: :`"-.
Sissors 1:1aed61747ed6 4 |__ - | ,' . ' ',
Sissors 1:1aed61747ed6 5 ._> \ /: : ; :,
Sissors 1:1aed61747ed6 6 '-. '\`. . : ' \
Sissors 1:1aed61747ed6 7 `. | .'._.' '._.' '._.'. |
Sissors 1:1aed61747ed6 8 `;-\. : : ' '/,__,
Sissors 1:1aed61747ed6 9 .-'`'._ ' . : _.'.__.'
Sissors 1:1aed61747ed6 10 ((((-'/ `";--..:..--;"` \
Sissors 1:1aed61747ed6 11 .' / \ \
Sissors 1:1aed61747ed6 12 jgs ((((-' ((((-'
Sissors 1:1aed61747ed6 13
Sissors 1:1aed61747ed6 14 Yeah ASCII art turtle more fun than copyright stuff
Sissors 1:1aed61747ed6 15 */
Sissors 1:1aed61747ed6 16
Sissors 1:1aed61747ed6 17
Sissors 0:f8c1b0ad5371 18 #include "mbed.h"
Sissors 0:f8c1b0ad5371 19
Sissors 0:f8c1b0ad5371 20 #ifndef FASTPWM_H
Sissors 0:f8c1b0ad5371 21 #define FASTPWM_H
Sissors 0:f8c1b0ad5371 22
Sissors 0:f8c1b0ad5371 23 /** Library that allows faster and/or higher resolution PWM output
Sissors 0:f8c1b0ad5371 24 *
Sissors 6:0f57969697b6 25 * Library can directly replace standard mbed PWM library.
Sissors 0:f8c1b0ad5371 26 *
Sissors 0:f8c1b0ad5371 27 * Contrary to the default mbed library, this library takes doubles instead of floats. The compiler will autocast if needed,
Sissors 0:f8c1b0ad5371 28 * but do take into account it is done for a reason, your accuracy will otherwise be limitted by the floating point precision.
Sissors 0:f8c1b0ad5371 29 */
Sissors 4:a7b9f778c4b4 30 class FastPWM : public PwmOut {
Sissors 0:f8c1b0ad5371 31 public:
Sissors 0:f8c1b0ad5371 32 /**
Sissors 0:f8c1b0ad5371 33 * Create a FastPWM object connected to the specified pin
Sissors 0:f8c1b0ad5371 34 *
Sissors 0:f8c1b0ad5371 35 * @param pin - PWM pin to connect to
Sissors 4:a7b9f778c4b4 36 * @param prescaler - Clock prescaler, -1 is dynamic (default), 0 is bit random, everything else normal
Sissors 0:f8c1b0ad5371 37 */
Sissors 4:a7b9f778c4b4 38 FastPWM(PinName pin, int prescaler = -1);
Sissors 0:f8c1b0ad5371 39
Sissors 0:f8c1b0ad5371 40 /**
Sissors 6:0f57969697b6 41 * Set the PWM period, specified in seconds (double), keeping the pulsewidth the same.
Sissors 0:f8c1b0ad5371 42 */
Sissors 0:f8c1b0ad5371 43 void period(double seconds);
Sissors 0:f8c1b0ad5371 44
Sissors 0:f8c1b0ad5371 45 /**
Sissors 6:0f57969697b6 46 * Set the PWM period, specified in milli-seconds (int), keeping the pulsewidth the same.
Sissors 0:f8c1b0ad5371 47 */
Sissors 0:f8c1b0ad5371 48 void period_ms(int ms);
Sissors 0:f8c1b0ad5371 49
Sissors 0:f8c1b0ad5371 50 /**
Sissors 6:0f57969697b6 51 * Set the PWM period, specified in micro-seconds (int), keeping the pulsewidth the same.
Sissors 0:f8c1b0ad5371 52 */
Sissors 0:f8c1b0ad5371 53 void period_us(int us);
Sissors 0:f8c1b0ad5371 54
Sissors 0:f8c1b0ad5371 55 /**
Sissors 6:0f57969697b6 56 * Set the PWM period, specified in micro-seconds (double), keeping the pulsewidth the same.
Sissors 0:f8c1b0ad5371 57 */
Sissors 0:f8c1b0ad5371 58 void period_us(double us);
Sissors 0:f8c1b0ad5371 59
Sissors 0:f8c1b0ad5371 60 /**
Sissors 4:a7b9f778c4b4 61 * Set the PWM period, specified in clock ticks, keeping _pulse width_ the same.
Sissors 4:a7b9f778c4b4 62 *
Sissors 4:a7b9f778c4b4 63 * This function can be used if low overhead is required. Do take into account the result is
Sissors 4:a7b9f778c4b4 64 * board (clock frequency) dependent, and this does not keep an equal duty cycle!
Sissors 4:a7b9f778c4b4 65 */
Sissors 4:a7b9f778c4b4 66 void period_ticks(uint32_t ticks);
Sissors 4:a7b9f778c4b4 67
Sissors 4:a7b9f778c4b4 68 /**
Sissors 0:f8c1b0ad5371 69 * Set the PWM pulsewidth, specified in seconds (double), keeping the period the same.
Sissors 0:f8c1b0ad5371 70 */
Sissors 0:f8c1b0ad5371 71 void pulsewidth(double seconds);
Sissors 0:f8c1b0ad5371 72
Sissors 0:f8c1b0ad5371 73 /**
Sissors 0:f8c1b0ad5371 74 * Set the PWM pulsewidth, specified in milli-seconds (int), keeping the period the same.
Sissors 0:f8c1b0ad5371 75 */
Sissors 0:f8c1b0ad5371 76 void pulsewidth_ms(int ms);
Sissors 0:f8c1b0ad5371 77
Sissors 0:f8c1b0ad5371 78 /**
Sissors 0:f8c1b0ad5371 79 * Set the PWM pulsewidth, specified in micro-seconds (int), keeping the period the same.
Sissors 0:f8c1b0ad5371 80 */
Sissors 0:f8c1b0ad5371 81 void pulsewidth_us(int us);
Sissors 0:f8c1b0ad5371 82
Sissors 0:f8c1b0ad5371 83 /**
Sissors 0:f8c1b0ad5371 84 * Set the PWM pulsewidth, specified in micro-seconds (double), keeping the period the same.
Sissors 0:f8c1b0ad5371 85 */
Sissors 0:f8c1b0ad5371 86 void pulsewidth_us(double us);
Sissors 0:f8c1b0ad5371 87
Sissors 0:f8c1b0ad5371 88 /**
Sissors 4:a7b9f778c4b4 89 * Set the PWM period, specified in clock ticks, keeping the period the same.
Sissors 4:a7b9f778c4b4 90 *
Sissors 4:a7b9f778c4b4 91 * This function can be used if low overhead is required. Do take into account the result is
Sissors 4:a7b9f778c4b4 92 * board (clock frequency) dependent!
Sissors 4:a7b9f778c4b4 93 */
Sissors 4:a7b9f778c4b4 94 void pulsewidth_ticks(uint32_t ticks);
Sissors 4:a7b9f778c4b4 95
Sissors 4:a7b9f778c4b4 96 /**
Sissors 0:f8c1b0ad5371 97 * Set the ouput duty-cycle, specified as a percentage (double)
Sissors 0:f8c1b0ad5371 98 *
Sissors 0:f8c1b0ad5371 99 * @param duty - A double value representing the output duty-cycle, specified as a percentage. The value should lie between 0.0 (representing on 0%) and 1.0 (representing on 100%).
Sissors 0:f8c1b0ad5371 100 */
Sissors 0:f8c1b0ad5371 101 void write(double duty);
Sissors 0:f8c1b0ad5371 102
Sissors 0:f8c1b0ad5371 103 /**
Sissors 0:f8c1b0ad5371 104 * Return the ouput duty-cycle, specified as a percentage (double)
Sissors 0:f8c1b0ad5371 105 *
Sissors 0:f8c1b0ad5371 106 * @param return - A double value representing the output duty-cycle, specified as a percentage.
Sissors 0:f8c1b0ad5371 107 */
Sissors 0:f8c1b0ad5371 108 double read( void );
Sissors 0:f8c1b0ad5371 109
Sissors 0:f8c1b0ad5371 110 /**
Sissors 0:f8c1b0ad5371 111 * An operator shorthand for write()
Sissors 0:f8c1b0ad5371 112 */
Sissors 0:f8c1b0ad5371 113 FastPWM& operator= (double value);
Sissors 0:f8c1b0ad5371 114
Sissors 0:f8c1b0ad5371 115 /**
Sissors 0:f8c1b0ad5371 116 * An operator shorthand for read()
Sissors 0:f8c1b0ad5371 117 */
Sissors 0:f8c1b0ad5371 118 operator double();
Sissors 0:f8c1b0ad5371 119
Sissors 4:a7b9f778c4b4 120 /**
Sissors 4:a7b9f778c4b4 121 * Set the PWM prescaler
Sissors 4:a7b9f778c4b4 122 *
Sissors 4:a7b9f778c4b4 123 * The period of all PWM pins on the same PWM unit have to be reset after using this!
Sissors 4:a7b9f778c4b4 124 *
Sissors 4:a7b9f778c4b4 125 * @param value - The required prescaler. Special values: 0 = lock current prescaler, -1 = use dynamic prescaler
Sissors 4:a7b9f778c4b4 126 * @param return - The prescaler which was set (can differ from requested prescaler if not possible)
Sissors 4:a7b9f778c4b4 127 */
Sissors 4:a7b9f778c4b4 128 int prescaler(int value);
Sissors 4:a7b9f778c4b4 129
Sissors 0:f8c1b0ad5371 130 private:
Sissors 4:a7b9f778c4b4 131 void initFastPWM(void);
Sissors 4:a7b9f778c4b4 132
Sissors 4:a7b9f778c4b4 133 uint32_t setPrescaler( uint32_t reqScale );
Sissors 4:a7b9f778c4b4 134 int calcPrescaler(uint64_t clocks);
Sissors 4:a7b9f778c4b4 135 uint32_t getPeriod( void );
Sissors 4:a7b9f778c4b4 136
Sissors 4:a7b9f778c4b4 137 void updateTicks( uint32_t prescaler );
Sissors 4:a7b9f778c4b4 138 uint32_t bits;
Sissors 4:a7b9f778c4b4 139
Sissors 0:f8c1b0ad5371 140 double _duty;
Sissors 0:f8c1b0ad5371 141
Sissors 4:a7b9f778c4b4 142 double dticks, dticks_us;
Sissors 4:a7b9f778c4b4 143 int iticks_ms, iticks_us;
Sissors 4:a7b9f778c4b4 144
Sissors 4:a7b9f778c4b4 145 bool dynamicPrescaler;
Sissors 4:a7b9f778c4b4 146
Sissors 0:f8c1b0ad5371 147
Sissors 0:f8c1b0ad5371 148 };
Sissors 0:f8c1b0ad5371 149 #endif