5
votes

I'm working with STM32F427 and I'd like to get cause of last reset. There is RCC clock control & status register RCC_CSR with many reset flags, but I'm not able to get any meaningful value.

By reading the value of that register, I get only 0x03, which means LSI ready and LSI ON, but no flags about reset are set if I try power on, software reset, low voltage etc. I found snippet of code for getting reset flags like below, but all the flags are still 0.

if (RCC_GetFlagStatus(RCC_FLAG_SFTRST)) ...

Do you have any suggestions how to get better results? Is there some needed configuration before reading these reset flags?

Thanks

3

3 Answers

8
votes

Read RCC_CSR as soon as possible after startup, before initializing any other peripheral. It is safe to initialize your system clocks first (which is done in SystemInit() if you use ST's libraries).

20
votes

@floppes recommends to:

Read RCC_CSR as soon as possible after startup, before initializing any other peripheral. It is safe to initialize your system clocks first (which is done in SystemInit() if you use ST's libraries).

Now, to determine the exact reset cause, here's a full function to help you.

Note that all reset flags can be found in the Reset and Clock Controller (RCC) header file for your microcontroller.

Ex: "STM32Cube_FW_F2_V1.7.0/Drivers/STM32F2xx_HAL_Driver/Inc/stm32f2xx_hal_rcc.h".

Here is a sample description of the __HAL_RCC_GET_FLAG() macro and its inputs as copied and pasted from "stm32f2xx_hal_rcc.h". All reset flags used in the function below were obtained from this list:

/** @brief Check RCC flag is set or not.
* @param FLAG specifies the flag to check.
* This parameter can be one of the following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready.
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready.
* @arg RCC_FLAG_PLLRDY: Main PLL clock ready.
* @arg RCC_FLAG_PLLI2SRDY: PLLI2S clock ready.
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready.
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready.
* @arg RCC_FLAG_BORRST: POR/PDR or BOR reset.
* @arg RCC_FLAG_PINRST: Pin reset.
* @arg RCC_FLAG_PORRST: POR/PDR reset.
* @arg RCC_FLAG_SFTRST: Software reset.
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset.
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset.
* @arg RCC_FLAG_LPWRRST: Low Power reset.
* @retval The new state of FLAG (TRUE or FALSE).
*/
#define RCC_FLAG_MASK ((uint8_t)0x1FU)
#define __HAL_RCC_GET_FLAG(__FLAG__) (((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & ((uint32_t)1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U)? 1U : 0U)

Functions to get and be able to print the STM32 system reset cause:

/// @brief  Possible STM32 system reset causes
typedef enum reset_cause_e
{
    RESET_CAUSE_UNKNOWN = 0,
    RESET_CAUSE_LOW_POWER_RESET,
    RESET_CAUSE_WINDOW_WATCHDOG_RESET,
    RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET,
    RESET_CAUSE_SOFTWARE_RESET,
    RESET_CAUSE_POWER_ON_POWER_DOWN_RESET,
    RESET_CAUSE_EXTERNAL_RESET_PIN_RESET,
    RESET_CAUSE_BROWNOUT_RESET,
} reset_cause_t;

/// @brief      Obtain the STM32 system reset cause
/// @param      None
/// @return     The system reset cause
reset_cause_t reset_cause_get(void)
{
    reset_cause_t reset_cause;

    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST))
    {
        reset_cause = RESET_CAUSE_LOW_POWER_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST))
    {
        reset_cause = RESET_CAUSE_WINDOW_WATCHDOG_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST))
    {
        reset_cause = RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST))
    {
        reset_cause = RESET_CAUSE_SOFTWARE_RESET; // This reset is induced by calling the ARM CMSIS `NVIC_SystemReset()` function!
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST))
    {
        reset_cause = RESET_CAUSE_POWER_ON_POWER_DOWN_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST))
    {
        reset_cause = RESET_CAUSE_EXTERNAL_RESET_PIN_RESET;
    }
    // Needs to come *after* checking the `RCC_FLAG_PORRST` flag in order to ensure first that the reset cause is 
    // NOT a POR/PDR reset. See note below. 
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST))
    {
        reset_cause = RESET_CAUSE_BROWNOUT_RESET;
    }
    else
    {
        reset_cause = RESET_CAUSE_UNKNOWN;
    }

    // Clear all the reset flags or else they will remain set during future resets until system power is fully removed.
    __HAL_RCC_CLEAR_RESET_FLAGS();

    return reset_cause; 
}

