nox771

03-28-2013, 10:36 PM

I've been working with the PWM functions on a Teensy3. I noticed that the analogWriteFrequency() and analogWrite() functions truncate rather than round fractional periods. This becomes more noticeable at high PWM frequencies.

For analogWriteFrequency(), since the period is truncated (ie. rounded down), the frequency always gets rounded up to the next quantized value. For instance if you do:

analogWriteFrequency(20,1600000);

then you get 1.6MHz PWM since 1.6MHz is an even divisor into the 48MHz bus clock.

But if you do this:

analogWriteFrequency(20,1600001);

then you get 1.655172MHz since the period (29.99998125 bus clocks) got truncated to 29 bus clocks.

A patch for this is to change the mod calculation in analogWriteFrequency() from:

mod = ((F_BUS >> prescale) / frequency) - 1;

to

mod = (((F_BUS >> prescale)*10/frequency)+5)/10 - 1; // integer round

which rounds up fractional clocks greater than or equal to 1/2 bus clocks. This effectively picks the closest quantized PWM freq given a specified freq.

Likewise for analogWrite() the duty cycle is truncated (rounded down regardless of fraction):

if (pin == 3 || pin == 4) {

cval = ((uint32_t)val * (uint32_t)(FTM1_MOD + 1)) >> analog_write_res;

} else {

cval = ((uint32_t)val * (uint32_t)(FTM0_MOD + 1)) >> analog_write_res;

}

Similar to above this can be fixed by adding 1/2 LSB prior to truncating (a bit easier here since the divisor is always a power of two, and the 'max' value is already defined in the preceding code):

if (pin == 3 || pin == 4) {

cval = ((uint32_t)val * (uint32_t)(FTM1_MOD + 1) + (max >> 1)) >> analog_write_res;

} else {

cval = ((uint32_t)val * (uint32_t)(FTM0_MOD + 1) + (max >> 1)) >> analog_write_res;

}

Is there any way Teensyduino can be patched like this? Currently I redefine the functions for my stuff, but it's a little bothersome since I have to keep a separate copy of analog_write_res (since it is a static global in pins_teensy.c I can't access it from my code).

For analogWriteFrequency(), since the period is truncated (ie. rounded down), the frequency always gets rounded up to the next quantized value. For instance if you do:

analogWriteFrequency(20,1600000);

then you get 1.6MHz PWM since 1.6MHz is an even divisor into the 48MHz bus clock.

But if you do this:

analogWriteFrequency(20,1600001);

then you get 1.655172MHz since the period (29.99998125 bus clocks) got truncated to 29 bus clocks.

A patch for this is to change the mod calculation in analogWriteFrequency() from:

mod = ((F_BUS >> prescale) / frequency) - 1;

to

mod = (((F_BUS >> prescale)*10/frequency)+5)/10 - 1; // integer round

which rounds up fractional clocks greater than or equal to 1/2 bus clocks. This effectively picks the closest quantized PWM freq given a specified freq.

Likewise for analogWrite() the duty cycle is truncated (rounded down regardless of fraction):

if (pin == 3 || pin == 4) {

cval = ((uint32_t)val * (uint32_t)(FTM1_MOD + 1)) >> analog_write_res;

} else {

cval = ((uint32_t)val * (uint32_t)(FTM0_MOD + 1)) >> analog_write_res;

}

Similar to above this can be fixed by adding 1/2 LSB prior to truncating (a bit easier here since the divisor is always a power of two, and the 'max' value is already defined in the preceding code):

if (pin == 3 || pin == 4) {

cval = ((uint32_t)val * (uint32_t)(FTM1_MOD + 1) + (max >> 1)) >> analog_write_res;

} else {

cval = ((uint32_t)val * (uint32_t)(FTM0_MOD + 1) + (max >> 1)) >> analog_write_res;

}

Is there any way Teensyduino can be patched like this? Currently I redefine the functions for my stuff, but it's a little bothersome since I have to keep a separate copy of analog_write_res (since it is a static global in pins_teensy.c I can't access it from my code).