We are writing a driver for the i.MXRT1060 Quad Timer to generate PWM outputs from 0% to 100%.
We have the code samples from "MCUXpresso", and they claim to be able to do this, but actually don't. It looks like these drivers weren't fully tested to see if they did what the function headers say they do.
The MCUXpresso "fsl_qtmr" code isn't a "working example".
This is what the code says it does, and what it actually does:
SDK_2.6.2_MIMXRT1062xxxxA/devices/MIMXRT1062/drivers/fsl_qtmr.c
status_t QTMR_SetupPwm(TMR_Type *base,
qtmr_channel_selection_t channel,
uint32_t pwmFreqHz,
uint8_t dutyCyclePercent,
bool outputPolarity,
uint32_t srcClock_Hz)
{
uint32_t periodCount, highCount, lowCount, reg;
if (dutyCyclePercent > 100)
{
return kStatus_Fail;
}
base->CHANNEL[channel].SCTRL |= (TMR_SCTRL_FORCE_MASK | TMR_SCTRL_OEN_MASK);
periodCount = (srcClock_Hz / pwmFreqHz);
highCount = (periodCount * dutyCyclePercent) / 100;
lowCount = periodCount - highCount;
base->CHANNEL[channel].COMP1 = lowCount;
base->CHANNEL[channel].COMP2 = highCount;
base->CHANNEL[channel].CMPLD1 = lowCount;
base->CHANNEL[channel].CMPLD2 = highCount;
There are two problems with the above. It doesn't generate pulses at the requested frequency or of the requested width, but has two "off by one" bugs. It can't generate "0%" or "100%" either.
The Reference Manual doesn't make this clear, but it does say the counter counts from "0" until the counter matches "COMP1" or "COMP2". It shows this in "Figure 53-8. Compare Load Timing" in the i.MXRT1060 Reference Manual. For instance, if "COMP1" is "10", the counter will count ELEVEN clocks. In order to get the correct requested timing, the "highCount" and "lowCount" variables in the above need to have '1" subtracted from them.
Which would cause all sorts of problems if they had been calculated to be zero already (at 0% or 100% requested), as that would make them 65535. The second problem is that a "COMPn" value of zero generates a one clock wide pulse. So the Quad Timer can't actually generate 0% or 100% on its own. You either have to override the GPIO pins to that, or you have to disable the timer and use the "FORCE" and "VAL" bits in the "SCTRLn" register to set the pin to 0% or 100%.
The sample code doesn't demonstrate that. It should be fixed so that it can, or documented that it can't do what it says.
I have just proved that a "0" loaded into the COMP2 register results in a single-clock-bit-wide pulse of 6.6ns at the clock and divider that I'm using (150MHz and prescaler dividing by one). If I change the prescaler to divide-by-16, the pulse is 107ns wide, and easier to see and measure.
I'll refer to the post with other Quad Timer problems here for anyone reading this post wanting to know of these other problems:
Has anybody used Timer Overflow Flag with the QTMR?
Tom