AnsweredAssumed Answered

i.MX RT QTMR Cascade Mode

Question asked by Stefano Voulaz on Feb 22, 2020

Hello,

I'm trying to implement a 32-bit free running counter using two cascaded counters of one QTMR, with no success. The counter counts fine, but reading the HOLD registers doesn't return the expected values when the timer is read on a carry boundary. The lower 16 bit word of the counter (from CNTR) has wrapped, but the upper 16 bit word (from HOLD) has not been incremented, i.e., doing fast reads in a loop:

 

   n-th pass: TMR1_CNTR0 (lower) reads FFFA, TMR1_HOLD1 (upper) reads 0001

   next pass: TMR1_CNTR0 (lower) reads 0002, TMR1_HOLD1 (upper) reads 0001 (should be 0002)

 

This is the initialization code for a 1015:

 

const qtmr_config_t QuadTimer_1_Channel_0_config = {
.primarySource = kQTMR_ClockCounter1InputPin,
.secondarySource = kQTMR_Counter1InputPin,
.enableMasterMode = false,
.enableExternalForce = false,
.faultFilterCount = 0,
.faultFilterPeriod = 0,
.debugMode = kQTMR_RunNormalInDebug
};
const qtmr_config_t QuadTimer_1_Channel_1_config = {
.primarySource = kQTMR_ClockCounter0Output,
.secondarySource = kQTMR_Counter0InputPin,
.enableMasterMode = false,
.enableExternalForce = false,
.faultFilterCount = 0,
.faultFilterPeriod = 0,
.debugMode = kQTMR_RunNormalInDebug
};

 

void QuadTimer_1_init(void) {
/* Quad timer channel Channel_0 peripheral initialization */
QTMR_Init(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, &QuadTimer_1_Channel_0_config);
/* Setup the timer period of the channel */
QTMR_SetTimerPeriod(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, 1U);
/* Quad timer channel Channel_1 peripheral initialization */
QTMR_Init(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, &QuadTimer_1_Channel_1_config);
/* Setup the timer period of the channel */
QTMR_SetTimerPeriod(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, 1U);

 

// Fixup: since the timers are cascaded to create a 32-bit timer, make them free-running
/* Set the length bit to NOT reinitialize the counters on a match */
QUADTIMER_1_PERIPHERAL->CHANNEL[0].CTRL &= ~TMR_CTRL_LENGTH_MASK;
QUADTIMER_1_PERIPHERAL->CHANNEL[1].CTRL &= ~TMR_CTRL_LENGTH_MASK;

 

/* Start the timer - select the timer counting mode */
QTMR_StartTimer(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, kQTMR_PriSrcRiseEdge);
/* Start the timer - select the timer counting mode */
QTMR_StartTimer(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, kQTMR_CascadeCount);
}

 

In this particular case Channel 0 is fed by Counter Input Pin 1 for routing constraints, but this does not affect the counter behaviour (tested). Note also the LENGTH field of TMRx_CTRLn set to zero to implement a free running counter wrapping just at 0xFFFFFFFF.

 

This is how the counter is read:

 

static uint32_t get_mclk_count()
{
return (uint32_t)(QUADTIMER_1_PERIPHERAL->CHANNEL[0].CNTR + ((uint32_t)QUADTIMER_1_PERIPHERAL->CHANNEL[1].HOLD << 16));

}

 

The same issue occurs also if a dummy read is done on CNTR first and then both the upper and lower 16 bit words are read from the relevant HOLD register. It looks like the hold functionality doesn't work at all - at least using the above configuration. Tested on 1015 and 1060 with same results.

 

Any clue?

 

Thanks and best regards,

Stefano

 

UPDATE: after another bit of debug and retries, I finally found the solution.

 

The cascade configuration seems to work correctly only if LENGTH field of TMRx_CTRLn set to one. This requires the counters to be programmed to re-initialize at 0xFFFF (if counting up). Note that this does not mean that the counter will "skip" the value 0xFFFF, instead, it will re-initialize one clock *after* reaching that value. I just mis-interpreted the following sentence: "Count until compare, then re-initialize" in the description of the LENGTH field in TMRx_CTRLn register.

 

Here follows the correct setup sequence to get a free-running 32-bit counter using QTIM:

 

const qtmr_config_t QuadTimer_1_Channel_0_config = {
.primarySource = kQTMR_ClockCounter1InputPin,
.secondarySource = kQTMR_Counter0InputPin,
.enableMasterMode = false,
.enableExternalForce = false,
.faultFilterCount = 0,
.faultFilterPeriod = 0,
.debugMode = kQTMR_RunNormalInDebug
};
const qtmr_config_t QuadTimer_1_Channel_1_config = {
.primarySource = kQTMR_ClockCounter0Output,
.secondarySource = kQTMR_Counter0InputPin,
.enableMasterMode = false,
.enableExternalForce = false,
.faultFilterCount = 0,
.faultFilterPeriod = 0,
.debugMode = kQTMR_RunNormalInDebug
};

void QuadTimer_1_init(void) {
/* Quad timer channel Channel_0 peripheral initialization */
QTMR_Init(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, &QuadTimer_1_Channel_0_config);
/* Setup the timer period of the channel */
QTMR_SetTimerPeriod(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, 0xFFFFU);
/* Quad timer channel Channel_1 peripheral initialization */
QTMR_Init(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, &QuadTimer_1_Channel_1_config);
/* Setup the timer period of the channel */
QTMR_SetTimerPeriod(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, 0xFFFFU);

/* Start the timer - select the timer counting mode */
QTMR_StartTimer(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_1_CHANNEL, kQTMR_CascadeCount);
/* Start the timer - select the timer counting mode */
QTMR_StartTimer(QUADTIMER_1_PERIPHERAL, QUADTIMER_1_CHANNEL_0_CHANNEL, kQTMR_PriSrcRiseEdge);
}

 

Hope this can be helpful to someone else.

Regards,

Stefano

Outcomes