干货:手把手指导单片机复位原因分析

我是轻松学长,一个爱折腾的程序袁,仰望星空、脚踏实地,做一个有趣有料的技术人。

本节解决问题:软件代码识别STM32复位原因,辅助代码调试。

当STM32发生复位时,可能原因有上电复位、掉电复位、看门狗复位、软件复位等多种,那怎么判断STM32复位的原因呢?且看轻松学长慢慢道来。

1、STM32 复位类型

STM32有三种复位:系统复位电源复位后备域复位

1.1 系统复位

指除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器外,将其他的所有寄存器复位为它们的复位数值。系统复位可通过查看RCC_CSR控制状态寄存器中的复位状态标志位识别复位事件来源,这就是今天的重点。

关于备份区域的理解可看下图:

image

有以下事件发生时,会产生一个系统复位:

  • 软件复位(SW复位)
  • 低功耗管理复位
  • NRST引脚上的低电平(外部复位)
  • 窗口看门狗计数终止(WWDG复位)
  • 独立看门狗计数终止(IWDG复位)

看门狗复位和外部复位好理解,那软件复位和低功耗管理复位怎么理解呢,何时会发生复位?

软件复位即通过将Cortex™-M3中断应用和复位控制寄存器中的SYSRESETREQ位置’1’,实现软件复位。STM32官方已经将软件复位过程给封装好了,即 NVIC_SystemReset() 函数,NVIC_SystemReset()函数的内容如下:

/**
  \brief   System Reset
  \details Initiates a system reset request to reset the MCU.
 */
__STATIC_INLINE void NVIC_SystemReset(void)
{
  __DSB();                                                          /* Ensure all outstanding memory accesses included
                                                                       buffered write are completed before reset */
  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                            SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
  __DSB();                                                          /* Ensure completion of memory access */

  for(;;)                                                           /* wait until reset */
  {
    __NOP();
  }
}

使用软件复位NVIC_SystemReset()函数时,需在该函数之前加上__set_FAULTMASK(1)语句,表示关闭所有中断的意思;

因为在《Cortex-M3权威指南》中有这么一句话提醒我们:从 SYSRESETREQ 被置为有效,到复位发生器执行复位令,往往会有一个延时。在此延时期间,处理器仍然可以响应中断请求。但我们的本意往往是要让此次执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把 FAULTMASK 置位,即关闭所有中断。

低功耗管理复位:在以下两种情况下可产生低功耗管理复位:

  • 1.在进入待机模式时产生低功耗管理复位:通过将用户选择字节中的nRST_STDBY位置’1’将使能该复位。这时,即使执行了进入待机模式的过程,系统将被复位而不是进入待机模式。

  • 2.在进入停止模式时产生低功耗管理复位:通过将用户选择字节中的nRST_STOP位置’1’将使能该复位。这时,即使执行了进入停机模式的过程,系统将被复位而不是进入停机模式。

1.2 电源复位

电源复位将复位除了备份区域外的所有寄存器,当发生以下事件之一时,会产生电源复位:

  • 上电/掉电复位(POR/PDR复位)
  • 从待机模式中返回

电源复位的复位源将最终作用于RESET引脚,并在复位过程中保持低电平。

芯片内部的复位信号会在NRST引脚上输出,脉冲发生器保证每一个(外部或内部)复位源都能有至少20μs的脉冲延时;当NRST引脚被拉低产生外部复位时,它将产生复位脉冲。

STM32芯片内部的复位电路如下图所示:

image

1.3 备份域复位

当以下事件发生之一时,会产生备份区域复位,备份区域复位只影响备份区域。

  • 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)中的BDRST位产生。
  • 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。

2、软件判断复位原因

方法:通过查看控制/状态寄存器(RCC_CSR)中的复位状态标志位识别复位事件来源。

先看一下stm32中文参考手册对RCC_CSR寄存器的描述:

image
image

stm32对CSR寄存器各个位的宏封装:

/* Flags in the CSR register */
#define RCC_FLAG_LSIRDY                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSIRDY)))   /*!< Internal Low Speed oscillator Ready */
#define RCC_FLAG_LSECSS                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSECSSD)))  /*!< CSS on LSE failure Detection */
#define RCC_FLAG_OBLRST                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_OBLRSTF)))  /*!< Options bytes loading reset flag */
#define RCC_FLAG_PINRST                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_PINRSTF)))  /*!< PIN reset flag */
#define RCC_FLAG_PORRST                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_PORRSTF)))  /*!< POR/PDR reset flag */
#define RCC_FLAG_SFTRST                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_SFTRSTF)))  /*!< Software Reset flag */
#define RCC_FLAG_IWDGRST                 ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_IWDGRSTF))) /*!< Independent Watchdog reset flag */
#define RCC_FLAG_WWDGRST                 ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_WWDGRSTF))) /*!< Window watchdog reset flag */
#define RCC_FLAG_LPWRRST                 ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LPWRRSTF))) /*!< Low-Power reset flag */
#define RCC_FLAG_LSERDY                  ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSERDY))) /*!< External Low Speed oscillator Ready */

