Tickless Freertos and LLS mode

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 

Tickless Freertos and LLS mode

2,372 次查看
whata
Contributor III

Hi, 

I'm attempting to implement an LLS mode in Freertos tickless operation. What i first did is I implemented the LLS mode in baremetal and verified that the MCU was entering the LLS mode (consuming around 2-3 uA) and was waking up correctly by the LLWU unit (via pin or lptrm). I then created a copy of: \boards\frdmkl26z\rtos_examples\freertos_tickless project which pretty much does the same thing wakes up either via lptmr or pin interrupt. 

I just extended the vPortSuppressTicksAndSleep function with code to properly enter the LLS mode without even touching clock settings or anything and it worked mostly as expected, the device could be woken up by an external interrupt or by timer timeout, except for one thing: Once the LLS was implemented the timing got all wrong and now 100 ticks are no longer 100ms but more like 50 seconds. How can that be? Is there a prescaler for LPTMR which gets magically modified somewhere?

Here's my additions to vPortSuppressTicksAndSleep 

               xModifiableIdleTime = xExpectedIdleTime;
               configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
               LOWPWR_PreSwitch();
               LOWPWR_SetWakeUp();
               LOWPWR_SwitchMode();
               LOWPWR_PostSwitch();
               if( xModifiableIdleTime > 0 )
               {

                    __asm volatile( "dsb" );
                    __asm volatile( "wfi" );
                    __asm volatile( "isb" );


               }
               configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

And the implementation of functions called from  vPortSuppressTicksAndSleep 

/*!
 * @brief This function must be executed prior to entering the LLS mode. Here we
 *  wait for transmission completion on all devices, and then disable the used
 *  peripherals and disable the pins to avoid unecessary current usage
 */
void LOWPWR_PreSwitch (void) {

    while (!(kLPSCI_TransmissionCompleteFlag & LPSCI_GetStatusFlags((UART0_Type *)BOARD_DEBUG_UART_BASEADDR)));
    DbgConsole_Deinit();
    PORT_SetPinMux(DEBUG_CONSOLE_RX_PORT, DEBUG_CONSOLE_RX_PIN, kPORT_PinDisabledOrAnalog);

}

/*!
 * @brief This function must be executed after exiting the LLS mode. Here we
 *  re-enable the peripherals which were disabled.
 */
void LOWPWR_PostSwitch (void) {
     /*
      * Re-enable the rx pin on the console
      */

     PORT_SetPinMux(DEBUG_CONSOLE_RX_PORT, DEBUG_CONSOLE_RX_PIN, DEBUG_CONSOLE_RX_PINMUX);

    /*
     * If enter stop modes when MCG in PEE mode, then after wakeup, the MCG is in PBE mode,
     * need to enter PEE mode manually.
     */
    while (!(kMCG_Pll0LockFlag & CLOCK_GetStatusFlags()));
    CLOCK_SetPeeMode();

    /* Re-enable console */

    BOARD_InitDebugConsole();

}

/*!
 * @brief This function sets-up the wake-up for the microcontroller. The're are two sources
 *  which are used for wake-up, a LPTMR interrupt and SW1 interrupt via the LLWU.
 */
void LOWPWR_SetWakeUp (void) {

    /* Set up LLWU to receive interrupts from LPTMR=0U */
    LLWU_EnableInternalModuleInterruptWakup(LLWU, LLWU_LPTMR_IDX, true);

    /* Set up LLWU to receive interrupt from pin=7U */
    LLWU_SetExternalWakeupPinMode(LLWU, LLWU_WAKEUP_PIN_IDX, kLLWU_ExternalPinFallingEdge);

}

/*!
 * @brief This function enters the low power mode
 */
void LOWPWR_SwitchMode (void) {
    SMC_PreEnterStopModes();
    SMC_SetPowerModeLls(SMC);
    SMC_PostExitStopModes();
}


/*!
 * @brief LLWU interrupt handler.
 */
void LLWU_IRQHandler(void)
{
     portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    /* If wakeup by LPTMR. */
    if (LLWU_GetInternalWakeupModuleFlag(LLWU, LLWU_LPTMR_IDX))
    {
        vPortLptmrIsr();
    }

    /* If wakeup by external pin. */
    if (LLWU_GetExternalWakeupPinFlag(LLWU, LLWU_WAKEUP_PIN_IDX))
    {
        PORT_ClearPinsInterruptFlags(BOARD_SW1_PORT, (1U << BOARD_SW1_GPIO_PIN));
        LLWU_ClearExternalWakeupPinFlag(LLWU, LLWU_WAKEUP_PIN_IDX);

        xSemaphoreGiveFromISR(xSWSemaphore, &xHigherPriorityTaskWoken);
    }

}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
标记 (1)
2 回复数

1,564 次查看
whata
Contributor III

Hi Jorge, 

Thank you for taking your time to look at the code, unfortunately commenting out the asm instructions didn't help much, but I found that the ulLPTimerInterruptFired was being cleared too early by the vPortSuppressTicksAndSleep function, once I fixed that everything worked as expected. 

0 项奖励
回复

1,564 次查看
jorge_a_vazquez
NXP Employee
NXP Employee

Hi whata

I have checked your code, I noticed that when you modify vPortSuppressTicksAndSleep you have the following part:

xModifiableIdleTime = xExpectedIdleTime;
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
LOWPWR_PreSwitch();
LOWPWR_SetWakeUp();
LOWPWR_SwitchMode();
LOWPWR_PostSwitch();
if( xModifiableIdleTime > 0 )
      {
       __asm volatile( "dsb" );
       __asm volatile( "wfi" );
       __asm volatile( "isb" );
       }
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );‍‍‍‍‍‍‍‍‍‍‍‍‍

But if you go into the SMC_SetPowerModeLls(SMC); function (the one that is called in SetWakeUp()), you will find that there it calls

__DSB();
__WFI();
__ISB();

So, the method to call this APIs in this example should be:

xModifiableIdleTime = xExpectedIdleTime;
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > 0 )
      {
       LOWPWR_PreSwitch();
       LOWPWR_SetWakeUp();
       LOWPWR_SwitchMode();
       LOWPWR_PostSwitch();
       }
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Hope this information could help you.

Best Regards

Jorge Alcala

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