Summary:
On a custom board using the KW45Z41083, a wake up via GPIO pin through the WUU appears to have a fairly long clock recovery time when the CMC clock mode selected was kCMC_GateAllSystemClocksEnterLowPowerMode. How do I reliably wait for all clocks to settle on wake up before allowing the application to run again?
Details:
Clock configuration -
-- External 26 MHz crystal used for 96 MHz internal system clock.
-- Internal FRO-32K clock enabled in CCM32K module to clock the WUU module during sleep.
-- LPUART0 clocked from FRO-192M via the MRCC module.
Attached is a stripped down version of my app that demonstrates the issue. On boot, if a BUTTON GPIO input is active, the example sits in a loop sending ASCII 'U' (0x55) at 115200 baud to LPUART0. At 115200, each bit in the UART transmit should be 1/115200 = 8.68 usec wide and I confirmed this with a scope.
void DebugPortTransmit(uint8_t data)
{
while (0 == (LPUART_GetStatusFlags(LPUART0) & kLPUART_TxDataRegEmptyFlag))
;
LPUART_WriteByte(LPUART0, data);
}
int main(void)
{
...
while (GPIO_PinRead(BOARD_INITPINS_BUTTON_GPIO, BOARD_INITPINS_BUTTON_PIN) != 0)
DebugPortTransmit('U');
}
When the BUTTON input deactivates, the example stops transmitting on the UART and goes to Deep Sleep with all system clocks off. The WUU is configured to wake the CPU on edge interrupt from BUTTON.
void WUU0_IRQHandler(void)
{
if (WUU_GetExternalWakeUpPinsFlag(WUU0) & WUU_PIN_FLAGS_BUTTON)
{
WUU_ClearExternalWakeUpPinsFlag(WUU0, WUU_PIN_FLAGS_BUTTON);
}
DisableIRQ(WUU0_IRQn);
SDK_ISR_EXIT_BARRIER;
}
int main(void)
{
...
while (1)
{
// While BUTTON input active, send a continuous stream of ASCII 'U' characters to LPUART0.
while (GPIO_PinRead(BOARD_INITPINS_BUTTON_GPIO, BOARD_INITPINS_BUTTON_PIN) != 0)
DebugPortTransmit('U');
// BUTTON in inactive now, set up WUU to wake from Deep Sleep via BUTTON input.
WUU_ClearExternalWakeUpPinsFlag(WUU0, WUU_PIN_FLAGS_BUTTON);
EnableIRQ(WUU0_IRQn);
wuu_external_wakeup_pin_config_t wuu_gpio_config;
wuu_gpio_config.edge = kWUU_ExternalPinAnyEdge;
wuu_gpio_config.event = kWUU_ExternalPinInterrupt;
wuu_gpio_config.mode = kWUU_ExternalPinActiveAlways;
WUU_SetExternalWakeUpPinsConfig(WUU0, WUU_IDX_BUTTON, &wuu_gpio_config);
// Go to Deep Sleep.
SPC_EnableLowPowerModeCoreVDDInternalVoltageScaling(SPC0, true);
cmc_power_domain_config_t config;
config.clock_mode = kCMC_GateAllSystemClocksEnterLowPowerMode;
config.main_domain = kCMC_DeepSleepMode;
config.wake_domain = kCMC_DeepSleepMode;
CMC_EnterLowPowerMode(CMC0, &config);
// If we get here BUTTON is active again and we woke up.
}
}
When BUTTON activates in Deep Sleep, the WUU wakes the CPU and the main loop goes back to its loop transmitting 'U' characters. This is when I start noticing problems. For about 1 msec after wake up, the FRO-192M clock to the LPUART0 doesn't seem to have recovered. The bit width in the transmitted 'U' bytes are wider than 11 usec when they should be exactly 8.68 usec. After about 1 msec they return to 8.68 usec.
I also get frequent lock ups where the UART status seems wrong and my loop waiting for kLPUART_TxDataRegEmptyFlag never exits.
In my real application, I get all sorts of other side-effects in other modules such as FlexCAN, etc.
I have reviewed the SDK examples kw45b41zevk_lowpower_central_bm, kw45b41zevk_lowpower_peripheral_bm, and kw45b41zevk_power_mode_switch_k4 extensively looking for suggestions on what to do when leaving Deep Sleep mode but I didn't find anything (or didn't understand what I was reading as the KW45 power-related hardware is very complicated).
What am I missing? What is the recommended procedure for waiting for the clocks to recover before returning to normal application code?
Surely, the answer isn't to put a 1 msec spin loop in the code right after wake up. My real application requires Wake-on-CAN capability, so a 1 msec delay would cause many CAN bus errors before the application is ready for CAN processing.