Hi,
I have some trouble understanding the limitations of using multiple channels of MRT at the same time. I took the LPCOpen example of MRT to demonstrate the problem and extended it to use all 4 channels instead of only one.
Code:
#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "board.h"
#include "fsl_mrt.h"
#include <stdbool.h>
#include "fsl_power.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define APP_LED_INIT LED_RED_INIT(LOGIC_LED_OFF);
#define APP_LED_ON (LED_RED_ON());
#define APP_LED_TOGGLE (LED_RED_TOGGLE());
#define MRT_CLK_FREQ CLOCK_GetFreq(kCLOCK_BusClk)
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
static volatile bool mrtIsrFlag0 = false;
static volatile bool mrtEnableCount0 = false;
static volatile uint32_t mrtCountValue0 = 0;
static volatile uint32_t mrtDividerValue0 = 0;
static volatile uint32_t count0 = 0;
static const uint32_t CH0_INTERVAL_MS = 200U;
static volatile bool mrtIsrFlag1 = false;
static volatile bool mrtEnableCount1 = false;
static volatile uint32_t mrtCountValue1 = 0;
static volatile uint32_t mrtDividerValue1 = 0;
static volatile uint32_t count1 = 0;
static const uint32_t CH1_INTERVAL_MS = 250U;
static volatile bool mrtIsrFlag2 = false;
static volatile bool mrtEnableCount2 = false;
static volatile uint32_t mrtCountValue2 = 0;
static volatile uint32_t mrtDividerValue2 = 0;
static volatile uint32_t count2 = 0;
static const uint32_t CH2_INTERVAL_MS = 500U;
static volatile bool mrtIsrFlag3 = false;
static volatile bool mrtEnableCount3 = false;
static volatile uint32_t mrtCountValue3 = 0;
static volatile uint32_t mrtDividerValue3 = 0;
static volatile uint32_t count3 = 0;
static const uint32_t CH3_INTERVAL_MS = 1000U;
/*******************************************************************************
* Code
******************************************************************************/
void MRT0_IRQHandler(void)
{
if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0)) // tried also "== 0x3" but same result
{
/* Clear interrupt flag.*/
MRT_ClearStatusFlags(MRT0, kMRT_Channel_0, kMRT_TimerInterruptFlag);
if (mrtEnableCount0 == true)
{
mrtCountValue0++;
if (mrtCountValue0 == (1 << mrtDividerValue0))
{
mrtIsrFlag0 = true;
count0++;
}
}
else
{
mrtIsrFlag0 = true;
count0++;
}
}
if (MRT_GetStatusFlags(MRT0, kMRT_Channel_1)) // tried also "== 0x3" but same result
{
/* Clear interrupt flag.*/
MRT_ClearStatusFlags(MRT0, kMRT_Channel_1, kMRT_TimerInterruptFlag);
if (mrtEnableCount1 == true)
{
mrtCountValue1++;
if (mrtCountValue1 == (1 << mrtDividerValue1))
{
mrtIsrFlag1 = true;
count1++;
}
}
else
{
mrtIsrFlag1 = true;
count1++;
}
}
if (MRT_GetStatusFlags(MRT0, kMRT_Channel_2)) // tried also "== 0x3" but same result
{
/* Clear interrupt flag.*/
MRT_ClearStatusFlags(MRT0, kMRT_Channel_2, kMRT_TimerInterruptFlag);
if (mrtEnableCount2 == true)
{
mrtCountValue2++;
if (mrtCountValue2 == (1 << mrtDividerValue2))
{
mrtIsrFlag2 = true;
count2++;
}
}
else
{
mrtIsrFlag2 = true;
count2++;
}
}
if (MRT_GetStatusFlags(MRT0, kMRT_Channel_3)) // tried also "== 0x3" but same result
{
/* Clear interrupt flag.*/
MRT_ClearStatusFlags(MRT0, kMRT_Channel_3, kMRT_TimerInterruptFlag);
if (mrtEnableCount3 == true)
{
mrtCountValue3++;
if (mrtCountValue3 == (1 << mrtDividerValue3))
{
mrtIsrFlag3 = true;
count3++;
}
}
else
{
mrtIsrFlag3 = true;
count3++;
}
}
SDK_ISR_EXIT_BARRIER;
}
/*!
* @brief Main function
*/
int main(void)
{
uint32_t mrt_clock;
/* Structure of initialize MRT */
mrt_config_t mrtConfig;
/* Board pin, clock, debug console init */
/* set BOD VBAT level to 1.65V */
POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false);
/* enable clock for GPIO; used to toggle the LED's */
CLOCK_EnableClock(kCLOCK_Gpio1);
/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
BOARD_InitPins();
BOARD_BootClockPLL150M();
BOARD_InitDebugConsole();
/* Initialize and enable LED */
APP_LED_INIT;
mrt_clock = MRT_CLK_FREQ;
/* mrtConfig.enableMultiTask = false; */
MRT_GetDefaultConfig(&mrtConfig);
/* Init mrt module */
MRT_Init(MRT0, &mrtConfig);
/* Setup Channel 0 to be repeated */
MRT_SetupChannelMode(MRT0, kMRT_Channel_0, kMRT_RepeatMode);
/* Enable timer interrupts for channel 0 */
MRT_EnableInterrupts(MRT0, kMRT_Channel_0, kMRT_TimerInterruptEnable);
/* Enable at the NVIC */
EnableIRQ(MRT0_IRQn);
/* Start channel 0 */
PRINTF("\r\nStarting channel No.0 ...");
if (MSEC_TO_COUNT(CH0_INTERVAL_MS, mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
mrtDividerValue0 = 0;
mrtEnableCount0 = true;
while (MSEC_TO_COUNT((CH0_INTERVAL_MS >> (++mrtDividerValue0)), mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
}
MRT_StartTimer(MRT0, kMRT_Channel_0, MSEC_TO_COUNT((CH0_INTERVAL_MS >> mrtDividerValue0), mrt_clock));
}
else
{
MRT_StartTimer(MRT0, kMRT_Channel_0, MSEC_TO_COUNT(CH0_INTERVAL_MS, mrt_clock));
}
PRINTF("\r\nStarting channel No.1 ...");
if (MSEC_TO_COUNT(CH1_INTERVAL_MS, mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
mrtDividerValue1 = 0;
mrtEnableCount1 = true;
while (MSEC_TO_COUNT((CH1_INTERVAL_MS >> (++mrtDividerValue1)), mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
}
MRT_StartTimer(MRT0, kMRT_Channel_1, MSEC_TO_COUNT((CH1_INTERVAL_MS >> mrtDividerValue1), mrt_clock));
}
else
{
MRT_StartTimer(MRT0, kMRT_Channel_1, MSEC_TO_COUNT(CH1_INTERVAL_MS, mrt_clock));
}
PRINTF("\r\nStarting channel No.2 ...");
if (MSEC_TO_COUNT(CH2_INTERVAL_MS, mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
mrtDividerValue2 = 0;
mrtEnableCount2 = true;
while (MSEC_TO_COUNT((CH2_INTERVAL_MS >> (++mrtDividerValue2)), mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
}
MRT_StartTimer(MRT0, kMRT_Channel_2, MSEC_TO_COUNT((CH2_INTERVAL_MS >> mrtDividerValue2), mrt_clock));
}
else
{
MRT_StartTimer(MRT0, kMRT_Channel_2, MSEC_TO_COUNT(CH2_INTERVAL_MS, mrt_clock));
}
PRINTF("\r\nStarting channel No.3 ...");
if (MSEC_TO_COUNT(CH3_INTERVAL_MS, mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
mrtDividerValue3 = 0;
mrtEnableCount3 = true;
while (MSEC_TO_COUNT((CH3_INTERVAL_MS >> (++mrtDividerValue3)), mrt_clock) > MRT_CHANNEL_INTVAL_IVALUE_MASK)
{
}
MRT_StartTimer(MRT0, kMRT_Channel_3, MSEC_TO_COUNT((CH3_INTERVAL_MS >> mrtDividerValue3), mrt_clock));
}
else
{
MRT_StartTimer(MRT0, kMRT_Channel_3, MSEC_TO_COUNT(CH3_INTERVAL_MS, mrt_clock));
}
PRINTF("\r\nmrtDividerValue0 = %u (%u msec)", mrtDividerValue0, CH0_INTERVAL_MS);
PRINTF("\r\nmrtDividerValue1 = %u (%u msec)", mrtDividerValue1, CH1_INTERVAL_MS);
PRINTF("\r\nmrtDividerValue2 = %u (%u msec)", mrtDividerValue2, CH2_INTERVAL_MS);
PRINTF("\r\nmrtDividerValue3 = %u (%u msec)", mrtDividerValue3, CH3_INTERVAL_MS);
while (true)
{
/* Check whether interrupt occurred and toggle LED */
if (true == mrtIsrFlag0)
{
PRINTF("\r\n [0] %u", count0 * CH0_INTERVAL_MS);
APP_LED_TOGGLE;
mrtCountValue0 = 0;
mrtIsrFlag0 = false;
}
if (true == mrtIsrFlag1)
{
PRINTF("\r\n [1] %u", count1 * CH1_INTERVAL_MS);
mrtCountValue1 = 0;
mrtIsrFlag1 = false;
}
if (true == mrtIsrFlag2)
{
PRINTF("\r\n [2] %u", count2 * CH2_INTERVAL_MS);
mrtCountValue2 = 0;
mrtIsrFlag2 = false;
}
if (true == mrtIsrFlag3)
{
PRINTF("\r\n [3] %u", count3 * CH3_INTERVAL_MS);
mrtCountValue3 = 0;
mrtIsrFlag3 = false;
}
}
}
This is what gets printed on terminal:
Starting channel No.0 ...
Starting channel No.1 ...
Starting channel No.2 ...
Starting channel No.3 ...
mrtDividerValue0 = 1 (200 msec)
mrtDividerValue1 = 2 (250 msec)
mrtDividerValue2 = 3 (500 msec)
mrtDividerValue3 = 4 (1000 msec)
[0] 200
[1] 250
[0] 400
[0] 600
[2] 500
[0] 800
[1] 500
[0] 1000
[1] 750
[0] 1200
[0] 1400
[0] 1600
[1] 1000
[2] 1000
[3] 1000
[0] 1800
[0] 2000
[1] 1250
[0] 2200
After 60 seconds I would expect msec counters to show roughly the same values (~60,000 msec).
Instead:
[2] 36000
[3] 36000
[0] 57800
[0] 58000
[1] 36250
[0] 58200
[0] 58400
[1] 36500
[2] 36500
[0] 58600
[0] 58800
[1] 36750
[0] 59000
[2] 37000
[3] 37000
[0] 59200
[1] 37000
[0] 59400
[0] 59600
[1] 37250
[0] 59800
[0] 60000
[1] 37500
[2] 37500
Summary:
Channel 0: OK
Channel 1: NOK
Channel 2: NOK
Channel 3: NOK
Then I tried to simply swap the intervals in reverse order. Terminal:
Starting channel No.0 ...
Starting channel No.1 ...
Starting channel No.2 ...
Starting channel No.3 ...
mrtDividerValue0 = 4 (1000 msec)
mrtDividerValue1 = 3 (500 msec)
mrtDividerValue2 = 2 (250 msec)
mrtDividerValue3 = 1 (200 msec)
[3] 200
[2] 250
[3] 400
[3] 600
[3] 800
[1] 500
[2] 500
[3] 1000
[2] 750
[3] 1200
[3] 1400
[0] 1000
[1] 1000
[2] 1000
[3] 1600
After 60 seconds:
[1] 59000
[2] 59000
[3] 94600
[2] 59250
[3] 94800
[3] 95000
[1] 59500
[2] 59500
[3] 95200
[3] 95400
[2] 59750
[3] 95600
[3] 95800
[0] 60000
[1] 60000
[2] 60000
[3] 96000
[3] 96200
[2] 60250
[3] 96400
[3] 96600
[3] 96800
[1] 60500
[2] 60500
Summary:
Channel 0: OK
Channel 1: OK
Channel 2: OK
Channel 3: NOK
I don't remember seeing this on LPC1768 but maybe I was just lucky. Do I have some silly bug or is this expected behavior with this configuration? And most importantly, is there something that I could do to make this example work?
Bus clock is taken from system clock (150MHz) and LPC55S28 has 4 x 24-bit channels on MRT0.
Thanks.
Solved! Go to Solution.
Found it! There were 2 problems in the code of the first post:
These changes will resolve the issue:
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_1))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_1) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_2))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_2) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_3))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_3) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
MRT_SetupChannelMode(MRT0, kMRT_Channel_0, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_1, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_2, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_3, kMRT_RepeatMode);
MRT_EnableInterrupts(MRT0, kMRT_Channel_0, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_1, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_2, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_3, kMRT_TimerInterruptEnable);
More debugging results. I removed all the code related to channels 2 and 3, and continued testing with only channels 0 and 1 to simplify things.
I set the following periods:
static const uint32_t CH0_INTERVAL_MS = 10U;
static const uint32_t CH1_INTERVAL_MS = 1000U;
And removed PRINTF overhead from super loop:
while (true)
{
/* Check whether interrupt occurred and toggle LED */
if (true == mrtIsrFlag0)
{
if (count0 == 6000U) // 60 sec == 10 msec * 6000
{
PRINTF("\r\n [0] %u", count0 * CH0_INTERVAL_MS);
PRINTF("\r\n [1] %u", count1 * CH1_INTERVAL_MS);
for(;;);
}
mrtCountValue0 = 0;
mrtIsrFlag0 = false;
}
if (true == mrtIsrFlag1)
{
if (count1 == 60U) // 60 sec == 1000 msec * 60
{
PRINTF("\r\n [1] %u", count1 * CH1_INTERVAL_MS);
PRINTF("\r\n [0] %u", count0 * CH0_INTERVAL_MS);
for(;;);
}
mrtCountValue1 = 0;
mrtIsrFlag1 = false;
}
}
I used stopwatch to see how long it takes for either if statement to execute. After little less than 10 seconds the "60 seconds" limit of channel 1 was reached:
Starting channel No.0 ...
Starting channel No.1 ...
mrtDividerValue0 = 0 (10 msec)
mrtDividerValue1 = 4 (1000 msec)
[1] 60000
[0] 9600
Then I commented out that if statement to verify how long it takes for channel 0 to reach its limit:
while (true)
{
/* Check whether interrupt occurred and toggle LED */
if (true == mrtIsrFlag0)
{
if (count0 == 6000U) // 60 sec == 10 msec * 6000
{
PRINTF("\r\n [0] %u", count0 * CH0_INTERVAL_MS);
PRINTF("\r\n [1] %u", count1 * CH1_INTERVAL_MS);
for(;;);
}
mrtCountValue0 = 0;
mrtIsrFlag0 = false;
}
if (true == mrtIsrFlag1)
{
// if (count1 == 60U) // 60 sec == 1000 msec * 60
// {
// PRINTF("\r\n [1] %u", count1 * CH1_INTERVAL_MS);
// PRINTF("\r\n [0] %u", count0 * CH0_INTERVAL_MS);
// for(;;);
// }
mrtCountValue1 = 0;
mrtIsrFlag1 = false;
}
}
And it took exactly 60 seconds:
Starting channel No.0 ...
Starting channel No.1 ...
mrtDividerValue0 = 0 (10 msec)
mrtDividerValue1 = 4 (1000 msec)
[0] 60000
[1] 375000
So channel 0 works alright, but the counter of channel 1 is updated 625% (375/60) more often than it should.
Then I noticed that I had completely forgot to do the same initializations for other channels than channel 0. I fixed that by:
MRT_SetupChannelMode(MRT0, kMRT_Channel_0, kMRT_RepeatMode);
MRT_SetupChannelMode(MRT0, kMRT_Channel_1, kMRT_RepeatMode); // this was missing
MRT_EnableInterrupts(MRT0, kMRT_Channel_0, kMRT_TimerInterruptEnable);
MRT_EnableInterrupts(MRT0, kMRT_Channel_1, kMRT_TimerInterruptEnable); // so was this
Now I was quite confident that it would finally work. But after around ~52 seconds I got the same result:
Starting channel No.0 ...
Starting channel No.1 ...
mrtDividerValue0 = 0 (10 msec)
mrtDividerValue1 = 4 (1000 msec)
[0] 60000
[1] 375000
I'm confused. How should this work?
Found it! There were 2 problems in the code of the first post:
These changes will resolve the issue:
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_1))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_1) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_2))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_2) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
- if (MRT_GetStatusFlags(MRT0, kMRT_Channel_3))
+ if (MRT_GetStatusFlags(MRT0, kMRT_Channel_3) == (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK))
MRT_SetupChannelMode(MRT0, kMRT_Channel_0, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_1, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_2, kMRT_RepeatMode);
+ MRT_SetupChannelMode(MRT0, kMRT_Channel_3, kMRT_RepeatMode);
MRT_EnableInterrupts(MRT0, kMRT_Channel_0, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_1, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_2, kMRT_TimerInterruptEnable);
+ MRT_EnableInterrupts(MRT0, kMRT_Channel_3, kMRT_TimerInterruptEnable);
Hi,
It appears that your code has bug.
You would like to implement a repeated fixed period interrupt with different periods based on multiple channels of MRT module, but when the period is longer than what the 24 bits of INTVAL[x] can represents, it uses software counting mode to enlarge the period. So I think your code has bugs, I just changed one channel, pls modify the other channels yourself.
void MRT0_IRQHandler(void)
{
if (MRT_GetStatusFlags(MRT0, kMRT_Channel_0)) // tried also "== 0x3" but same result
{
/* Clear interrupt flag.*/
MRT_ClearStatusFlags(MRT0, kMRT_Channel_0, kMRT_TimerInterruptFlag);
if (mrtEnableCount0 == true)
{
mrtCountValue0++;
if (mrtCountValue0 == (1 << mrtDividerValue0))
{
mrtIsrFlag0 = true;
mrtCountValue0=0; //Rong write
count0++;
}
}
else
{
mrtIsrFlag0 = false;
count0++;
}
}
Pls have a try.
Hope it can help you
BR
XiangJun Rong
Thanks for your reply @xiangjun_rong!
Unfortunately that does not fix the problem. The software counters are cleared in the super loop instead of ISR. That's the way it was done in the example.
Hi,
Maybe the printf() function has issue, it takes a long time.
Can you have a test, you delete all the printf() in the while(true) loop, and declare a two dimension uint32_t array, you can save both the channel and counter to the array, then check if the time value are similar for different channels.
BR
XiangJun Rong
Hi,
I actually suspected printf() also, so I already tested that earlier - not with an array but breakpoint instead. But since periods are relatively long, printf() did not have any effect. The outcome is exactly the same.