第十八章獨立看門狗(IWDG)實驗
本章我們學習如何使用STM32H7的獨立看門狗(以下簡稱IWDG)。STM32H7内部自帶了2個看門狗:獨立看門狗(IWDG)和視窗看門狗(WWDG)。這一章我們隻介紹獨立看門狗,視窗看門狗将在下一章介紹。在本章中,我們将通過按鍵KEY_UP來喂狗,然後通過LED0提示複位狀态。
本章分為如下幾個小節:
18.1 IWDG簡介
18.2 硬體設計
18.3 程式設計
18.4 下載下傳驗證
18.1 IWDG簡介
獨立看門狗本質上是一個定時器,這個定時器有一個輸出端,可以輸出複位信号。該定時器是一個12位的遞減計數器,當計數器的值減到0的時候,就會産生一個複位信号。如果在計數沒減到0之前,重置計數器的值的話,那麼就不會産生複位信号,這個動作我們稱為喂狗。看門狗功能由VDD電壓域供電,在停止模式和待機模式下仍然可以工作。
18.1.1 IWDG框圖
下面先來學習IWDG框圖,通過學習IWDG框圖會有一個很好的整體掌握,同時對之後的程式設計也會有一個清晰的思路。

圖18.1.1.1 IWDG框圖
從IWDG框圖整體認知就是,IWDG有一個輸入(時鐘lsi_ck),經過一個8位的可程式設計預分頻器提供時鐘給一個12位遞減計數器,滿足條件就會輸出一個複位信号(iwdg1_out_rst)。IWDG内部輸入/輸出信号如下表:
信号名稱 | 信号類型 | 說明 |
lsi_ck | 數字信号 | LSI時鐘 |
iwdg1_out_rst | 數字信号 | IWDG1 複位信号輸出 |
表18.1.1.1 IWDG内部輸入/輸出信号
STM32H750的獨立看門狗由内部專門的32Khz低速時鐘(lsi_ck)驅動,即使主時鐘發生故障,它也仍然有效。這裡需要注意獨立看門狗的時鐘是一個内部RC時鐘,是以并不是準确的32Khz,而是在17~47Khz之間的一個可變化的時鐘,隻是我們在估算的時候,以32Khz的頻率來計算,看門狗對時間的要求不是很精确,是以,時鐘有些偏差,都是可以接受的。
18.1.2 IWDG寄存器
IWDG的框圖很簡單,用到的寄存器也不多。我們主要用到其中3個寄存器:
- 鍵寄存器(IWDG_KR)鍵寄存器可以看作是獨立看門狗的控制寄存器,其描述如圖18.1.2.1所示:
-
圖18.1.2.1 WDG_KR寄存器
在鍵寄存器(IWDG_KR)中寫入0xCCCC,開始啟用獨立看門狗;此時計數器開始從其複位值0xFFF遞減計數。當計數器計數到末尾0x000時,會産生一個複位信号(IWDG_RESET)。 無論何時,隻要鍵寄存器IWDG_KR中被寫入0xAAAA, IWDG_RLR中的值就會被重新加載到計數器中進而避免産生看門狗複位 。
IWDG_PR和IWDG_RLR寄存器具有寫保護功能。要修改這兩個寄存器的值,必須先向IWDG_KR寄存器中寫入0x5555。将其他值寫入這個寄存器将會打亂操作順序,寄存器将重新被保護。重裝載操作(即寫入0xAAAA)也會啟動寫保護功能。
- 預分頻寄存器(IWDG_PR)預分頻寄存器描述如圖18.1.2.2所示:
-
圖18.1.2.2 IWDG_ PR寄存器
該寄存器用來設定看門狗時鐘(LSI)的分頻系數,最低為4分頻,最高位256分頻,該寄存器是一個32位的寄存器,但是我們隻用了最低3位,其他都是保留位。
- 重載寄存器(IWDG_ RLR)
圖18.1.2.3 IWDG_ RLR寄存器
該寄存器用來儲存重裝載到計數器中的值。該寄存器也是一個32位寄存器,隻有低12位是有效的。
18.2 硬體設計
1. 例程功能
在配置看門狗後,LED0将常亮,如果KEY_UP按鍵按下,就喂狗,隻要KEY_UP不停的按,看門狗就一直不會産生複位,保持LED0的常亮,一旦超過看門狗定溢出時間(Tout)還沒按,那麼将會導緻程式重新開機,這将導緻LED0熄滅一次。
2. 硬體資源
1)RGB燈
RED :LED0 - PB4
2)獨立按鍵
WK_UP - PA0。
3)獨立看門狗
3. 原理圖
獨立看門狗實驗的核心是在STM32H750内部進行,并不需要外部電路。但是考慮到訓示目前狀态和喂狗等操作,我們需要2個IO口,一個用來輸入喂狗信号,另外一個用來訓示程式是否重新開機。喂狗我們采用闆上的KEY_UP鍵來操作,而程式重新開機,則是通過LED0來訓示的。
18.3 程式設計
18.3.1 IWDG的HAL庫驅動
IWDG在HAL庫中的驅動代碼在stm32h7xx_hal_iwdg.c檔案(及其頭檔案)中。
1. HAL_IWDG_Init函數
IWDG的初始化函數,其聲明如下:
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
- 函數描述:用于初始化IWDG。
- 函數形參:形參1是IWDG句柄,IWDG_HandleTypeDef結構體類型,其定義如下:
typedef struct
{
IWDG_TypeDef *Instance; /* IWDG寄存器基位址 */
IWDG_InitTypeDef Init; /* IWDG初始化參數 */
}IWDG_HandleTypeDef;
- 1)Instance:指向IWDG寄存器基位址。2)Init:IWDG初始化結構體,用于配置計數器的相關參數。IWDG_InitTypeDef這個結構體類型定義如下:
typedef struct
{
uint32_t Prescaler; /* 預分頻系數 */
uint32_t Reload; /* 重裝載值 */
uint32_t Window; /* 視窗值 */
-
} IWDG_InitTypeDef;
1)Prescaler:預分頻系數,IWDG_PRESCALER_4到IWDG_PRESCALER_256。
2)Reload:重裝載值,範圍:0到0x0FFF。
3)Window:視窗值。
- 函數傳回值:HAL_StatusTypeDef枚舉類型的值。2. HAL_IWDG_Refresh函數HAL_IWDG_Refresh函數是獨立看門狗的喂狗函數。其聲明如下:
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
- 函數描述:用于把重裝載寄存器的值重載到計數器中,喂狗,防止IWDG複位。
- 函數形參:形參1是IWDG_HandleTypeDef結構體指針類型的IWDG句柄。
- 函數傳回值:
HAL_StatusTypeDef枚舉類型的值。
獨立看門狗配置步驟
1)取消寄存器寫保護,設定看門狗預分頻系數和重裝載值
首先我們必須取消IWDG_PR和IWDG_RLR寄存器的寫保護,這樣才可以設定寄存器IWDG_PR和IWDG_RLR的值。取消寫保護和設定預分頻系數以及重裝載值在HAL庫中是通過函數HAL_IWDG_Init實作的。
通過該函數設定看門狗的分頻系數,和重裝載的值。看門狗的喂狗時間(也就是看門狗溢出時間)的計算方式為:
Tout=((4×2^prer) ×rlr) /32
其中Tout為看門狗溢出時間(機關為ms)。
prer為看門狗時鐘預分頻值(IWDG_PR值),範圍為0~7。
rlr為看門狗的重裝載值(IWDG_RLR的值)。
比如我們設定prer值為4(4代表的是64分頻,HAL庫中可以使用宏定義辨別符IWDG_PRESCALER_64),rlr值為500,那麼就可以得到Tout=64×500/32=1000ms,這樣,看門狗的溢出時間就是1s,隻要你在一秒鐘之内,有一次寫入0XAAAA到IWDG_KR,就不會導緻看門狗複位(當然寫入多次也是可以的)。這裡需要提醒大家的是,看門狗的時鐘不是準确的32Khz,是以在喂狗的時候,最好不要太晚了,否則,有可能發生看門狗複位。
2)重載計數值喂狗(向IWDG_KR寫入0XAAAA)
在HAL中重載計數值的函數是HAL_IWDG_Refresh,該函數的作用是把值0xAAAA寫入到IWDG_KR寄存器,進而觸發計數器重載,即實作獨立看門狗的喂狗操作。
3) 啟動看門狗(向IWDG_KR寫入0XCCCC)
HAL庫函數裡面啟動獨立看門狗是通過宏定義辨別符來實作的:
#define __HAL_IWDG_START(__HANDLE__)
WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE);
我們隻需要調用宏定義辨別符__HAL_IWDG_START即可實作看門狗使能。實際上,當我們調用了看門狗初始化函數HAL_IWDG_Init之後,在内部已經調用了該宏啟動看門狗。
18.3.2 程式流程圖
圖18.3.2.1 獨立看門狗實驗程式流程圖
18.3.3 程式解析
1. IWDG驅動代碼
這裡我們隻講解核心代碼,詳細的源碼請大家參考CD光牒本實驗對應源碼。獨立看門狗(IWDG)驅動源碼包括兩個檔案:wdg.c和wdg.h。
wdg.h頭檔案隻有函數的聲明,就不解釋了。下面我們直接解析wdg.c的程式,先看IWDG的初始化函數,其定義如下:
/**
初始化獨立看門狗
* @param prer: IWDG_PRESCALER_4~IWDG_PRESCALER_256,對應4~256分頻
分頻因子 = 4 * 2^prer. 但最大值隻能是256!
自動重裝載值,0~0XFFF.
時間計算(大概):Tout=((4 * 2^prer) * rlr) / 32 (ms).
無
*/
void iwdg_init(uint8_t prer, uint16_t rlr)
{
g_iwdg_handle.Instance = IWDG1;
g_iwdg_handle.Init.Prescaler = prer; /* 設定IWDG分頻系數 */
g_iwdg_handle.Init.Reload = rlr; /* 重裝載 */
g_iwdg_handle.Init.Window = IWDG_WINDOW_DISABLE; /* 關閉視窗功能 */
HAL_IWDG_Init(&g_iwdg_handle);
}
IWDG_Init是獨立看門狗初始化函數,主要設定預分頻數和重裝載寄存器的值。通過這兩個寄存器,就可以大概知道看門狗複位的時間周期為多少了。
/**
喂獨立看門狗
無
無
*/
void iwdg_feed(void)
{
HAL_IWDG_Refresh(&g_iwdg_handle); /* 重裝載計數器 */
}
iwdg_feed函數用來喂狗,在該函數内部隻需調用HAL庫函數HAL_IWDG_Refresh。
2. main.c代碼
在main.c裡面編寫如下代碼:
int main(void)
{
sys_cache_enable(); /* 打開L1-Cache */
HAL_Init(); /* 初始化HAL庫 */
sys_stm32_clock_init(240, 2, 2, 4); /* 設定時鐘, 480Mhz */
delay_init(480); /* 延時初始化 */
usart_init(115200); /* 序列槽初始化為115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按鍵 */
delay_ms(100); /* 延時100ms再初始化看門狗,LED0的變化可見"*/
iwdg_init(IWDG_PRESCALER_64, 500); /* 預分頻數為64,重載值為500,溢出時間為1s */
LED0(0); /* 點亮LED0(紅燈) */
while (1)
{
if (key_scan(0) == WKUP_PRES) /* 如果WK_UP按下,則喂狗 */
{
iwdg_feed(); /* 喂狗 */
}
delay_ms(10);
}
}