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.

Usage

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

Prescaler

Warning

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

fastpwm.prescaler(value);

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.

Ticks

fastpwm.period_ticks(ticks);
fastpwm.pulsewidth_ticks(ticks);

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.

Double

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.

Credits

Some of the ideas are 'loaned' from Jochen Krapf's fork of the original FastPWM: http://mbed.org/users/jocis/code/HighPWM/

Committer:
Sissors
Date:
Sun Jan 31 08:29:35 2016 +0000
Revision:
26:0c924507a81f
Parent:
25:8b1bf34c72aa
Child:
27:7f484dd7323d
Added two more NUCLEO Targets

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors 13:cdefd9d75b64 1 #include "mbed.h"
Sissors 13:cdefd9d75b64 2
alpsayin 25:8b1bf34c72aa 3 #ifdef TARGET_NUCLEO_F303RE
alpsayin 25:8b1bf34c72aa 4 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
alpsayin 25:8b1bf34c72aa 5 switch (pin) {
alpsayin 25:8b1bf34c72aa 6 // Channels 1
alpsayin 25:8b1bf34c72aa 7 case PC_0: case PB_8: case PB_9: case PA_6: case PA_8: case PB_4: case PB_5: case PA_2: case PC_6: case PA_12: case PB_14: case PB_15:
alpsayin 25:8b1bf34c72aa 8 // Channels 1N
alpsayin 25:8b1bf34c72aa 9 case PA_1: case PA_5: case PB_6: case PB_3: case PA_13: case PB_7: case PC_13:
alpsayin 25:8b1bf34c72aa 10 return &pwm->CCR1;
alpsayin 25:8b1bf34c72aa 11
alpsayin 25:8b1bf34c72aa 12 // Channels 2
alpsayin 25:8b1bf34c72aa 13 case PC_1: case PA_7: case PC_7: case PA_9: case PA_3: case PA_14:
alpsayin 25:8b1bf34c72aa 14 // Channels 2N
alpsayin 25:8b1bf34c72aa 15 case PB_0:
alpsayin 25:8b1bf34c72aa 16 return &pwm->CCR2;
alpsayin 25:8b1bf34c72aa 17
alpsayin 25:8b1bf34c72aa 18 // Channels 3
alpsayin 25:8b1bf34c72aa 19 case PA_10: case PC_2: case PC_8:
alpsayin 25:8b1bf34c72aa 20 // Channels 3N
alpsayin 25:8b1bf34c72aa 21 case PB_1:
alpsayin 25:8b1bf34c72aa 22 return &pwm->CCR3;
alpsayin 25:8b1bf34c72aa 23
alpsayin 25:8b1bf34c72aa 24 // Channels 4
alpsayin 25:8b1bf34c72aa 25 case PC_3: case PC_9: case PA_11:
alpsayin 25:8b1bf34c72aa 26 // Channels 4N
alpsayin 25:8b1bf34c72aa 27
alpsayin 25:8b1bf34c72aa 28 return &pwm->CCR4;
alpsayin 25:8b1bf34c72aa 29 }
alpsayin 25:8b1bf34c72aa 30 return NULL;
alpsayin 25:8b1bf34c72aa 31 }
alpsayin 25:8b1bf34c72aa 32 #endif
alpsayin 25:8b1bf34c72aa 33
Sissors 13:cdefd9d75b64 34 #ifdef TARGET_NUCLEO_F030R8
Sissors 13:cdefd9d75b64 35 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
Sissors 13:cdefd9d75b64 36 switch (pin) {
Sissors 13:cdefd9d75b64 37 // Channels 1
Sissors 13:cdefd9d75b64 38 case PA_4: case PA_6: case PB_1: case PB_4: case PB_8: case PB_9: case PB_14: case PC_6: case PB_6: case PB_7:
Sissors 13:cdefd9d75b64 39 return &pwm->CCR1;
Sissors 13:cdefd9d75b64 40
Sissors 13:cdefd9d75b64 41 // Channels 2
Sissors 13:cdefd9d75b64 42 case PA_7: case PB_5: case PC_7:
Sissors 13:cdefd9d75b64 43 return &pwm->CCR2;
Sissors 13:cdefd9d75b64 44
Sissors 13:cdefd9d75b64 45 // Channels 3
Sissors 13:cdefd9d75b64 46 case PB_0: case PC_8:
Sissors 13:cdefd9d75b64 47 return &pwm->CCR3;
Sissors 13:cdefd9d75b64 48
Sissors 13:cdefd9d75b64 49 // Channels 4
Sissors 13:cdefd9d75b64 50 case PC_9:
Sissors 13:cdefd9d75b64 51 return &pwm->CCR4;
Sissors 13:cdefd9d75b64 52 }
Sissors 13:cdefd9d75b64 53 return NULL;
Sissors 13:cdefd9d75b64 54 }
Sissors 13:cdefd9d75b64 55 #endif
Sissors 13:cdefd9d75b64 56
jocis 15:49a7eff133b3 57 #if defined TARGET_NUCLEO_F401RE || defined TARGET_NUCLEO_F411RE
Sissors 13:cdefd9d75b64 58 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
Sissors 13:cdefd9d75b64 59 switch (pin) {
jocis 16:ec208b5ec0bb 60 // Channels 1 : PWMx/1
Sissors 13:cdefd9d75b64 61 case PA_0: case PA_5: case PA_6: case PA_8: case PA_15: case PB_4: case PB_6: case PC_6: case PA_7: case PB_13:
Sissors 13:cdefd9d75b64 62 return &pwm->CCR1;
Sissors 13:cdefd9d75b64 63
jocis 16:ec208b5ec0bb 64 // Channels 2 : PWMx/2
Sissors 13:cdefd9d75b64 65 case PA_1: case PA_9: case PB_3: case PB_5: case PB_7: case PC_7: case PB_0: case PB_14:
Sissors 13:cdefd9d75b64 66 return &pwm->CCR2;
Sissors 13:cdefd9d75b64 67
jocis 16:ec208b5ec0bb 68 // Channels 3 : PWMx/3
Sissors 13:cdefd9d75b64 69 case PA_2: case PA_10: case PB_8: case PB_10: case PC_8: case PB_1: case PB_15:
Sissors 13:cdefd9d75b64 70 return &pwm->CCR3;
Sissors 13:cdefd9d75b64 71
jocis 16:ec208b5ec0bb 72 // Channels 4 : PWMx/4
Sissors 13:cdefd9d75b64 73 case PA_3: case PA_11: case PB_9: case PC_9:
Sissors 13:cdefd9d75b64 74 return &pwm->CCR4;
Sissors 13:cdefd9d75b64 75 }
Sissors 13:cdefd9d75b64 76 return NULL;
Sissors 13:cdefd9d75b64 77 }
jocis 16:ec208b5ec0bb 78 #endif
jocis 16:ec208b5ec0bb 79
jocis 16:ec208b5ec0bb 80 #if defined TARGET_NUCLEO_F103RB
Sissors 24:1f451660d8c0 81 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
jocis 16:ec208b5ec0bb 82 switch (pin) {
jocis 16:ec208b5ec0bb 83 // Channels 1 : PWMx/1
jocis 16:ec208b5ec0bb 84 case PA_6: case PA_8: case PA_15: case PB_4: case PC_6: case PB_13:
jocis 17:8378bc456f0d 85 return &pwm->CCR1;
jocis 16:ec208b5ec0bb 86
jocis 16:ec208b5ec0bb 87 // Channels 2 : PWMx/2
jocis 16:ec208b5ec0bb 88 case PA_1: case PA_7: case PA_9: case PB_3: case PB_5: case PC_7: case PB_14:
jocis 17:8378bc456f0d 89 return &pwm->CCR2;
jocis 16:ec208b5ec0bb 90
jocis 16:ec208b5ec0bb 91 // Channels 3 : PWMx/3
jocis 16:ec208b5ec0bb 92 case PA_2: case PA_10: case PB_0: case PB_10: case PC_8: case PB_15:
jocis 17:8378bc456f0d 93 return &pwm->CCR3;
jocis 16:ec208b5ec0bb 94
jocis 16:ec208b5ec0bb 95 // Channels 4 : PWMx/4
jocis 16:ec208b5ec0bb 96 case PA_3: case PA_11: case PB_1: case PB_11: case PC_9:
jocis 17:8378bc456f0d 97 return &pwm->CCR4;
jocis 16:ec208b5ec0bb 98 }
jocis 16:ec208b5ec0bb 99 return NULL;
jocis 16:ec208b5ec0bb 100 }
Sissors 19:ba7a5bf634b3 101 #endif
Sissors 19:ba7a5bf634b3 102
Sissors 19:ba7a5bf634b3 103 #ifdef TARGET_NUCLEO_F334R8
Sissors 19:ba7a5bf634b3 104 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
Sissors 19:ba7a5bf634b3 105 switch (pin) {
Sissors 19:ba7a5bf634b3 106 // Channels 1
Sissors 19:ba7a5bf634b3 107 case PA_2: case PA_6: case PA_7: case PA_8: case PA_12: case PB_4: case PB_5: case PB_8: case PB_9: case PB_14: case PC_0: case PC_6:
Sissors 19:ba7a5bf634b3 108 case PA_1: case PA_13: case PB_6: case PB_13: case PC_13:
Sissors 19:ba7a5bf634b3 109 return &pwm->CCR1;
Sissors 19:ba7a5bf634b3 110
Sissors 19:ba7a5bf634b3 111 // Channels 2
Sissors 19:ba7a5bf634b3 112 case PA_3: case PA_4: case PA_9: case PB_15: case PC_1: case PC_7:
Sissors 19:ba7a5bf634b3 113 return &pwm->CCR2;
Sissors 19:ba7a5bf634b3 114
Sissors 19:ba7a5bf634b3 115 // Channels 3
Sissors 19:ba7a5bf634b3 116 case PA_10: case PB_0: case PC_2: case PC_8:
Sissors 19:ba7a5bf634b3 117 case PF_0:
Sissors 19:ba7a5bf634b3 118 return &pwm->CCR3;
Sissors 19:ba7a5bf634b3 119
Sissors 19:ba7a5bf634b3 120 // Channels 4
Sissors 19:ba7a5bf634b3 121 case PA_11: case PB_1: case PB_7: case PC_3: case PC_9:
Sissors 19:ba7a5bf634b3 122 return &pwm->CCR4;
Sissors 19:ba7a5bf634b3 123 }
Sissors 19:ba7a5bf634b3 124 return NULL;
Sissors 19:ba7a5bf634b3 125 }
altaran 20:3c609bc4ae9c 126 #endif
altaran 20:3c609bc4ae9c 127
altaran 20:3c609bc4ae9c 128 #if defined TARGET_NUCLEO_F072RB
altaran 20:3c609bc4ae9c 129 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
altaran 20:3c609bc4ae9c 130 switch (pin) {
altaran 20:3c609bc4ae9c 131 // Channels 1 : PWMx/1
altaran 20:3c609bc4ae9c 132 case PA_2: case PA_6: case PA_4: case PA_7: case PA_8: case PB_1: case PB_4: case PB_8: case PB_9: case PB_14: case PC_6:
altaran 21:aa2884be5496 133 // Channels 1N : PWMx/1N
altaran 21:aa2884be5496 134 case PA_1: case PB_6: case PB_7: case PB_13:
altaran 20:3c609bc4ae9c 135 return &pwm->CCR1;
altaran 20:3c609bc4ae9c 136
altaran 20:3c609bc4ae9c 137 // Channels 2 : PWMx/2
altaran 20:3c609bc4ae9c 138 case PA_3: case PA_9: case PB_5: case PC_7: case PB_15:
altaran 20:3c609bc4ae9c 139 return &pwm->CCR2;
altaran 20:3c609bc4ae9c 140
altaran 20:3c609bc4ae9c 141 // Channels 3 : PWMx/3
altaran 20:3c609bc4ae9c 142 case PA_10: case PB_0: case PC_8:
altaran 20:3c609bc4ae9c 143 return &pwm->CCR3;
altaran 20:3c609bc4ae9c 144
altaran 20:3c609bc4ae9c 145 // Channels 4 : PWMx/4
altaran 20:3c609bc4ae9c 146 case PA_11: case PC_9:
altaran 20:3c609bc4ae9c 147 return &pwm->CCR4;
altaran 20:3c609bc4ae9c 148 }
altaran 20:3c609bc4ae9c 149 return NULL;
altaran 20:3c609bc4ae9c 150 }
Sissors 26:0c924507a81f 151 #endif
Sissors 26:0c924507a81f 152
Sissors 26:0c924507a81f 153 #ifdef TARGET_NUCLEO_F303K8
Sissors 26:0c924507a81f 154 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
Sissors 26:0c924507a81f 155 switch (pin) {
Sissors 26:0c924507a81f 156 // Channels 1
Sissors 26:0c924507a81f 157 case PA_12: case PA_8: case PB_5: case PB_4: case PA_2: case PA_7: case PA_6:
Sissors 26:0c924507a81f 158 // Channels 1N
Sissors 26:0c924507a81f 159 case PB_7: case PB_6:
Sissors 26:0c924507a81f 160 return &pwm->CCR1;
Sissors 26:0c924507a81f 161
Sissors 26:0c924507a81f 162 // Channels 2
Sissors 26:0c924507a81f 163 case PA_9: case PA_4: case PA_3:
Sissors 26:0c924507a81f 164 // Channels 2N
Sissors 26:0c924507a81f 165 case PB_0:
Sissors 26:0c924507a81f 166 return &pwm->CCR2;
Sissors 26:0c924507a81f 167
Sissors 26:0c924507a81f 168 // Channels 3
Sissors 26:0c924507a81f 169 case PA_10:
Sissors 26:0c924507a81f 170 // Channels 3N
Sissors 26:0c924507a81f 171 case PB_1: case PF_0:
Sissors 26:0c924507a81f 172 return &pwm->CCR3;
Sissors 26:0c924507a81f 173
Sissors 26:0c924507a81f 174 // Channels 4
Sissors 26:0c924507a81f 175 case PA_11:
Sissors 26:0c924507a81f 176 // Channels 4N
Sissors 26:0c924507a81f 177
Sissors 26:0c924507a81f 178 return &pwm->CCR4;
Sissors 26:0c924507a81f 179 }
Sissors 26:0c924507a81f 180 return NULL;
Sissors 26:0c924507a81f 181 }
Sissors 26:0c924507a81f 182 #endif
Sissors 26:0c924507a81f 183
Sissors 26:0c924507a81f 184
Sissors 26:0c924507a81f 185 #ifdef TARGET_NUCLEO_F446RE
Sissors 26:0c924507a81f 186 __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin) {
Sissors 26:0c924507a81f 187 switch (pin) {
Sissors 26:0c924507a81f 188 // Channels 1 : PWMx/1
Sissors 26:0c924507a81f 189 case PA_0: case PA_5: case PA_6: case PB_6: case PA_8: case PB_4:
Sissors 26:0c924507a81f 190 // Channels 1N
Sissors 26:0c924507a81f 191 case PA_7:
Sissors 26:0c924507a81f 192 return &pwm->CCR1;
Sissors 26:0c924507a81f 193
Sissors 26:0c924507a81f 194 // Channels 2 : PWMx/2
Sissors 26:0c924507a81f 195 case PA_1: case PC_7: case PA_9: case PB_5: case PB_3:
Sissors 26:0c924507a81f 196 // Channels 2N
Sissors 26:0c924507a81f 197 case PB_0:
Sissors 26:0c924507a81f 198 return &pwm->CCR2;
Sissors 26:0c924507a81f 199
Sissors 26:0c924507a81f 200 // Channels 3 : PWMx/3
Sissors 26:0c924507a81f 201 case PB_8: case PB_10: case PA_10: case PA_2:
Sissors 26:0c924507a81f 202 return &pwm->CCR3;
Sissors 26:0c924507a81f 203
Sissors 26:0c924507a81f 204 // Channels 4 : PWMx/4
Sissors 26:0c924507a81f 205 case PB_9: case PA_3:
Sissors 26:0c924507a81f 206 return &pwm->CCR4;
Sissors 26:0c924507a81f 207 }
Sissors 26:0c924507a81f 208 return NULL;
Sissors 26:0c924507a81f 209 }
Sissors 13:cdefd9d75b64 210 #endif