Low power mode with KW41Z

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

Low power mode with KW41Z

1,038 Views
paralax
Contributor I

Hi everyone.

I'm working on a custom board with a Kinetis KW41Z on it. The board is powered up with a 3.3V rail, and I use the integrated DCDC to generate the 1.8V. At the moment, only the KW41Z, the inductors and capacitors needed by the DCDC are populated, and the 32Mhz + 32KHz crystals.

I want to achieve a sleep mode, while using FreeRTOS.

To do so, I activated tickless mode, as showed in the various sdk examples. I think this activation is successful as I enter my custom vPortSupressTicksAndSleep function.

In this function, I then setup the LPTimer0, go to sleep, and wakeup.The wakeup is programmed by a simple task that update a variable every seconds, and the timer is init like this:

//Configure Low Power timer
    lptmr_config_t lptmrConfig;
    CLOCK_EnableClock(kCLOCK_Lptmr0);
    /* Configuration LPTMR  */
    /*
     * lptmrConfig.timerMode = kLPTMR_TimerModeTimeCounter;
     * lptmrConfig.pinSelect = kLPTMR_PinSelectInput_0;
     * lptmrConfig.pinPolarity = kLPTMR_PinPolarityActiveHigh;
     * lptmrConfig.enableFreeRunning = false;
     * lptmrConfig.bypassPrescaler = true;
     * lptmrConfig.prescalerClockSource = kLPTMR_PrescalerClock_1;
     * lptmrConfig.value = kLPTMR_Prescale_Glitch_0;
     */
    LPTMR_GetDefaultConfig(&lptmrConfig);
    lptmrConfig.prescalerClockSource=kLPTMR_PrescalerClock_1;
    lptmrConfig.bypassPrescaler = true;
    LPTMR_Init(LPTMR0, &lptmrConfig);

Everything seems running nicely: I can see by toggling a led in the LPTimer that the interrupt is firing every seconds as expected. I can see by toggling a led before going to sleep and after waking up that the devices behave as expected. The timing seems correct too.

However, I can't get a current consumption lower than 4mA. I tried all the low power mode, none of them seems to make any difference in sleep consumption.

My sleep function is very simple:

  GPIO_SetPinsOutput(GPIOC, 1 << 4);
  smc_power_mode_vlls_config_t vlls_config;
  vlls_config.subMode = kSMC_StopSub1;
  SMC_SetPowerModeVlls(SMC, &vlls_config);

I've tried disabling all the peripheral by hands, played with the clock configs, etc... Still 4mA minimum. Is there some sort of init needed that I missed?

Please note that I don't use the LLWU as I don't see how, with the SDK I have, to use it to configure the wake by LPTimer. And I wakeup without it anyway...

Thanks in advance.

Regards,

Matthieu

PS: here is some other code, in case it is needed:

void vOnPreSleepProcessing(portTickType expectedIdleTicks)
{
  (void) expectedIdleTicks;
  #if configUSE_TICKLESS_IDLE == 2
  CLOCK_DisableClock(kCLOCK_PortC);
  GPIO_SetPinsOutput(GPIOC, 1 << 4);
  smc_power_mode_vlls_config_t vlls_config;
  vlls_config.subMode = kSMC_StopSub1;
  SMC_SetPowerModeVlls(SMC, &vlls_config);
  #endif
}
void vOnPostSleepProcessing(portTickType expectedIdleTicks)
{
  (void) expectedIdleTicks;
  CLOCK_EnableClock(kCLOCK_PortC);
  GPIO_ClearPinsOutput(GPIOC, 1 << 4);
  /*dcdc_min_power_config_t dcdc;
  DCDC_GetDefaultMinPowerDefault(&dcdc);
  DCDC_SetMinPowerConfig(DCDC, &dcdc);*/
}
/**
 * @brief Setup the systick timer to generate the tick interrupts at the required
 * frequency.
 *
 * @author NXP, SDK2.7
 */
void vPortSetupTimerInterrupt(void)
{
/* Calculate the constants required to configure the tick interrupt. */
#if configUSE_TICKLESS_IDLE == 2
    ulTimerCountsForOneTick   = (configCPU_CLOCK_HZ / configTICK_RATE_HZ);
    ulLPTimerCountsForOneTick = (configLPTMR_CLOCK_HZ / configTICK_RATE_HZ);
    if (ulLPTimerCountsForOneTick != 0)
    {
        xMaximumPossibleSuppressedTicks = portMAX_16_BIT_NUMBER / ulLPTimerCountsForOneTick;
    }
    else
    {
        /* ulLPTimerCountsForOneTick is zero, not allowed state */
        while (1)
            ;
    }
    NVIC_EnableIRQ(LPTMR0_IRQn);
#endif /* configUSE_TICKLESS_IDLE */
    /* Configure SysTick to interrupt at the requested rate. */
    SysTick->LOAD = (configCPU_CLOCK_HZ / configTICK_RATE_HZ) - 1UL;
    SysTick->VAL  = 0UL;
    SysTick->CTRL = (SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk);
}

/**
 * @brief LPTimer0 interrupt handler
 *
 * @author NXP, SDK2.7
 */
