本節解決問題:軟件代碼識別STM32復位原因,輔助代碼調試。
當STM32發生復位時,可能原因有上電復位、掉電復位、看門狗復位、軟件復位等多種,那怎么判斷STM32復位的原因呢?且看輕松學長慢慢道來。
1、STM32 復位類型
STM32有三種復位:系統復位、電源復位和后備域復位。
1.1 系統復位
指除時鍾控制寄存器CSR中的復位標志和備份區域中的寄存器外,將其他的所有寄存器復位為它們的復位數值。系統復位可通過查看RCC_CSR控制狀態寄存器中的復位狀態標志位識別復位事件來源,這就是今天的重點。
關於備份區域的理解可看下圖:
有以下事件發生時,會產生一個系統復位:
- 軟件復位(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芯片內部的復位電路如下圖所示:
1.3 備份域復位
當以下事件發生之一時,會產生備份區域復位,備份區域復位只影響備份區域。
- 軟件復位,備份區域復位可由設置備份域控制寄存器 (RCC_BDCR)中的BDRST位產生。
- 在VDD和VBAT兩者掉電的前提下,VDD或VBAT上電將引發備份區域復位。
2、軟件判斷復位原因
方法:通過查看控制/狀態寄存器(RCC_CSR)中的復位狀態標志位識別復位事件來源。
先看一下stm32中文參考手冊對RCC_CSR寄存器的描述:
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單片機上的復位類型判斷思路及案例。其他的單片機一樣的思路,也可以實現同樣的問題,注意查看其官方手冊對相應寄存器的描述即可。