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

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

4,725 Views
TomE
Specialist II

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

Labels (2)
0 Kudos
Reply
9 Replies

4,323 Views
houe
Contributor II

Any updates to this? I am running into this exact problem. Both 0 and 100% do not work on IMXRT1020 dev board and SDK 2.7.

It is not clear to me from documentation that the QTMR peripheral is capable of doing 0 and 100 duty cycle perfectly. I've been thinking of switching to plain GPIO control for 0% and 100%. But I too am a little perplexed that the NXP provided driver didn't handle this correctly. Seems like those two cases would be one of the first things to be tested. Or are we just making some common mistake? Hope we have answers soon.

0 Kudos
Reply

4,323 Views
TomE
Specialist II

NXP has a deep history of different families of different peripheral modules. You can see the history in the design when you come across a peripheral with 8 bit registers. They date from the 6800 days. Timers, PWM, SPI, CAN, I2C, ADC and so on. Different chips are bought out to continue the development of previous lines. 6802 went to MC9S to Kinetis. MC68k to Coldfire. And so on. Peripherals are often "carried over" so customers can port their code to the new chips easily.

I don't know where the peripherals in the i.MXRT are from. The only peripheral I can recognise (from previous chips) is the Flexcan one.There may be customers of specific chips out there that can transition easily to these chip as the design is similar and the peripherals are the same. For those of us taking this up "from scratch" or with existing Coldfire or i.MX code it's a lot of work.

Some might be from the MC56 "DSC" range. They have a "Quad Timer". Yes, the one in the MC56F826xx looks to be the same. Maybe it has better sample code, or maybe the same sample code.

Maybe the original customer this timer was designed for (to go in a million ADSL boxes or whatever it was) didn't need a PWM that went to zero, so it wasn't a requirement, so this timer can't do that. If you need a 0% to 100% PWM then don't use this peripheral. If no other peripheral in this chip can do what you need, then make that part of the selection process.

Tom

0 Kudos
Reply

4,323 Views
houe
Contributor II

I should have responded sooner, but after further investigation the peripheral itself does have a feature to force the output high or low (i.e. 0% and 100%). I created my own SetupPwm() function which takes advantage of this feature. NXP should do the same for their driver code as the current code in the SDK does not do what its documentation states it does - its buggy.

0 Kudos
Reply

3,804 Views
Hat
Contributor II

The updated SDK fixes this problem only partially. It generates the correct frequency whenever the output duty cycle is not 0% or 100%. It still produces the the one clock cylce pulse at 0%.

 

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

        if (highCount > 0U)
        {
            highCount -= 1U;
        }
        if (lowCount > 0U)
        {
            lowCount -= 1U;
        }

        /* This should not be a 16-bit overflow value. If it is, change to a larger divider for clock source. */
        assert(highCount <= 0xFFFFU);
        assert(lowCount <= 0xFFFFU);

 

@houe Are you able to share your SetupPwm() function?

@kerryzhou Could you please check with the SDK developers in regards to this?

0 Kudos
Reply

3,776 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Hat,

   Any question, please create the new question post by yourself, then we will process it on your own question post, thanks a lot for your understanding.

  You can post the question in this link:

https://community.nxp.com/t5/i-MX-RT/bd-p/imxrt

 

Best Regards,

kerry

0 Kudos
Reply

4,323 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Tom Evans,

  Thanks so much for your slefless dedication.

  Today, I am testing the new SDK, I mainly test this part, really like your said, 0% and 100% totally not correct.

  I already reported to the SDK department, in the next version, this part will be fixed.

  Thank you again.

  If you find any bug about the RT SDK, welcome to let us know!

Have a great day,
Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
Reply

4,323 Views
TomE
Specialist II

The code sample shown in that post has exactly the same bugs as the ones I've just reported.

The QTMR__SetCmpldValue() function is a cut-and-paste of QTMR_SetupPwm(). It gets the period wrong, the pulse widths wrong and doesn't work at 0% and 100% duty cycle.

> Wish it helps you!

It doesn't help me at all. I've pointed out problems with the sample code, and you've referred me to code with the same problems. It should help you that I can inspect that code and tell you that it is buggy as well and doesn't work properly. It hasn't been written according to how the timer is documented in the Reference Manual. Also that it hasn't been tested properly. This is very easy to see by simple inspection of the code. It is obviously wrong.

I have tested both problems. I've proved the counters need to be set to period-minus-one to get the right frequency against an external reference. I've proved that setting a comparison register to zero generates output pulses one clock period wide.

It is very easy to prove that it is buggy. Set up a test, and program the PWM to any frequency. Then measure that frequency with an accurate digital frequency meter. It will show it running at slightly the wrong frequency, by two parts in whatever the sum or the comparison load values are. It is easier to compare the output against a proper, accurate signal at the same frequency from a timer that has been programmed properly with an oscilloscope. The waveforms will drift relative to each other.

If the sum of the counts is somewhere around 50000 (for a 150MHz clock generating a 3kHz output), then the error is 2 parts in 50000, or 0.004% or 40 ppm. For something like a 3MHz output, the error is 4%, and that should be very easy to see.

Try to set 0% or 100% duty cycle. The output will still be pulsing and not at true 0% or 100%. This is easier to see with a large prescaler setting.

Tom

0 Kudos
Reply

4,324 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Tom Evans,

  Thanks a lot for your contribution.

  I will double check your detail pointed bug points, if it is confirmed, I will report it to our SDK department directly.

  Any updated information from my side, I will let you know.

Best Regards,

kerry

0 Kudos
Reply

4,324 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Tom Evans ,

  I highly recommend you to check the following post at first:

PWM VARIABLE DUTY CYCLE 

  We already share the QTIMER duty change code in that post, it is use QTIMER associated with DMA.

Wish it helps you!

If you still have questions about it, please kindly let me know.

Have a great day,
Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
Reply