一、獨立看門狗
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喂狗源碼:
https://download.csdn.net/download/m0_63622771/90842063
二、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日期時間例子源碼:
https://download.csdn.net/download/m0_63622771/90842069
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;