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.