stm32获取复位标志的宏:__HAL_RCC_GET_FLAG(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 @ref RCC_FLAG_HSIRDY HSI oscillator clock ready.
  *            @arg @ref RCC_FLAG_MSIRDY MSI oscillator clock ready.
  *            @arg @ref RCC_FLAG_HSERDY HSE oscillator clock ready.
  *            @arg @ref RCC_FLAG_PLLRDY Main PLL clock ready.
  *            @arg @ref RCC_FLAG_LSERDY LSE oscillator clock ready.
  *            @arg @ref RCC_FLAG_LSECSS CSS on LSE failure Detection (*)
  *            @arg @ref RCC_FLAG_LSIRDY LSI oscillator clock ready.
  *            @arg @ref RCC_FLAG_OBLRST Option Byte Load reset
  *            @arg @ref RCC_FLAG_PINRST  Pin reset.
  *            @arg @ref RCC_FLAG_PORRST  POR/PDR reset.
  *            @arg @ref RCC_FLAG_SFTRST  Software reset.
  *            @arg @ref RCC_FLAG_IWDGRST Independent Watchdog reset.
  *            @arg @ref RCC_FLAG_WWDGRST Window Watchdog reset.
  *            @arg @ref RCC_FLAG_LPWRRST Low Power reset.
  * @note (*) This bit is available in high and medium+ density devices only.
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_RCC_GET_FLAG(__FLAG__) (((((__FLAG__) >> 5U) == CR_REG_INDEX)? RCC->CR :RCC->CSR) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))   

stm32清除复位标志的宏:__HAL_RCC_CLEAR_RESET_FLAGS()

/** @brief Set RMVF bit to clear the reset flags.
  *         The reset flags are RCC_FLAG_PINRST, RCC_FLAG_PORRST, RCC_FLAG_SFTRST,
  *         RCC_FLAG_IWDGRST, RCC_FLAG_WWDGRST, RCC_FLAG_LPWRRST
  */
#define __HAL_RCC_CLEAR_RESET_FLAGS() (RCC->CSR |= RCC_CSR_RMVF)

2.1 代码思路

看了对上面对控制/状态寄存器(RCC_CSR)的描述,代码的思路就已经很明显了,通过RCC_CSR寄存器中的复位状态标志位获取复位事件来源,应用层做复位标志位,最后清除复位标志。

  • 定义复位类型枚举
/* Reset Flag Status */
typedef enum
{
    RCC_RESET_FLAG_NONE                   = 0x00,                           /*!< None Reset Flag                */
    RCC_RESET_FLAG_IWDGRST              = 0x01,                         /*!< Independent Watchdog Reset Flag    */
    RCC_RESET_FLAG_SFTRST                = 0x02,                            /*!< Software Reset Flag                */
    RCC_RESET_FLAG_PORRST                = 0x03,                            /*!< POR/PDR Reset Flag             */
    RCC_RESET_FLAG_PINRST                = 0x04,                            /*!< PIN Reset Flag                 */
    RCC_RESET_FLAG_LPWRRST              = 0x05,                         /*!< Low-Power Reset Flag           */
    RCC_RESET_FLAG_OBLRST                = 0x06,                            /*!< Options Bytes Loading Reset Flag   */
    RCC_RESET_FLAG_WWDGRST              = 0x07                          /*!< Window Watchdog Reset Flag     */
}RCC_RESET_FLAG_TypeDef;
  • 获取STM32复位类型
RCC_RESET_FLAG_TypeDef RCC_ResetFlag_GetStatus(void)
{
    RCC_RESET_FLAG_TypeDef ResetStatusFlag = RCC_RESET_FLAG_NONE;
    
    if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) 
    {   
        ResetStatusFlag = RCC_RESET_FLAG_IWDGRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) != RESET) 
    {
        ResetStatusFlag = RCC_RESET_FLAG_SFTRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET) 
    {
        ResetStatusFlag = RCC_RESET_FLAG_PORRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET) 
    {
        ResetStatusFlag = RCC_RESET_FLAG_PINRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST) != RESET) 
    {
        ResetStatusFlag = RCC_RESET_FLAG_LPWRRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_OBLRST) != RESET) 
    {
        ResetStatusFlag = RCC_RESET_FLAG_OBLRST;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
    {
        ResetStatusFlag = RCC_RESET_FLAG_WWDGRST;
    }

    __HAL_RCC_CLEAR_RESET_FLAGS();
    
    return ResetStatusFlag;
}

到这里就结束啦,这只是应用在STM32单片机上的复位类型判断思路及案例。其他的单片机一样的思路,也可以实现同样的问题,注意查看其官方手册对相应寄存器的描述即可。

任何芯片的官方手册都是一笔宝藏,待你去挖掘。

你可能感兴趣的:(干货:手把手指导单片机复位原因分析)