This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

PWM Duty Cycle update best practices

Because of the limitation in the GPIOTE hardware that only one channel can be connected per GPIO, care must be taken when changing a PWM duty cycle. If for instance the compare events that each trigger a GPIOTE toggle happen in the same clock cycle, the task is only triggered once and suddenly the output duty cycle is inverted.

My question is: What is the best practice for changing a PWM value?

Stopping the timer, updating compare matches, and restarting it kinda works. Unfortunately it "pauses" the output waveform during the update. If the update is not done in an IRQ handler, there is a good chance an interrupt will inflate the "pause" which could have disastrous effect on certain hardware. This method has been implemented in the Simple PWM library that has been floating around here.

The other solution I've found is to use an unsued CC register to trigger an interrupt at exactly the right time in the overall timer period. With careful control of the IRQ Handler, and choice of the extra CC, the update of the CC which's event is mapped to the GPIOTE can happen at a predictable time in the timer cycle and thus you should be able to ensure glitch free output.

I have had both systems mostly working, with the major exception that I've still noticed some glitches or unintended output inversions, which has blown up some of the other components on my board. This is surely because I haven't quite fully taken care of the edge cases.

So, to ask a different way, does anyone else out there have any other ways of doing this?

  • No HardFault for me, I could get a PWM waveform under certain conditions (never set value to 0 or max, and it worked only when increasing duty cycle).

    Too bad there's no working example in the repo nor author contact...

  • I wrote the original driver, but not the noglitch one. It seems there are some issues in the noglitch version that remain to be solved.

    I am considering rewriting the entire thing so that we only need a single driver that solves both problems. Unless anyone else have any good ideas I believe I need to ensure that the PWM interrupt can run without danger of higher priority interrupts superseding it, otherwise I can't think of a way to take care of all possible race conditions.

    I believe this solution will require the PWM driver to use interrupt priority 0 when the SoftDevice is not present, and it will have to use the timeslot API in S110 v7 or later when the SoftDevice is running, to make sure that the PWM's can be updates safely without incurring glitches in the output.

    I can't make any promises on the timing yet, a new driver will be released when you least expect it :P

  • Thanks for taking the effort to solve this! I cannot oversee all issues/race conditions, but a first sight using the timeslot API seems to be a bit overkill... For my application it would not be an issue if the changing of the PWM is delayed for a very short time by some high prio interrupts, the main issue now is that the PWM timer is stopped and restarted. In between, there is no PWM output on the desired pin (for an amount of time influenced by high prio interrupts) causing the flickering. The noglitch version currently in the repository seems to make a nice step towards keeping the PWM timer running and updating compare registers. Wouldn't it be possible to further build on this approach and solve the currently existing errors??

  • Anyone have example code with softdevice or without softdevice? please post one, thanks :)

Related