AnsweredAssumed Answered

i.MXRT Quad Timer (QTMR): Are There Any Working Code Examples?

Question asked by TomE on Dec 11, 2019
Latest reply on Apr 20, 2020 by michael boyko

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

 

/*!
* brief Sets up Quad timer module for PWM signal output.
*
* The function initializes the timer module according to the parameters passed in by the user. The
* function also sets up the value compare registers to match the PWM signal requirements.
*
* param base             Quad Timer peripheral base address
* param channel          Quad Timer channel number
* param pwmFreqHz        PWM signal frequency in Hz
* param dutyCyclePercent PWM pulse width, value should be between 0 to 100
*                         0=inactive signal(0% duty cycle)...
*                         100=active signal (100% duty cycle)
* param outputPolarity   true: invert polarity of the output signal, false: no inversion
* param srcClock_Hz      Main counter clock in Hz.
*
* return Returns an error if there was error setting up the signal.
*/

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)
    {
        /* Invalid dutycycle */
        return kStatus_Fail;
    }

    /* Set OFLAG pin for output mode and force out a low on the pin */
    base->CHANNEL[channel].SCTRL |= (TMR_SCTRL_FORCE_MASK | TMR_SCTRL_OEN_MASK);

    /* Counter values to generate a PWM signal */
    periodCount = (srcClock_Hz / pwmFreqHz);
    highCount   = (periodCount * dutyCyclePercent) / 100;
    lowCount    = periodCount - highCount;

    /* Setup the compare registers for PWM output */
    base->CHANNEL[channel].COMP1 = lowCount;
    base->CHANNEL[channel].COMP2 = highCount;

    /* Setup the pre-load registers for PWM output */
    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

Outcomes