STM32如何获取上次复位状态

STM32 how to get last reset status

我正在使用 STM32F427,我想了解上次重置的原因。 RCC 时钟控制和状态寄存器 RCC_CSR 有许多复位标志,但我无法获得任何有意义的值。

通过读取该寄存器的值,我只得到 0x03,这意味着 LSI 准备就绪并且 LSI ON,但是如果我尝试开机、软件复位、低电压等,则不会设置有关复位的标志。我发现了片段获取重置标志的代码如下所示,但所有标志仍为 0。

if (RCC_GetFlagStatus(RCC_FLAG_SFTRST)) ...

您对如何获得更好的结果有什么建议吗?在读取这些重置标志之前是否需要一些配置?

谢谢

if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST))...

会告诉您是否有软件重置。

然后别忘了

RCC_ClearFlag();

启动后尽快阅读RCC_CSR,然后再初始化任何其他外围设备。首先初始化系统时钟是安全的(如果您使用 ST 的库,则在 SystemInit() 中完成)。

@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).

现在,要确定确切的重置原因,这里有一个完整的函数可以帮助您。

请注意,所有复位标志都可以在微控制器的复位和时钟控制器 (RCC) header 文件中找到。

例如:“STM32Cube_FW_F2_V1.7.0/Drivers/STM32F2xx_HAL_Driver/Inc/stm32f2xx_hal_rcc.h”.

这是从“stm32f2xx_hal_rcc.h”复制和粘贴的 __HAL_RCC_GET_FLAG() 宏及其输入的示例描述。全部 以下函数中使用的重置标志是从此列表中获得的:

/** @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)`  

获取并能够打印STM32系统复位原因的函数:

/// @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))
    {
        // This reset is induced by calling the ARM CMSIS 
        // `NVIC_SystemReset()` function!
        reset_cause = RESET_CAUSE_SOFTWARE_RESET; 
    }
    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;
}

用法示例:

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

输出:

The system reset cause is "INDEPENDENT_WATCHDOG_RESET"

当然,如果您打算用它做很多事情,而不仅仅是将上面函数的 return 值转换为枚举而不是 C 字符串打印出来。

编辑 2019 年 10 月 26 日: 刚刚添加了枚举和单独的函数以从重置类型中获取可打印的字符串!如果您只需要枚举重置类型并且永远不必打印名称,那么现在不必在程序中存储所有这些重置字符串名称 space,从而节省了一堆闪存 space!我还为函数添加了 doxygen headers,因为看到基本代码文档的好例子总是很高兴。