Boron PWM Pulse Width Limited to 1 us

It appears that there is a 1 microsecond limitation on the minimum achievable period of the analogWrite(), regardless of frequency parameter.

SYSTEM_MODE(SEMI_AUTOMATIC); 

void setup() {
    pinMode(D2, OUTPUT);
}

void loop() {
    // Setting 1: 5 KHz
    analogWrite(D2, 1, 5000);
    delay(5000);
    // Setting 2: 10 KHz
    analogWrite(D2, 1, 10000);
    delay(5000);
    // Setting 3: 20 KHz
    analogWrite(D2, 1, 20000);
    delay(5000);
    // Setting 3: 50 KHz
    analogWrite(D2, 1, 50000);
    delay(5000);
}

Measured results
Using an oscilloscope to capture the frequency and period for each of the 4 settings, these are my results.
Freq = 5 KHz, Measured pulse width = 1 us, Expected pulse width = (1/256)/5000 = 0.78 usec
Freq = 10 KHz, Measured pulse width = 1 us, Expected pulse width = (1/256)/10000 = 0.39 usec
Freq = 20 KHz, Measured pulse width = 1 us, Expected pulse width = (1/256)/20000 = 0.195 usec
Freq = 50 KHz, Measured pulse width = 1 us, Expected pulse width = (1/256)/50000 = 0.078 usec

The actual pulse duty resolution at high frequencies is severely limited. Considering the maximum frequency of the Boron device’s PWM is listed in the docs as 500 KHz, you’d only get 2 steps of resolution with a minimum 1 usec pulse width!

Setting the analogWriteResolution() does not seem to change anything. And it is not a problem with my scope’s time resolution, they are the same pulse widths even when you zoom in.

My guess is that the Device OS code for this sets the timer clock frequency to a fixed value of 1 MHz, and scales the other parameters.

Scope captures
20 KHz


50 KHz

The maximum frequency available for a Boron device in the PWM is 500 kHz. You can consult this information on:

https://docs.particle.io/reference/device-os/firmware/boron/#analogwrite-pwm-

You can setup the PWM resolution with:
“analogWriteResolution()”

and revied the maximum available frequency for a pin with the function:
“analogWriteMaxFrequency()”

Unfortunately, there is no dependence on analogWriteResolution().

Take this code for example:

SerialLogHandler logHandler;
SYSTEM_MODE(SEMI_AUTOMATIC); 

void setup() {
    pinMode(D2, OUTPUT);
    delay(2000);
}

void loop() {
    for(unsigned int res = 2; res <= 31; ++res) {   
        analogWriteResolution(D2, res);
        analogWrite(D2, 1, analogWriteMaxFrequency(D2));
        Log.info("req res = %d\tactual res = %d\tfreq = %lu", 
            res,  analogWriteResolution(D2), analogWriteMaxFrequency(D2));
        delay(2000);
    }
}

Here is the Log output:

0000002308 [app] INFO: req res = 2      actual res = 2  freq = 500000
0000004309 [app] INFO: req res = 3      actual res = 3  freq = 500000
0000006309 [app] INFO: req res = 4      actual res = 4  freq = 500000
0000008310 [app] INFO: req res = 5      actual res = 5  freq = 500000
0000010310 [app] INFO: req res = 6      actual res = 6  freq = 500000
0000012311 [app] INFO: req res = 7      actual res = 7  freq = 500000
0000014311 [app] INFO: req res = 8      actual res = 8  freq = 500000
0000016312 [app] INFO: req res = 9      actual res = 9  freq = 500000
0000018312 [app] INFO: req res = 10     actual res = 10 freq = 500000
0000020313 [app] INFO: req res = 11     actual res = 11 freq = 500000
0000022313 [app] INFO: req res = 12     actual res = 12 freq = 500000
0000024314 [app] INFO: req res = 13     actual res = 13 freq = 500000
0000026315 [app] INFO: req res = 14     actual res = 14 freq = 500000
0000028315 [app] INFO: req res = 15     actual res = 15 freq = 500000
0000030316 [app] INFO: req res = 16     actual res = 15 freq = 500000
0000032316 [app] INFO: req res = 17     actual res = 15 freq = 500000
0000034317 [app] INFO: req res = 18     actual res = 15 freq = 500000
0000036317 [app] INFO: req res = 19     actual res = 15 freq = 500000
0000038318 [app] INFO: req res = 20     actual res = 15 freq = 500000
0000040318 [app] INFO: req res = 21     actual res = 15 freq = 500000
0000042319 [app] INFO: req res = 22     actual res = 15 freq = 500000
0000044319 [app] INFO: req res = 23     actual res = 15 freq = 500000
0000046320 [app] INFO: req res = 24     actual res = 15 freq = 500000
0000048321 [app] INFO: req res = 25     actual res = 15 freq = 500000
0000050321 [app] INFO: req res = 26     actual res = 15 freq = 500000
0000052322 [app] INFO: req res = 27     actual res = 15 freq = 500000
0000054322 [app] INFO: req res = 28     actual res = 15 freq = 500000
0000056323 [app] INFO: req res = 29     actual res = 15 freq = 500000
0000058323 [app] INFO: req res = 30     actual res = 15 freq = 500000
0000060324 [app] INFO: req res = 31     actual res = 15 freq = 500000

This code tries every possible resolution setting at the maximum frequency. This had no effect whatsoever on the pin’s output, which stayed always at 50% duty at 500 KHz for the full duration of the program.

I’m quite confident a pulse width of less than 1 usec was never achieved during this program. I set my scope to trigger on pulse widths less than 1 usec. The scope never triggered.

Shouldn’t increasing the resolution allow smaller pulse widths? The documentation isn’t clear exactly what resolution means practically.