Correct PWM only output for half the range

Just wanted to share something I came across today. Looking at the docs for AnalogWrite, you would think that any call to analogWrite ( https://docs.particle.io/reference/firmware/core/#analogwrite-pwm- ) would set the desired PWM output, right? Turns out that if you change from the default PWM frequency of 500 Hz, you have to do something undocumented to make it work.

I’m controlling a gas valve using PWM at 1450Hz, but this will likely be the same for all kinds of process control equipment. The valve just wouldn’t open despite me iterating through the entire 0-255 range for analogWrite. I hooked the output up to an Oscilloscope and sure enough - any attempts to go above 127 returned a PWM signal that looked like it was 127 (50% on/off). For some reason, the Photon did not obey my commands and refused to set the duty cycle higher.

I asked a friend for advice and he said something about checking the analog resolution. Why would I want to do that I said, but he insisted that I try it. I just added this line of code and printed the result to serial:

int result = 2^(analogWriteResolution(pin)) - 1;

Adding this single line of code made the entire PWM range work as it should. My friend said that this had cost him weeks of debug time, but he had found that changing PWM frequency requires recalculating the PWM internally and on STM32 that didn’t happen automatically. Just a single call like that and it’s recalculated and works. I’m sooooo glad he dropped by, or I would have wasted a massive amount of hours on this as well.

Maybe it could be worth adding something about this to the Docs?

3 Likes

This might definetly be worth opening a GitHub issue.
I think this should be taken care of officially.

The doc say this:

analogWrite() takes two or three arguments:

  • pin: the number of the pin whose value you wish to set
  • value: the duty cycle: between 0 (always off) and 255 (always on). Since 0.6.0: between 0 and 255 (default 8-bit resolution) or 2^(analogWriteResolution(pin)) - 1 in general.
  • frequency: the PWM frequency: between 1 Hz and 65535 Hz (default 500 Hz). Since 0.6.0: between 1 Hz and analogWriteMaxFrequency(pin).

The “max out at 127” part makes me think there could be a type issue like using char or int8_t for the PWM value. Could that have been a problem?