Teensy 4.0 analogWriteFrequency rounding errors

kirby

New member
Teensy beginner here. I've been doing PWM with a Teensy 4.0 using the analogWriteFrequency() function and noticed that there are significant rounding errors when using frequencies that are not perfect divisors of the CPU clock speed. For example, at a 528 MHz clock speed on a Teensy 4.0 the following sketch sets a frequency of 40018 Hz on pin 2. However, I measure a frequency of 40012.1 Hz on pin 2 with a TDS 1002 oscilloscope.

When I set the frequency to perfect divisors of the 528 MHz clock speed (such as 52800, 20000, or 44000 Hz), I observe no difference between the measured and set frequency. Setting the frequency to any non-divisor results in a difference between measured and set frequency varying between around 0.5-5.0 Hz. The bug persists when I change PWM resolution.

I think this is related to a bug found in Teensy 3.6, see https://forum.pjrc.com/threads/23448-analogWriteFrequency-and-analogWrite-rounding

Any ways to work around or fix this bug? Am I making some mistake?

Code:
// minimal example of frequency rounding errors in analogWriteFrequency(), for Teensy 4.0
// setting a frequency of 40018 Hz yields measured frequency of 40012.1 Hz at a CPU clock speed of 528 MHz
#define FREQUENCY 40018. 
#define PIN 2

#define PWMRES 8        // PWM resolution 8 bits = 256 steps
#define PWMSTEPS 256    // to match PWMRES: there are 256 steps

// initialize a 50% duty cycle
const int amp = PWMSTEPS/2 - 1;

// set the frequency and amplitude 
void setup(void) {
  analogWriteRes(PWMRES); // write PWM resolution
  analogWriteFrequency(PIN, FREQUENCY);      // set the signal frequency
  analogWrite(PIN, amp); // set PWM amplitude to 50% duty cycle
}

void loop(void) {
}
 
The frequency and high time of any PWM signal is based on a clock frequency. In the case of T4.0, the clock frequency of the PWM module is F_CPU/4, so for your F_CPU=528MHz, the PWM clock frequency is 528/4 = 132 MHz. When you call analogWriteFrequency() with a desired frequency, the code in that function computes the number of clock cycles that will get as close as possible to the desired frequency. For your example of 40018 Hz, here is what happens:

- clock frequency / PWM frequency = 132 MHz / 40018 Hz = 3298.51 clock cycles
- code rounds 3298.51 up to 3299, since the PWM period must be in whole clock cycles
- for a period of 3299 clocks, the actual PWM frequency is 132 MHz / 3299 = 40012.1 Hz

The bottom line is that the only PWM frequencies that can produced exactly are those that divide evenly into the clock frequency. Because rounding is used, the actual frequency can be higher or lower than requested.
 
Back
Top