Multiple channels of Multi-Rate Timer (MRT) on LPC55S28

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

Multiple channels of Multi-Rate Timer (MRT) on LPC55S28

Jump to solution
1,407 Views
vanska
Contributor II

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.

Labels (3)
0 Kudos
1 Solution
1,373 Views
vanska
Contributor II

Found it! There were 2 problems in the code of the first post:

  • Missing initializations of other channels than 0
  • MRT_GetStatusFlags return value must be compared to 0x3

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);

View solution in original post

0 Kudos
6 Replies
1,379 Views
vanska
Contributor II

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?

0 Kudos
1,374 Views
vanska
Contributor II

Found it! There were 2 problems in the code of the first post:

  • Missing initializations of other channels than 0
  • MRT_GetStatusFlags return value must be compared to 0x3

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);
0 Kudos
1,400 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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

0 Kudos
1,398 Views
vanska
Contributor II

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.

0 Kudos
1,396 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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

0 Kudos
1,394 Views
vanska
Contributor II

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.

0 Kudos