一、WWDG簡介
看門狗其實就是一個定時器,從功能上說它可以讓微控制器在程序發生意外(程序進入死循環或跑飛)的時候,能重新回覆到系統剛上電狀態,以保障系統出問題的時候可以重啓一次。說的複雜一點,看門狗就是能讓程序出問題是能重新啓動系統。
STM32 有兩個看門狗,一個是獨立看門狗,一個是窗口看門狗。我們知道獨立看門狗的工作原理就是一個遞減計數器不斷的往下遞減計數,當減到 0 之前如果沒有餵狗的話,產生復位。窗口看門狗跟獨立看門狗一樣,也是一個遞減計數器不斷的往下遞減計數,當減到一個固定值 0X40 時還不餵狗的話,產生復位,這個值叫窗口的下限,是固定的值,不能改變。這個是跟獨立看門狗類似的地方,不同的地方是窗口看門狗的計數器的值在減到某一個數之前餵狗的話也會產生復位,這個值叫窗口的上限,上限值由用戶獨立設置。窗口看門狗計數器的值必須在上窗口和下窗口之間纔可以餵狗,這就是窗口看門狗中窗口兩個字的含義。
二、WWDG應用場景
**WWDG 一般被用來監測,由外部干擾或不可預見的邏輯條件造成的應用程序背離正常的運行序列而產生的軟件故障。**比如一個程序段正常運行的時間是 50ms,在運行完這個段程序之後緊接着進行餵狗,如果在規定的時間窗口內還沒有餵狗,那就說明我們監控的程序出故障了,跑飛了,那麼就會產生系統復位,讓程序重新運行。
三、新建工程
1. 打開 STM32CubeMX 軟件,點擊“新建工程”
2. 選擇 MCU 和封裝
3. 配置時鐘
RCC 設置,選擇 HSE(外部高速時鐘) 爲 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
選擇 Clock Configuration,配置系統時鐘 SYSCLK 爲 72MHz
修改 HCLK 的值爲 72 後,輸入回車,軟件會自動修改所有配置
4. 配置調試模式
非常重要的一步,否則會造成第一次燒錄程序後續無法識別調試器
SYS 設置,選擇 Debug 爲 Serial Wire
四、WWDG
4.1 參數配置
在 System Core
中選擇 WWDG
設置,並勾選 Activated
激活
WWDG counter clock prescaler
預分頻器值設爲 8
WWDG window value
上窗口值設爲 90
WWDG free-running downcounter value
計數器值設爲 127
超時時間 Twwdg = Tpclk1 x 4096 x 2^wdgtb x (T[5:0] + 1) (ms)
由圖知 Tpclk1 爲 36 MHz,當 prv 取 WWDG_Prescaler_8,即wdgtb爲3
即最小超時值爲 910us,最大超時值爲 58.25ms。
1
是計數器的初始值2
是我們設置的上窗口值3
是下窗口值(0x3F)
窗口看門狗計數器的值只有在2
和3
之間(上窗口和下窗口之間)纔可以餵狗
看門狗中斷 Early wakeup interrupt
提前喚醒中斷,選擇 Enable
使能
4.2 配置NVIC
使能WWDG中斷
4.3 生成代碼
輸入項目名和項目路徑
選擇應用的 IDE 開發環境 MDK-ARM V5
每個外設生成獨立的 ’.c/.h’
文件
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應的外設文件。 如 GPIO 初始化代碼生成在 gpio.c 中。
點擊 GENERATE CODE 生成代碼
4.4 修改中斷回調函數
打開 stm32f1xx_it.c
中斷服務函數文件,找到 WWDG 中斷的服務函數 WWDG_IRQHandler()
中斷服務函數裏面就調用了串口中斷處理函數 HAL_WWDG_IRQHandler()
打開 stm32f1xx_hal_wwdg.c
文件,找到窗口看門狗中斷處理函數原型 HAL_WWDG_IRQHandler()
,其主要作用就是判斷產生中斷,清除中斷標識位,然後調用中斷回調函數 HAL_WWDG_EarlyWakeupCallback()
。
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
這個函數不應該被改變,如果需要使用回調函數,請重新在用戶文件中實現該函數。
HAL_WWDG_EarlyWakeupCallback()
按照官方提示我們應該再次定義該函數,__weak
是一個弱化標識,帶有這個的函數就是一個弱化函數,就是你可以在其他地方寫一個名稱和參數都一模一樣的函數,編譯器就會忽略這一個函數,而去執行你寫的那個函數;而 UNUSED(hwwdg)
,這就是一個防報錯的定義,當傳進來的窗口看門狗沒有做任何處理的時候,編譯器也不會報出警告。其實我們在開發的時候已經不需要去理會中斷服務函數了,只需要找到這個中斷回調函數並將其重寫即可而這個回調函數還有一點非常便利的地方這裏沒有體現出來,就是當同時有多箇中斷使能的時候,STM32CubeMX會自動地將幾個中斷的服務函數規整到一起並調用一個回調函數,也就是無論幾個中斷,我們只需要重寫一個回調函並判斷傳進來的定時器號即可。
接下來我們就在 stm32f1xx_it.c
這個文件的最下面添加 HAL_WWDG_EarlyWakeupCallback()
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)Buffer, 1, 0xffff);
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
}
}
/* USER CODE END 1 */
4.5 添加打印函數
在 while 循環中 1 秒打印一條語句
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_USART1_UART_Init();
MX_WWDG_Init();
/* USER CODE BEGIN 2 */
printf("\n\r***** WWDG Test Start *****\n\r");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
printf("\n\r Running...\n\r");
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
4.6 查看打印
串口打印功能查看 STM32CubeMX學習筆記(6)——USART串口使用
當去掉 stm32f1xx_it.c 中 HAL_WWDG_EarlyWakeupCallback
的 HAL_WWDG_Refresh(hwwdg);
,也就是不餵狗時,系統約 59 毫秒重啓一次。
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
// HAL_WWDG_Refresh(hwwdg);
}
4.7 HAL庫與標準庫代碼比較
STM32CubeMX 使用 HAL 庫生成的代碼:
/**
* @brief WWDG Initialization Function
* @param None
* @retval None
*/
static void MX_WWDG_Init(void)
{
/* USER CODE BEGIN WWDG_Init 0 */
/* USER CODE END WWDG_Init 0 */
/* USER CODE BEGIN WWDG_Init 1 */
/* USER CODE END WWDG_Init 1 */
hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
hwwdg.Init.Window = 90;
hwwdg.Init.Counter = 127;
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;
if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN WWDG_Init 2 */
/* USER CODE END WWDG_Init 2 */
}
HAL_WWDG_Refresh(hwwdg);
使用 STM32 標準庫的代碼:
WWDG_Config(0X7F, 0X5F, WWDG_Prescaler_8);
// WWDG 中斷優先級初始化
static void WWDG_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/* WWDG 配置函數
* tr :遞減計時器的值, 取值範圍爲:0x7f~0x40
* wr :窗口值,取值範圍爲:0x7f~0x40
* prv:預分頻器值,取值可以是
* @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(36MHZ)/4096)/1
* @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(36mhz)/4096)/2
* @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(36mhz)/4096)/4
* @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(36mhz)/4096)/8
*/
void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv)
{
// 開啓 WWDG 時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
// 設置遞減計數器的值
WWDG_SetCounter( tr );
// 設置預分頻器的值
WWDG_SetPrescaler( prv );
// 設置上窗口值
WWDG_SetWindowValue( wr );
// 設置計數器的值,使能WWDG
WWDG_Enable(WWDG_CNT);
// 清除提前喚醒中斷標誌位
WWDG_ClearFlag();
// 配置WWDG中斷優先級
WWDG_NVIC_Config();
// 開WWDG 中斷
WWDG_EnableIT();
}
WWDG_SetCounter( WWDG_CNT );
MX_WWDG_Init();
對應 WWDG_Config(0X7F, 0X5F, WWDG_Prescaler_8);
HAL_WWDG_Refresh(hwwdg);
對應 WWDG_SetCounter(WWDG_CNT);
五、注意事項
用戶代碼要加在 USER CODE BEGIN N
和 USER CODE END N
之間,否則下次使用 STM32CubeMX 重新生成代碼後,會被刪除。
• 由 Leung 寫於 2021 年 1 月 29 日
• 參考:STM32CubeMX系列教程15:看門狗(WDG)
【STM32】HAL庫 STM32CubeMX教程五----看門狗(獨立看門狗,窗口看門狗)