// Note: any of the STM32 Hardware Abstraction Layer (HAL) Reset and Clock Controller (RCC) header
// files, such as "STM32Cube_FW_F7_V1.12.0/Drivers/STM32F7xx_HAL_Driver/Inc/stm32f7xx_hal_rcc.h",
// "STM32Cube_FW_F2_V1.7.0/Drivers/STM32F2xx_HAL_Driver/Inc/stm32f2xx_hal_rcc.h", etc., indicate that the 
// brownout flag, `RCC_FLAG_BORRST`, will be set in the event of a "POR/PDR or BOR reset". This means that a 
// Power-On Reset (POR), Power-Down Reset (PDR), OR Brownout Reset (BOR) will trip this flag. See the 
// doxygen just above their definition for the `__HAL_RCC_GET_FLAG()` macro to see this:
// "@arg RCC_FLAG_BORRST: POR/PDR or BOR reset." <== indicates the Brownout Reset flag will *also* be set in 
// the event of a POR/PDR. 
// Therefore, you must check the Brownout Reset flag, `RCC_FLAG_BORRST`, *after* first checking the 
// `RCC_FLAG_PORRST` flag in order to ensure first that the reset cause is NOT a POR/PDR reset.


/// @brief      Obtain the system reset cause as an ASCII-printable name string from a reset cause type
/// @param[in]  reset_cause     The previously-obtained system reset cause
/// @return     A null-terminated ASCII name string describing the system reset cause
const char * reset_cause_get_name(reset_cause_t reset_cause)
{
    const char * reset_cause_name = "TBD";

    switch (reset_cause)
    {
        case RESET_CAUSE_UNKNOWN:
            reset_cause_name = "UNKNOWN";
            break;
        case RESET_CAUSE_LOW_POWER_RESET:
            reset_cause_name = "LOW_POWER_RESET";
            break;
        case RESET_CAUSE_WINDOW_WATCHDOG_RESET:
            reset_cause_name = "WINDOW_WATCHDOG_RESET";
            break;
        case RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET:
            reset_cause_name = "INDEPENDENT_WATCHDOG_RESET";
            break;
        case RESET_CAUSE_SOFTWARE_RESET:
            reset_cause_name = "SOFTWARE_RESET";
            break;
        case RESET_CAUSE_POWER_ON_POWER_DOWN_RESET:
            reset_cause_name = "POWER-ON_RESET (POR) / POWER-DOWN_RESET (PDR)";
            break;
        case RESET_CAUSE_EXTERNAL_RESET_PIN_RESET:
            reset_cause_name = "EXTERNAL_RESET_PIN_RESET";
            break;
        case RESET_CAUSE_BROWNOUT_RESET:
            reset_cause_name = "BROWNOUT_RESET (BOR)";
            break;
    }

    return reset_cause_name;
}

Example usage:

reset_cause_t reset_cause = reset_cause_get();
printf("The system reset cause is \"%s\"\n", reset_cause_get_name(reset_cause));

Output:

The system reset cause is "INDEPENDENT_WATCHDOG_RESET"

Of course, convert the return value of the function above to be an enum rather than a C string if you intend to do much with it other than just print it out.

Edit 26 Oct. 2019: just added enums and separate function to obtain printable string from a reset type! This saves a bunch of flash space now by not having to store all of those reset string names in your program space if you just need the enum reset type and never have to print the name! I also added doxygen headers for the functions because it's always nice to see good examples of basic code documentation.

3
votes
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST))...

Will tell you if there was a software reset.

And then don't forget to

RCC_ClearFlag();