16-看门狗和RTC
一、独立看门狗
1、独立看门狗概述
在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞(不按照正常程序进行运行,如程序重启,但是如果我们填加看门狗的技术,就可以防止这个种情况的发生),而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
看门狗( watchdog timer),是一个定时器(12位)电路, 一般有一个输入,叫喂狗(kicking the dog or service the dog),一个输出到MCU的RST(复位)端,MCU正常工作的时候,每隔一段时间输出一个信号到喂狗端,如果超过规定的时间不喂狗,(一般在程序跑飞时,不在程序正常的状态),WDT 定时超过,就会给出一个复位信号到MCU,使MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。
2、STM32独立看门狗
独立看门狗适合应用于需要看门狗作为一个在主程序之外 能够完全独立工作,并且对时间精度要求低的场合。
STM32F4 的独立看门狗由内部专门的 LSIRC32Khz 低速时钟 (LSI) 驱动,即使主时钟发生故障,它也仍然有效。这里需要注意独立看门狗的时钟是一个内部RC时钟,所以并不是准确的32Khz,而是在15~47Khz 之间的一个可变化的时钟,只是我们在估算的时候,以 32Khz 的频率来计算,独立看门狗对时间的要求不是很精确,所以,时钟有些偏差都是接受的范围。
3、独立看门狗操作步骤
独立看门狗操作步骤需要添加的库函数文件:stm32f4xx_iwdg.c
1、 取消寄存器写保护:
IWDG_WriteAccessCmd();
2、设置独立看门狗的预分频系数,确定时钟:
IWDG_SetPrescaler();
3、设置看门狗重装载值,确定溢出时间:
IWDG_SetReload();
4、使能看门狗
IWDG_Enable();
5、应用程序喂狗:就是重新将重装载寄存器值装载到计数器中,就相当于你每个月的伙食费,没钱了会向父母要差不多意思。
IWDG_ReloadCounter();
如下例子是防止程序重启的代码,应用到看门狗的作用
看门狗例子源码:
https://download.csdn.net/download/m0_63622771/90841876
4、效果分析
没有喂狗,系统重启
int main(void)
{int ret;u8 data[5];//中断的优先级分组,一个项目只能设置一次//中断优先级分为第二组,抢占级范围:0~3 响应:0~3NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);Delay_Init();//初始化后GPIOF_ODR默认为低电平,所以灯亮Led_Init();Usart1_Init(115200);Tim3_NoISR_Init(50000-1);Dht11_Init();//下面我们设置了一条狗,使其每两秒内要喂狗,不然会让程序重启。Iwdg_Init();//如果打印这一句话,说明系统重启。printf("iwdg test\r\n");while(1){ ret = Dht11_Data(data);if(ret == 0){//让CPU打印数据printf("湿度:%d.%d\r\n", data[0], data[1]);printf("温度:%d.%d\r\n", data[2], data[3]);}delay_s(1);delay_ms(500);delay_ms(400);}return 0;
}
下面是添加喂狗的情况,系统会正常运行
int main(void)
{int ret;u8 data[5];//中断的优先级分组,一个项目只能设置一次//中断优先级分为第二组,抢占级范围:0~3 响应:0~3NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);Delay_Init();//初始化后GPIOF_ODR默认为低电平,所以灯亮Led_Init();Usart1_Init(115200);Tim3_NoISR_Init(50000-1);Dht11_Init();Iwdg_Init();//如果打印这一句话,说明系统重启。printf("iwdg test\r\n");while(1){ ret = Dht11_Data(data);if(ret == 0){//让CPU打印数据printf("湿度:%d.%d\r\n", data[0], data[1]);printf("温度:%d.%d\r\n", data[2], data[3]);}delay_s(1);delay_ms(500);delay_ms(400);//保证上面程序运行的时间少于2S//应用程序喂狗:IWDG_ReloadCounter();}return 0;
}
注意:真正看门狗喂程序不应该放主函数当中,因为主函数运行的时间不可控制,而是应该将喂狗程序放在一个基本定时器当中,每隔固定时间进行喂狗。
例如下面使用定时器7,每隔1.8s喂狗源码:
二、RTC(时钟)
1、RTC概述
RTC(Real Time Clock,实时时钟)是一种专门用于记录时间的设备或模块,通常作为计算机系统中的一部分存在。其本质是一个计数器,以秒为单位进行计数,可以提供精确的时间信息,并且具有以下特性:
提供时间信息: RTC能够提供当前的时间,通常以秒钟数的形式表示,但也可以提供更精细的时间分辨率,如毫秒或微秒级别。
持久性: RTC具有持久性,即在MCU(Microcontroller Unit,微控制器单元)掉电后(主电源)仍然能够继续运行(依靠纽扣电池,也就说纽扣电池给RTC进行供电),因此能够确保时间信息的连续性和准确性。
低功耗: RTC通常具有低功耗特性,以确保在长时间内使用时消耗的能量较少,这对于依靠电池供电的应用尤为重要,因为它可以延长电池的使用寿命。
总的来说,RTC在许多应用中扮演着关键的角色,特别是在需要准确记录时间并且需要在掉电后继续运行的场景下,如数据记录、日志记录、定时任务等。
STM32 的 RTC 外设,实质是一个掉电后还继续运行的定时器。RTC是个独立的BCD定时器/计数器。提供一个日历时钟,两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC还包含用于管理低功耗模式的自动唤醒单元。
两个32位寄存器包含二进码十进制格式(BCD)的秒,分钟,小时(12或24小时制),星期几,日期,月份和年份。此外,还可以提供二进制的亚秒值。系统可以自动将月份的天数补偿为28,29(闰年),30,31天。并且还可以进行夏令时补偿。
STM32F4 的 RTC 时钟源( RTCCLK )通过时 钟控制器,可以从 LSE 时钟、 LSI 时钟以及 HSE 时钟三者中选择(通过 RCC_BDCR 寄存器选择)。一般我们选择 LSE ,即外部 32.768Khz 晶振作为时钟源 (RTCCLK) ,而 RTC 时钟核心,要求提供 1Hz 的时钟 ,所以,我们要设置 RTC 的可编程预分配器。STM32F4 的可编程预分配器(RTC_PRER)分为 2 个部分:
1, 一个通过 RTC_PRER 寄存器的 PREDIV_A 位配置的 7 位异步预分频器。
异步预分频需要设置为:0x7f (127则是128分频)
2, 一个通过 RTC_PRER 寄存器的 PREDIV_S 位配置的 15 位同步预分频器。
同步预分频需要设置为:0xFF (255则是256分频)
BCD码
BCD码(Binary-Coded Decimal)是一种用二进制编码表示十进制数的编码方式1.BCD码的分类:
有权码:如8421码、2421码、5421码,每个二进制位有固定的权值。
无权码:如余3码、余3循环码、格雷码,每个编码中的1和0没有确切的权值。最常见的是8421码
选择外部时钟源,当主时钟发生故障时,RTC还能正常运行;且当主电源发生故障,RTC由钮扣电池进行独立供电
纽扣电池供电电路,纽扣电池供电由PWR外设进行管理
纽扣电池实物图
纽扣电池电路图
2、RTC时间与日期配置流程
1、使能PWR时钟:RCC_APB1PeriphClockCmd();
2、使能后备寄存器访问: PWR_BackupAccessCmd();
3、配置RTC时钟源,使能RTC时钟:
RCC_RTCCLKConfig();
RCC_RTCCLKCmd();
如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
4、 初始化RTC(同步/异步分频系数和时钟格式):RTC_Init ();
5、 设置时间:RTC_SetTime ();
6、设置日期:RTC_SetDate();
后备寄存器是一种掉电后可保存数据的寄存器。
uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR)//读后备寄存器
void RTC_WriteBackupRegister(uint32_t RTC_BKP_DR, uint32_t Data)//写入后备寄存器
RTC日期时间例子源码:
3、RTC闹钟
STM32有两个闹钟,分别闹钟A与闹钟B.
RTC 中断:所有 RTC 中断均与 EXTI(外部中断控制) 控制器相连。
要使能 RTC 闹钟中断,需按照以下顺序操作:
1. 将 EXTI 线 17 配置为中断模式并将其使能,然后选择上升沿有效。
2. 配置 NVIC 中的 RTC_Alarm IRQ 通道并将其使能。
3. 配置 RTC 以生成 RTC 闹钟(闹钟 A 或闹钟 B)。
闹钟A配置流程
1、RTC时间已经初始化好相关参数。
2、关闭闹钟:RTC_AlarmCmd(RTC_Alarm_A,DISABLE);
3、配置闹钟参数:RTC_SetAlarm();
4、开启配置闹钟中断:
EXTI_Init();
NVIC_Init();
RTC_ITConfig();
5、开启闹钟:RTC_AlarmCmd(RTC_Alarm_A,ENABLE);
6、编写中断服务函数:RTC_Alarm_IRQHandler();
可通过下面函数判断是哪个闹钟发生响应
FlagStatus RTC_GetFlagStatus(uint32_t RTC_FLAG)//获取标志位
RTC闹钟例子:
https://download.csdn.net/download/m0_63622771/90842074
4、实验效果
时间设置
RTC_TimeStruct.RTC_H12 = RTC_H12_PM; //下午,在24小时制时,这个参数是无用RTC_TimeStruct.RTC_Hours = 9; //时RTC_TimeStruct.RTC_Minutes = 16; //分RTC_TimeStruct.RTC_Seconds = 10; //秒//RTC_Format_BIN,写10进制,硬件自动转换为2进制进行存储RTC_SetTime (RTC_Format_BIN, &RTC_TimeStruct);
闹钟A的设置时间1后
//闹钟时间设置RTC_AlarmTime.RTC_H12 = RTC_H12_AM; //上午,在24小时制时,这个参数是无用RTC_AlarmTime.RTC_Hours = 9; //时RTC_AlarmTime.RTC_Minutes = 17; //分RTC_AlarmTime.RTC_Seconds = 10; //秒
5、掩码位理解
掩码位:对某个位屏蔽,可以理解为忽略这个操作
在代码中:RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_Hours; //表示闹钟A有小时掩码位
如设置的时间:9:55:10,接下来,忽略了小时,也就每隔小时的55分10秒都会响应响应,一天有24个闹钟。
三、函数说明
//1
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
函数功能:是否使能 IWDG_PR and IWDG_RLR寄存器
返回值:无
uint16_t IWDG_WriteAccess:是否使能
IWDG_WriteAccess_Enable
IWDG_WriteAccess_Disable//2
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler)
函数功能:设置看门预分频器
返回值:无
uint8_t IWDG_Prescaler:分频系数
IWDG_Prescaler_4
IWDG_Prescaler_8
IWDG_Prescaler_16
IWDG_Prescaler_32
IWDG_Prescaler_64
IWDG_Prescaler_128
IWDG_Prescaler_256 //3
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource)
void IWDG_SetReload(uint16_t Reload)
函数功能:设置重载值
返回值:无
uint16_t Reload:重载值寄存器的值,范围:0~0xFFF//4
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource)
函数功能:RTC时钟源配置
返回值:无
uint32_t RCC_RTCCLKSource:时钟源选择
RCC_RTCCLKSource_LSE
RCC_RTCCLKSource_LSI
RCC_RTCCLKSource_HSE_Div2
RCC_RTCCLKSource_HSE_Div3
.
.
.
RCC_RTCCLKSource_HSE_Div30
RCC_RTCCLKSource_HSE_Div31//5
ErrorStatus RTC_Init(RTC_InitTypeDef* RTC_InitStruct);
函数功能:RTC设置
返回值
成功:SUCCESS
失败:ERROR
RTC_InitTypeDef* RTC_InitStruct:RTC结构体
typedef struct
{uint32_t RTC_HourFormat; //小时制选择uint32_t RTC_AsynchPrediv; //异步通道分频器uint32_t RTC_SynchPrediv; //同步通道分频器
}RTC_InitTypeDef;//6
ErrorStatus RTC_SetTime(uint32_t RTC_Format, RTC_TimeTypeDef* RTC_TimeStruct);
函数功能:RTC时间设置
返回值
成功:SUCCESS
失败:ERROR
uint32_t RTC_Format:存储格式
RTC_Format_BIN:二进制存储
RTC_Format_BCD:BCD编码存储
RTC_TimeTypeDef* RTC_TimeStruct:时间结构体
typedef struct
{uint8_t RTC_Hours; //时uint8_t RTC_Minutes; //分uint8_t RTC_Seconds; //秒uint8_t RTC_H12; //上/下午
}RTC_TimeTypeDef; //7
ErrorStatus RTC_SetDate(uint32_t RTC_Format, RTC_DateTypeDef* RTC_DateStruct);
函数功能:RTC日期设置
返回值
成功:SUCCESS
失败:ERROR
uint32_t RTC_Format:存储格式
RTC_Format_BIN:二进制存储
RTC_Format_BCD:BCD编码存储RTC_DateTypeDef* RTC_DateStruct:日期结构体
typedef struct
{uint8_t RTC_WeekDay; //星期uint8_t RTC_Month; //月uint8_t RTC_Date; //日uint8_t RTC_Year; //年
}RTC_DateTypeDef;//8
void RTC_SetAlarm(uint32_t RTC_Format, uint32_t RTC_Alarm, RTC_AlarmTypeDef* RTC_AlarmStruct)
函数说明:闹钟设置
返回值:无
uint32_t RTC_Format:存储格式
RTC_Format_BIN:二进制存储
RTC_Format_BCD:BCD编码存储
uint32_t RTC_Alarm:选择闹钟
RTC_Alarm_A:闹钟A
RTC_Alarm_B:闹钟BRTC_AlarmTypeDef* RTC_AlarmStruct:闹钟结构体typedef struct
{RTC_TimeTypeDef RTC_AlarmTime; //时间设置uint32_t RTC_AlarmMask; //掩码位uint32_t RTC_AlarmDateWeekDaySel; //选择日期还是星期设置闹钟uint8_t RTC_AlarmDateWeekDay; //日期还是星期
}RTC_AlarmTypeDef;//时间设置的结构体
typedef struct
{uint8_t RTC_Hours; //时uint8_t RTC_Minutes; //分uint8_t RTC_Seconds; //秒uint8_t RTC_H12; //上/下午
}RTC_TimeTypeDef;