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.
Bump. Trying again to get some help on this unresolved issue.
Quick summary - On a wake up from Deep Sleep via GPIO, I have had to hack in a 1 msec delay loop into my production code to wait for the FRO-192M clock to settle and give accurate timings to a UART based on that clock. I want to remove that delay loop from my code.
Is there some SDK function I can call to wait for the FRO-192M clock to be ready for use after wake up?
I received an email from NXP asking to mark this accepted but I haven't received any response from NXP to my last posting, so this is still unresolved.
Bump. I still need help from NXP on this.
Hello,
Hope you are doing well. For a CAN application, you would need to use some functions before entering Deep Sleep Mode. For example, the App_PrepareCANRXWakeupPinToLowPower.
Also, you need to use some functions, after exiting deep sleep mode.
Check the SPC_CheckPowerDomainLowPowerRequest for the specific power domain and clear the respective flag using SPC_ClearPowerDomainLowPowerRequestFlag. Also, do a clear of all low power request with SPC_ClearLowPowerRequest.
Also, if you are planning to use BLE, I would recommend checking the "Bluetooth Low Energy Application Developer’s Guide".
Best Regards,
Ricardo
The example I attached doesn't do anything with CAN or BLE, so let's not discuss that yet. I'm simply trying to wake from Deep Sleep via GPIO and send some characters out a UART to verify UART bit timings (and thus the FIRC clock source) are accurate after wake up.
My example doesn't request any changes to the power domains via SPC_SetExternalVoltageDomainsConfig(), so it was my understanding that I shouldn't have to add calls to SPC_ClearPowerDomainLowPowerRequestFlag() or SPC_ClearLowPowerRequest() after wake up.
If I add this sequence right after CMC_EnterLowPowerMode() returns ...
if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain0)) SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain0); if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain1)) SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain1); if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain2)) SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain2); SPC_ClearLowPowerRequest(SPC0);
... nothing changes - the bit timings out the UART are still wrong for about 1 msec after wake up.
If I also add this call before going to sleep ...
SPC_SetExternalVoltageDomainsConfig(SPC0, 0x6U, 0x0U);
... then when the GPIO wakes up the CPU, I get a full reboot of the CPU instead of a wake from sleep. The reason for the reset reported by CMC0->SRS is 0x00000110 (PIN and WARM bits on).
One other thing I tried was to add this loop after waking up ...
while (! CLOCK_IsFircValid()) ;
... but this also had no effect on the outcome because CLOCK_IsFircValid() returns true on the first call, even when the UART is still sending out bits with the incorrect bit widths.
Can you try downloading my example and reproducing this for yourself?