Shutdown and wake (RTC / WKUP) work in debug / run but NOT in standalone after power loss
I have developed code (on the NUCLEO-L433RC-P) that uses shutdown and wake.
The code works when I debug or run, but it stops working after a cold restart (power removed then reconnected).
After a cold restart, the software runs correctly until the first shutdown is called. Once shutdown is called, it seems to crash and I have to perform a reset.
From power up, the MCU should run as follows...
- Run for 10 seconds
- Shutdown for 5 seconds
- Run for 10 seconds
- Shutdown for 5 seconds
- And so on.....
- This cycle should continue until 60 seconds is reached, when it should shutdown and stay shutdown.
- It should not wake again until the WKUP pin is pressed
Why does it seem to crash after the first shutdown (step 2 above) ?
I am aware that RTC backup registers are lost when power is removed, but I cannot see this causing the problem.
I have attached a flowchart diagram (wake_shutdown.png) to show how my code works.
The relevant code is also below
- main
- MX_RTC_Init
- wakeConfigReset
- wakeConfigUpdate
- mcuSleep
- mcuPowerdown
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_DAC1_Init();
MX_TIM6_Init();
MX_SPI2_Init();
MX_LPUART1_UART_Init();
MX_SPI1_Init();
MX_I2C1_Init();
MX_USART3_UART_Init();
MX_ADC1_Init();
MX_TIM7_Init();
MX_RTC_Init();
if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB)) {
// woken from low power mode
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
// read wake marker from RTC backup register to determine if woken from sleep or shutdown
wake_marker = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
if (wake_marker == WAKE_FROM_SLEEP_RTC) {
// woken from sleep (RTC wake)
strcpy(prtText, "RTC wake \r\n");
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, strlen(prtText) - 1, HAL_MAX_DELAY);
// Use sizeof(newline) - 1 to avoid sending null terminator
wakeConfigUpdate(); // get time and date and restore reference pressure from RTC backup register
uint32_t tmp = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3); // restore power switch state from RTC backup register
powerSeq = *(uint8_t*)&tmp;
// check if elapsed time is greater then shutdown time
if (elapsedSeconds >= RTC_SHUTDOWN_S) {
strcpy(prtText, "Shutdown \r\n");
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, strlen(prtText) - 1, HAL_MAX_DELAY);
mcuPowerdown();
}
} else if (wake_marker == WAKE_FROM_SHUTDOWN_WKUP) {
// woken from shutdown (power switch pressed during shutdown)
strcpy(prtText, "WKUP pin wake \r\n");
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, strlen(prtText) - 1, HAL_MAX_DELAY);
wakeConfigReset(); // reset time and date and set default reference pressure
}
}
else
{
// woken from reset
strcpy(prtText, "Reset wake \r\n");
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, strlen(prtText) - 1, HAL_MAX_DELAY);
wakeConfigReset(); // reset time and date and set default reference pressure
// store value in RTC backup register to indicate mcu was reset
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, WAKE_FROM_RESET);
}
len = sprintf(prtText, "wake_marker = 0x%0lX\r\n", wake_marker);
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, len, HAL_MAX_DELAY);
len = sprintf(prtText, "startSeconds = %lu\r\n", startSeconds);
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, len, HAL_MAX_DELAY);
len = sprintf(prtText, "currentSeconds = %lu\r\n", currentSeconds);
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, len, HAL_MAX_DELAY);
len = sprintf(prtText, "elapsedSeconds = %lu\r\n", elapsedSeconds);
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, len, HAL_MAX_DELAY);
strcpy(prtText, "\r\n");
HAL_UART_Transmit(&hlpuart1, (uint8_t*)prtText, strlen(prtText), HAL_MAX_DELAY);
while (1) {
}
}
static void MX_RTC_Init(void)
{
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
}
void wakeConfigReset (void) {
// set reference pressure to default value
pressure_ref = REF_PRESS_DEFAULT;
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x1;
sDate.Year = 0x0;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
// get time and date
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// convert start time and date to unix time
startSeconds = RTC_to_unix(&sTime, &sDate);
// store start time in RTC backup register
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, startSeconds);
}
void wakeConfigUpdate (void) {
// Restore pressure reference after wake
uint32_t tmp = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);
pressure_ref = *(float*)&tmp;
// get time and date
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// convert time and date to unix time
currentSeconds = RTC_to_unix(&sTime, &sDate);
// read unix start time from RTC backup register
startSeconds = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
// calculate elapsed time
elapsedSeconds = currentSeconds - startSeconds;
}
void mcuSleep (void){
/* The Following Wakeup sequence is highly recommended prior to each Standby mode entry
mainly when using more than one wakeup source this is to not miss any wakeup event.
- Disable all used wakeup sources,
- Clear all related wakeup flags,
- Re-enable all used wakeup sources,
- Enter the shutdown mode.
*/
// write pressure to RTC backup register
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, *(uint32_t*)&pressure_bak);
// write value to RTC backup register to indicate mcu was in sleep
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, WAKE_FROM_SLEEP_RTC);
// write power switch state to RTC backup register
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, *(uint32_t*)&powerSeq);
// Disable all used wakeup sources
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, RTC_WAKE_SEC, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
// Clear all related wakeup flags
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// Call sleep (note that the terms 'sleep' and 'powerdown' are only used for readability, both call shutdown mode)
HAL_PWREx_EnterSHUTDOWNMode();
}
void mcuPowerdown (void){
// write value to RTC backup register to indicate mcu was in shutdown
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, WAKE_FROM_SHUTDOWN_WKUP);
// disable RTC wakeup
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
Enable_PC13_Wakeup();
// Call power down (note that the terms 'sleep' and 'powerdown' are only used for readability, both call shutdown mode)
HAL_PWREx_EnterSHUTDOWNMode();
}
UPDATE 1:
The clock was configured to use LSI (low speed internal), so I changed it to LSE (low speed external) and it has fixed the RTC wake from shutdown after a cold restart.
The ref manual states...
"The RTC is functional in VBAT mode and in all low-power modes when it is clocked by the LSE. When clocked by the LSI, the RTC is not functional in VBAT mode, but is functional in all low-power modes except Shutdown mode."
Everything works correctly in debug/run mode, but when there is a cold reset (power loss) the shutdown after 60 seconds does not work. It just cycles through 10 second wake, 5 second sleep etc.
UPDATE 2:
I modified the code in main (including the version in this post) to send the data over UART for debugging.
I send the values of startSeconds, currentSeconds, elapsedSeconds and whether the wakeup source was RTC, WKUP pin or reset.
I can see after a cold reset that the RTC backup is always zero, so it always thinks its waking up from reset.
Why does the RTC back work in debug/run mode, but not in standalone after a cold reset ?
In debug/run mode the terminal result is:

After cold reset the terminal result is:

