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