细说STM32F407单片机独立看门狗IWDG的原理及使用方法
目录
(1)DEBUG、USART6、GPIO、CodeGenrator
看门狗(Watchdog)就是MCU上的一种特殊的定时器,用于监视系统的运行,在发生错误(例如程序出现死循环)时,能自动使系统复位。STM32F407上有1个独立看门狗和1个窗口看门狗,这两个看门狗的作用不一样。
一、独立看门狗的工作原理
独立看门狗(Independent Watchdog,IWDG)是由内部32kHz低速时钟LSI驱动的自由运行的12位递减计数器。LSI在时钟树上的位置如图所示。“独立看门狗”简称为“看门狗”。
独立看门狗内部还可以对LSI时钟进行分频,分频后的时钟作为计数器的时钟信号。在预分频器寄存器IWDG_PR里,有PR[2:0]用于设置分频系数,分频系数从4、8、16到256。系统复位时,IWDG的12位递减计数器的值是4095。启动IWDG后,计数器就递减计数,当计数器值变为0的时候,就会使系统产生复位。
独立看门狗有一个重载寄存器IWDG_RLR,可以设置一个12位的重载值,例如4000。在看门狗的递减计数器的值变为0之前,将IWDG_RLR里的值重新载入看门狗计数器,就可以避免产生复位。
独立看门狗还有一个关键字寄存器IWDG_KR,其KEY[15:0]是一个只可以写的关键字。写入不同的关键字有不同的作用。
- 写入0xAAAA时,重载寄存器IWDG_RLR中的12位值就会被写入计数器,从而使计数器从头开始递减计数,避免系统复位。此操作称为刷新看门狗。
- 写入0x5555后,才可以修改预分频器寄存器IWDG_PR和重载寄存器IWDG_RLR的内容。
- 写入0xCCCC时,启动独立看门狗。
重载值为4095时独立看门狗的超时:
预分频系数 | 超时/ms |
/4 | 512 |
/8 | 1024 |
/16 | 2048 |
/32 | 4096 |
/64 | 8192 |
/128 | 16384 |
/256 | 32768 |
二、独立看门狗的HAL驱动程序
独立看门狗的HAL驱动程序头文件是stm32f4xx_hal_iwdg.h,独立看门狗的驱动函数比较少,只有2个常规函数和几个宏函数。独立看门狗没有中断。
1、初始化函数HAL_IWDG_Init()
函数HAL_IWDG_Init()用于初始化独立看门狗,其原型定义如下:
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
其中,参数hiwdg是IWDG_HandleTypeDef结构体指针,是看门狗对象指针。独立看门狗初始化后就自动启动了,且无法关闭。
在CubeMX生成的外设初始化代码中,会定义一个独立看门狗外设对象变量,即
IWDG_HandleTypeDef hiwdg; //独立看门狗外设对象变量
结构体IWDG_HandleTypeDef的定义如下,各成员变量的意义见注释:
typedef struct
{
IWDG_TypeDef *Instance; //IWDG寄存器基址
IWDG_InitTypeDef Init; //IWDG的参数
}IWDG_HandleTypeDef;
其中的成员变量Init是结构体类型IWDG_InitTypeDef,它定义了IWDG的参数,这个结构体定义如下,各成员变量的意义见注释:
typedef struct
{
uint32_t Prescaler; //IWDG预分频系数,也就是预分频寄存器工WDG_PR里的PR[2:0]
uint32_t Reload; //IWDG计数器重载值,也就是重载寄存器IWDG_RLR的值
}IWDG_InitTypeDef;
2、刷新看门狗的函数HAL_IWDG_Refresh()
函数HAL_IWDG_Refresh()用于刷新看门狗,就是将重载寄存器IWDG_RLR的值重新载入看门狗计数器,避免产生系统复位。函数HAL_IWDG_Refresh()的原型定义如下,只需使用IWDG对象指针作为函数参数:
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
3、宏函数
文件stm32f4xx_hal_iwdg.h还有几个主要的宏函数,这些函数的输入参数__HANDLE__是独立看门狗对象指针。
- __HAL_IWDG_START(__HANDLE__),启动独立看门狗,就是向关键字寄存器IWDG_KR写入0x0000CCCC。
- __HAL_IWDG_RELOAD_COUNTER(__HANDLE__),重置看门狗计数器的值,就是向关键字寄存器IWDG_KR写入0x0000AAAA,这会导致重载寄存器IWDG_RLR中的值载入看门狗计数器。这个宏函数与函数HAL_IWDG_Refresh()的功能相同。
- __IWDG_ENABLE_WRITE_ACCESS(__HANDLE__),使预分频寄存器IWDG_PR和重载寄存器IWDG_RLR变为可写的,其代码就是向关键字寄存器IWDG_KR写入0x00005555。
- __IWDG_DISABLE_WRITE_ACCESS(__HANDLE__),使预分频寄存器IWDG_PR和重载寄存器IWDG_RLR变为不可写的,其代码就是向关键字寄存器IWDG_KR写入0x00000000。
三、独立看门狗使用示例
1、项目动作说明
本文作者旨在介绍如何IWDG及用法。继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。项目的目的:
- 配置独立看门狗的超时为8192ms。
- 使用RTC周期唤醒功能,唤醒周期为1s,使用一个全局变量Seconds进行秒计时。
- 按下任何一个按键时,刷新看门狗,使全局变量Seconds归零。
- 超过8s无按键刷新看门狗,系统将复位。
[S2]KeyUp = IWDG_Refresh. LED1 ON
[S3]KeyDown = IWDG_Refresh. LED1 ON
[S4]KeyLeft = IWDG_RELOAD_COUNTER. LED2 ON
[S4]KeyRight = IWDG_RELOAD_COUNTER. LED2 ON
示例仍然引用KEYLED文件夹中的文件。需要参考本文作者写的其他文章:
参考文章1:细说STM32F407单片机中断方式CAN通信_stm32f407 103 can通信-CSDN博客 https://wenchm.blog.csdn.net/article/details/144913579
参考文章2: 细说STM32F407单片机以DMA方式读写外部SRAM的方法_stm32外部sram可用dma吗-CSDN博客 https://wenchm.blog.csdn.net/article/details/145013118
2、工程配置
(1)DEBUG、USART6、GPIO、CodeGenrator
与参考文章1相同。
(2) 时钟
HSE和LSE均使用外部晶振。 这里,设置HCLK=168MHz,PCLK1=42MHz,PCLK2=84MHz。
(3) RTC
开启RTC的时钟源和日历,在时钟树上设置LSE为RTC的时钟源。开启RTC的周期唤醒功能,设置唤醒时钟为1Hz信号,唤醒周期为1s。开启RTC周期唤醒中断,设置RTC的周期唤醒中断抢占优先级为1。RTC设置结果如图20-2所示。
(4)IWDG
LSI时钟频率是32kHz,看门狗最大重载值是4095(对应0xFFF),根据预分频系数可以计算出IWDG的最长超时(timeout)。
MCU内部的LSI时钟频率不是非常准确,例如,STM32F407的LSI频率范围是17kHz~47kHz,所以在设置刷新独立看门狗的周期时,要留出一定的余量。
对独立看门狗IWDG的设置界面如图所示。在模式设置部分只需激活IWDG即可,参数设置部分只有两个参数。
- IWDG counter clock prescaler,独立看门狗计数器的预分频系数,可选值为4~256。这里设置为64,因为LSI是32kHz,所以看门狗计数器的时钟信号频率是500Hz。
- IWDG down-counter reload value,递减计数器的重载值,可输入范围是0~4095,这里设置为4095。
(5)NVIC
开启RTC周期唤醒中断,设置RTC的周期唤醒中断抢占优先级为1。
经过这样的设置后,递减计数器的时钟信号周期是2ms,所以独立看门狗的超时是8192ms,也就是大约8s。如果超过8s不刷新独立看门狗,系统就会复位。
3、软件设计
(1)KEYLED
本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章2相同。
(2) main.c
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PD */
uint8_t Seconds=0;//秒计数器
/* USER CODE END PD */
/* USER CODE BEGIN 2 */
//IWDG初始化后就已经自动启动,且无法停止
printf("Demo20_IWDG: Independent Watchdog\r\n");
printf("IWDG should be refreshed in 8s.\r\n");
printf("Press any key to refresh IWDG.\r\n");
//显示菜单
printf("[S2]KeyUp = IWDG_Refresh.\r\n");
printf("[S3]KeyDown = IWDG_Refresh.\r\n");
printf("[S4]KeyLeft = IWDG_RELOAD_COUNTER.\r\n");
printf("[S4]KeyRight = IWDG_RELOAD_COUNTER.\r\n\r\n");
// MCU output low level LED light is on
LED1_OFF();
LED2_OFF();
LED3_OFF();
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
KEYS curKey = ScanPressedKey(KEY_WAIT_ALWAYS);
switch(curKey)
{
case KEY_DOWN:
case KEY_UP:
{
HAL_IWDG_Refresh(&hiwdg);//刷新IWDG
printf("IWDG is refreshed:\r\n");
LED1_ON();
LED2_OFF();
LED3_OFF();
break;
}
case KEY_LEFT:
case KEY_RIGHT:
{
__HAL_IWDG_RELOAD_COUNTER(&hiwdg);//刷新IWDG
printf("IWDG is refreshed:\r\n");
LED1_OFF();
LED2_ON();
LED3_OFF();
}
default:
{
LED1_OFF();
LED2_OFF();
LED3_OFF();
printf("IWDG is not refreshed:\r\n");
}
}
Seconds = 0;//秒计数清零
HAL_Delay(500);//消除按键抖动影响
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
Seconds++;//秒计数值
printf("Stopwatch counting: %d\r\n",Seconds);
}
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 4 */
在main()函数的while循环中检测按键输入,4个按键中有任何一个按下时,就会调用函数HAL_IWDG_Refresh()或宏函数_HAL_IWDG_RELOAD_COUNTER()刷新看门狗,并且使全局变量Seconds清零。这两个函数的功能是完全一样的。
文件main.c还实现了RTC周期唤醒中断的回调函数HAL_RTCEx_WakeUpTimerEventCallback(),在这个回调函数里将全局变量Seconds加1,并且在串口助手上显示。
4、运行与调试
运行时,串口助手上会显示秒计数值,如果计数值超过4还未按下任何键,系统就会复位。如果在计数值达到4之前按任何键就会刷新看门狗,并且重新开始秒计数。
原文地址:https://blog.csdn.net/wenchm/article/details/145159637
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!