Thanks, tested this out and it works just fine. Interestingly, with a given PWM setting the duty cycle as measured by my scope can change when the frequency changes. For example, with a PWM setting of, say, 25 the duty cycle might be 9.78% or 10.00% with the only change being the frequency. So, I wonder if there some kind of binning going on. I tired a freq of 10Hz but my bench power supply was bouncing all over the place so I upped the frequency to 75Hz and the readings from the power supply are now more stable and the duty cycle values are in line with calculated values.
Brian
Studying the FTM chapter of the processor's reference manual would have explained everything to you. The FTM timers have a mod register which is set to a number x of bus clock pulses which determines the PWM frequency by F_BUS/x and which indicates the number of clock cycles for the whole thing, high and low period (t_hi + t_lo) in sum.
The channel CV register for the pin you use is set to a value y which determines the the active duty cycle (t_hi only). Thus, when you use analogWrite(z), the y value is calculated from z depending on resolution and the mod value (x). If you change the PWM frequency and thus x afterwards, it is normal that an unchanged y (t_hi) will lead to a different duty cycle, that's elementary logic, because for an unchanged y, the high pulse width remains unchanged which makes that the duty cycle changes.
Thus, changing the frequency with analogWriteFrequency() should always be preceded by analogWrite(0) and followed by a new analogWrite(whatever_again) to make sure that the duty cycle is recalculated depending on the new PWM frequency.
I didn't know this, but I'd expect the Teensy to maintain the duty cycle through PWM frequency changes. So is this a bug or documented behaviour (apart from "documented in the code")? If it's neither I suggest we make it either one?
in analogWriteFrequency() duty (mod) is recalculated in hardware/teensy/avr/cores/teensy3/pins_teensy.c
mod = (float)(ftmClock >> prescale) / frequency - 0.5f; //Use ftmClock instead of F_TIMER
yes, the teensy library maintains duty cycle even as you change frequency with analogWriteFrequency()
in analogWriteFrequency() duty (mod) is recalculated in hardware/teensy/avr/cores/teensy3/pins_teensy.c
mod = (float)(ftmClock >> prescale) / frequency - 0.5f; //Use ftmClock instead of F_TIMER
I didn't know this, but I'd expect the Teensy to maintain the duty cycle through PWM frequency changes. So is this a bug or documented behaviour (apart from "documented in the code")? If it's neither I suggest we make it either one?
Let's see how things are connected together: The user does want to change the frequency of the signal while maintaining the relative duty cycle (50% in tis case).
But the FTM handles things differently. In the MOD register, it stores a number of ticks which are proportional to the overall signal period and which are thus calculated as round(timer_clock/frequency), where timer_clock is defined by F_BUS and the pre-scaler settings.
When it comes to the relative duty cycle, some math is needed, too, because the FTM has no direct setting fo it. Similar to the MOD register, the CNTIN register stores the number of ticks for which the output goes high, independent of the MOD register content. Thus, changing the frequency while maintaining the relative duty cycle requires writing to the MOD and the CNTIN register.
Let's take an example: Using a T3.6 at 180MHz will give us a F_BUS=60MHz as the FTM clock when no pre-scaler is used. We want an output frequency of 1kHz, so we set the MOD register to 60000 ticks. That means that the timer will restart every 1ms. But nothing will happen at the output as long as we do not tell the timer that we want it to go high for half of the time, which corresponds to writing 30000 to the CNTIN register. Now, the output will go high for 0.5ms every 1ms which corresponds to a 50% duty cycle.
Now let's raise the frequency to 1.5kHz. That means, that the signal period goes from 1ms to 0.667ms or from 60000 ticks to 40000 ticks. Thus, we'd write 40000 to the MOD register. What happens afterwards? The output will still go high for 0.5ms but every 0.667ms which means that the relative duty cycle has changed to 75%...
In order to come back to our initial 50%, we need to adapt the value in CNTIN, too, and set it to 20000 ticks. Now, the "high time" of the output is only 0.333ms for an overall period of 0.667, which corresponds again to a 50% duty cycle.
Thus, changing the frequency while maintaining the relative duty cycle requires changing the MOD and CNTIN values, which is not covered by the analogWriteFrequency() function alone.
Hello @Theremingenieur
My understanding is that CNTIN is the value from which the timer counts all the way up to MOD. So, to me, CNTIN is set to 0 in analogWriteFrequency(). Would your statement be more correct if you replace CNTIN with FTMx_CnV ?