void vPortLptmrIsr(void)
{
    ulLPTimerInterruptFired = true;
    LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
    LPTMR_StopTimer(LPTMR0);
    GPIO_TogglePinsOutput(GPIOC, 1 << 5);
}
/**
 * @brief Reimplementation of the vPortSuppressTicksAndSleep to enter and exit sleep
 *
 * @param xExpectedIdleTime The expected time the decice will remain in sleep
 *
 * @author  NXP, SDK2.7
 */
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{
    uint32_t ulReloadValue, ulCompleteTickPeriods;
    TickType_t xModifiableIdleTime;
    LPTMR_Type *pxLptmrBase;
    pxLptmrBase = LPTMR0;
    if (pxLptmrBase == 0)
        return;
    /* Make sure the SysTick reload value does not overflow the counter. */
    if (xExpectedIdleTime > xMaximumPossibleSuppressedTicks)
    {
        xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
    }
    if (xExpectedIdleTime == 0)
        return;
    /* Calculate the reload value required to wait xExpectedIdleTime
    tick periods.  -1 is used because this code will execute part way
    through one of the tick periods. */
    ulReloadValue = LPTMR_GetCurrentTimerCount(pxLptmrBase) + (ulLPTimerCountsForOneTick * (xExpectedIdleTime - 1UL));
    /* Stop the LPTMR and systick momentarily.  The time the LPTMR and systick is stopped for
    is accounted for as best it can be, but using the tickless mode will
    inevitably result in some tiny drift of the time maintained by the
    kernel with respect to calendar time. */
    LPTMR_StopTimer(pxLptmrBase);
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    /* Enter a critical section but don't use the taskENTER_CRITICAL()
    method as that will mask interrupts that should exit sleep mode. */
    __disable_irq();
    __DSB();
    __ISB();
    /* If a context switch is pending or a task is waiting for the scheduler
    to be unsuspended then abandon the low power entry. */
    if (eTaskConfirmSleepModeStatus() == eAbortSleep)
    {
        /* Restart from whatever is left in the count register to complete
        this tick period. */
        SysTick->LOAD = SysTick->VAL;
        /* Restart SysTick. */
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
        /* Reset the reload register to the value required for normal tick
        periods. */
        SysTick->LOAD = ulTimerCountsForOneTick - 1UL;
        /* Re-enable interrupts - see comments above __disable_irq()
        call above. */
        __enable_irq();
    }
    else
    {
        /* Set the new reload value. */
        LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
        //LPTMR_SetTimerPeriod(pxLptmrBase, ulReloadValue);
        LPTMR_SetTimerPeriod(pxLptmrBase, 100);
        /* Enable LPTMR. */
        LPTMR_StartTimer(pxLptmrBase);
        /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
        set its parameter to 0 to indicate that its implementation contains
        its own wait for interrupt or wait for event instruction, and so wfi
        should not be executed again.  However, the original expected idle
        time variable must remain unmodified, so a copy is taken. */
        xModifiableIdleTime = xExpectedIdleTime;
        vOnPreSleepProcessing(xModifiableIdleTime);
        /*if (xModifiableIdleTime > 0)
        {
            __DSB();
            __WFI();
            __ISB();
        }*/
        vOnPostSleepProcessing(xExpectedIdleTime);
        ulLPTimerInterruptFired = false;
        /* Re-enable interrupts - see comments above __disable_irq()
        call above. */
        __enable_irq();
        __NOP();
        if (ulLPTimerInterruptFired)
        {
            /* The tick interrupt handler will already have pended the tick
            processing in the kernel.  As the pending tick will be
            processed as soon as this function exits, the tick value
            maintained by the tick is stepped forward by one less than the
            time spent waiting. */
            ulCompleteTickPeriods   = xExpectedIdleTime - 1UL;
            ulLPTimerInterruptFired = false;
        }
        else
        {
            /* Something other than the tick interrupt ended the sleep.
            Work out how long the sleep lasted rounded to complete tick
            periods (not the ulReload value which accounted for part
            ticks). */
            ulCompleteTickPeriods = LPTMR_GetCurrentTimerCount(pxLptmrBase) / ulLPTimerCountsForOneTick;
        }
        /* Stop LPTMR when CPU waked up then set SysTick->LOAD back to its standard
        value.  The critical section is used to ensure the tick interrupt
        can only execute once in the case that the reload register is near
        zero. */
        LPTMR_StopTimer(pxLptmrBase);
        portENTER_CRITICAL();
        {
            SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
            vTaskStepTick(ulCompleteTickPeriods);
            SysTick->LOAD = ulTimerCountsForOneTick - 1UL;
        }
        portEXIT_CRITICAL();
    }
}
Labels (1)
0 Kudos
3 Replies

909 Views
Sebastian_Del_Rio
NXP Employee
NXP Employee

Hi Matthieu, I hope you're doing well!

 

Could you please take a look at the Power Manager SDK example included in the SDK archive, in the following path:

<…\SDK_SDK_2.2.1_FRDM-KW41Z\boards\frdmkw41z\demo_apps\power_manager>

 

This example lets the device enter the different power modes, and set wake-up sources.

 

Please let me know if you need any more information.

 

Best regards,

Sebastian

0 Kudos

909 Views
paralax
Contributor I

Hi,

Thanks for your input. Most of my code came from this demo, but in the end I managed to find the errors. Indeed, I missed a couple of things:

- I missed an initialisation:

MC_SetPowerModeProtection(SMC, kSMC_AllowPowerModeAll);

- The debugger itself seems to interfere with the power mode. For me the sweet spot was to use LLS3 which provides the needed current reduction along with the LPTimer wakeup.

- The SMC Pre and Post sleep mode seems to interfere with the LPTimer interrupts configured by the suppressTicksAndSleep of the FreeRTOS demo, from which I took some code too.

Anyway, it seems to be working just fine now and I got around 100uA from what I can measure Smiley Happy

Matthieu

0 Kudos

909 Views
Sebastian_Del_Rio
NXP Employee
NXP Employee

Hi Matthieu,

It's great that you found the issue! Please let me know if you need any more information.

Take care, best regards,

Sebastian

0 Kudos