STM32CubeMX教程11 RTC 实时时钟 - 入侵检测和时间戳
使用STM32CubeMX軟件配置STM32F407開發板RTC實現入侵檢測和時間戳功能,具體為周期喚醒回調中使用串口輸出當前RTC時間,按鍵WK_UP存儲當前RTC時間到備份寄存器,按鍵KEY_2從備份寄存器中讀取上次存儲的時間,按鍵KEY_1負責產生入侵事件
1、準備材料
開發板(正點原子stm32f407探索者開發板V2.4)
ST-LINK/V2驅動
STM32CubeMX軟件(Version 6.10.0)
keil μVision5 IDE(MDK-Arm)
CH340G Windows系統驅動程序(CH341SER.EXE)
XCOM V2.6串口助手
杜邦線一根
2、實驗目標
使用STM32CubeMX軟件配置STM32F407開發板RTC實現入侵檢測和時間戳功能,具體為周期喚醒回調中使用串口輸出當前RTC時間,按鍵WK_UP存儲當前RTC時間到備份寄存器,按鍵KEY_2從備份寄存器中讀取上次存儲的時間,按鍵KEY_1負責產生入侵事件
3、實驗流程
3.0、前提知識
STM32F407的RTC上有兩個入侵檢測模塊,但是筆者使用的LQFP144封裝的STM32F407ZGT6只有一個入侵檢測模塊,只有一個入侵檢測模塊的STM32F407單片機是利用RTC_AF1(PC13)引腳來進行觸發的,和按鍵外部中斷類似,如果設置入侵檢測觸發為低電平觸發,那么當PC13為低電平時就會進入Tampere1事件回調函數,當發生入侵事件時,RTC的20個備份寄存器中的值會全部丟失
由于開發板上PC13引腳并沒有按鍵控制,不方便實現其電平的翻轉變化操作,因此本實驗需要一根杜邦線,將按鍵KEY_1所使用的PE3引腳與PC13引腳短接,相當于使用按鍵KEY_1來間接控制PC13的電平變化,如下圖所示,當按鍵KEY_1松開時,此時PE3/PC13狀態應該由外部上/下拉決定,而當按鍵KEY_1按下時,PE3/PC13的狀態應該為低電平,通過設置PC13外部上拉,就可以實現KEY_1按鍵松開時為高電平,按下為低電平
3.1、CubeMX相關配置
請閱讀“STM32CubeMX教程10 RTC 實時時鐘 - 周期喚醒、鬧鐘A/B事件和備份寄存器”實驗3.1.1小節配置RCC和SYS
3.1.1、時鐘樹配置
系統時鐘樹配置與上一實驗一致,均設置為STM32F407總線能達到的最高時鐘頻率,配置LSE,RTC時鐘頻率為32.768kHz,具體如下圖所示
3.1.2、外設參數配置
本實驗需要需要初始化USART1作為輸出信息渠道,具體配置步驟請閱讀“STM32CubeMX教程9 USART/UART 異步通信”
單擊Pinout & Configuration頁面左邊Timers/RTC,并在頁面中間激活日歷,周期喚醒WakeUp采用內部模式,勾選入侵檢測1將其輸入復用到引腳RTC_AF1(PC13),則此后PC13引腳便作為入侵檢測引腳,具體配置如下圖所示
與上一小節實驗類似,需要配置RTC通用參數、日歷日期時間、周期喚醒參數和入侵檢測參數
① 濾波設置中,如果不濾波則入侵檢測的觸發方式只能選擇邊沿觸發,而如果選擇濾波,則觸發方式只能選擇電平觸發,這里由于使用的機械按鍵存在抖動,因此對輸入濾波
② 入侵引腳是否上拉設置中,如上述3.0小節所述,我們需要PE3/PC13外部上拉才能實現目標,因此此處選擇上拉
③ 保存了入侵時間戳就可以在Tampere1事件回調函數中使用HAL_RTCEx_GetTimeStamp獲取入侵時間戳
④ 入侵檢測觸發方式設置中,由于按鍵按下為低電平,因此這里選擇低電平
3.1.3、外設中斷配置
在Pinout & Configuration頁面左邊System Core/NVIC中勾選入侵檢測及周期喚醒中斷,然后選擇合適的中斷優先級即可
3.2、生成代碼
請先閱讀“STM32CubeMX教程1 工程建立”實驗3.4.3小節配置Project Manager
單擊頁面右上角GENERATE CODE生成工程
3.2.1、外設初始化調用流程
與上一小節RTC初始化函數MX_RTC_Init對比,可以發現本小節的初始化函數中減少了鬧鐘A/B的初始化,但是新增加了入侵檢測的初始化,如下圖所示,也即我們在CubeMX中設置的參數,類似的中斷相關的初始化設置仍然在HAL_RTC_MspInit函數中
3.2.2、外設中斷調用流程
在CubeMX中勾選RTC入侵檢測啟動中斷后,在stm32f4xx_it.c中均會生成對應的中斷服務函數TAMP_STAMP_IRQHandler()
在該TAMP_STAMP_IRQHandler()中斷服務函數中調用了HAL庫HAL_RTCEx_TamperTimeStampIRQHandler()函數統一處理時間戳/入侵事件
最終根據發生的事件來源調用了時間戳事件回調函數HAL_RTCEx_TimeStampEventCallback()、入侵檢測1事件回調函數HAL_RTCEx_Tamper1EventCallback()和入侵檢測2事件HAL_RTCEx_Tamper2EventCallback()
具體流程如下圖所示
3.2.3、添加其他必要代碼
由于無入侵檢測2,筆者這里只實現了入侵檢測1事件回調函數HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc),將其實現在了rtc.c中,另外周期喚醒回調函數內容與上一小結內容一致,這里不再贅述,入侵檢測1事件回調函數具體代碼如下圖所示
源代碼如下
/*Tampere1事件回調函數*/
void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc)
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
if(HAL_RTCEx_GetTimeStamp(hrtc, &sTime, &sDate, RTC_FORMAT_BIN) == HAL_OK)
{
char str[24];
sprintf(str,"TimeStamp = %2d:%2d:%2d\r\n",sTime.Hours,sTime.Minutes,sTime.Seconds);
printf("Tampere1 Event Happend, %s", str);
}
HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin);
}
經過了上述的過程之后目前還缺少兩個操作,利用按鍵WK_UP存儲當前RTC時間到備份寄存器,按鍵KEY_2從備份寄存器中讀取上次存儲的時間,其代碼實現在了主函數主循環中,簡單采用輪詢的方式處理按鍵,如下圖所示
源代碼如下
/*按下WK_UP按鍵將當前時間存儲到備份寄存器*/
if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == GPIO_PIN_SET)
{
HAL_Delay(50);
if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == GPIO_PIN_SET)
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
if(HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
{
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR2, sTime.Hours);
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR3, sTime.Minutes);
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR4, sTime.Seconds);
char timeStr[30];
sprintf(timeStr,"%2d:%2d:%2d",sTime.Hours,sTime.Minutes,sTime.Seconds);
printf("Store %s to the backup register\r\n", timeStr);
while(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin));
}
}
}
/*按下KEY2按鍵將存儲到備份寄存器的時間利用串口輸出*/
if(HAL_GPIO_ReadPin(KEY_2_GPIO_Port,KEY_2_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(50);
if(HAL_GPIO_ReadPin(KEY_2_GPIO_Port,KEY_2_Pin) == GPIO_PIN_RESET)
{
uint32_t sHour,sMinute,sSecond;
sHour = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); //Hour
sMinute = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3); //Minute
sSecond = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4); //Second
char timeStr[30];
sprintf(timeStr,"%u:%u:%u",sHour,sMinute,sSecond);
printf("Read out %s from the backup register\r\n", timeStr);
while(!HAL_GPIO_ReadPin(KEY_2_GPIO_Port,KEY_2_Pin));
}
}
4、常用函數
/*時間戳回調函數*/
void HAL_RTCEx_TimeStampEventCallback(RTC_HandleTypeDef *hrtc)
/*Tampere1事件回調函數*/
void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc)
/*Tampere2事件回調函數*/
void HAL_RTCEx_Tamper2EventCallback(RTC_HandleTypeDef *hrtc)
/*獲取RTC時間戳*/
HAL_StatusTypeDef HAL_RTCEx_GetTimeStamp(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTimeStamp, RTC_DateTypeDef *sTimeStampDate, uint32_t Format)
5、燒錄驗證
5.1、具體步驟
“RTC Mode and Configuration中啟用內部模式的WakeUp周期喚醒 -> 勾選入侵檢測Tamper1 Routed to AF1 -> 配置合適的日歷通用參數、日歷日期時間、周期喚醒參數和入侵檢測參數 -> NVIC中勾選RTC周期喚醒中斷及RTC入侵檢測中斷,并選擇合適的中斷優先級 -> 在生成的工程代碼中重新實現周期喚醒回調函數、Tampere1事件回調函數HAL_RTCEx_Tamper1EventCallback -> 添加必要的代碼邏輯(具體看上述3.2)”
5.2、實驗現象
燒錄程序,利用杜邦線短接PE3和PC13,當開發板上電后,會在周期喚醒回調函數中不斷地輸出當前RTC的時間,另外開發板上的紅色LED燈也會不斷地閃爍,當按下開發板上的WK_UP按鍵之后會將當前RTC日歷的時間存儲到備份寄存器RTC_BKP_DR2~4中,按下開發板上的KEY_2按鍵可以從備份寄存器中將上次存儲的時間讀出來
然后當按下按鍵KEY_1的時候,會發生入侵事件,此時入侵被檢測到,會觸發Tampere1事件回調函數通過串口輸出入侵事件的信息,并且如果再去通過KEY_2按鍵讀取備份寄存器中存儲的時間會發現由于入侵的發生,備份寄存器中的值已經被清空
上述整個流程串口輸出信息如下圖所示
6、奇怪的現象
有時候會出現寫備份寄存器寫不進去的情況,如果你也遇到了,可以嘗試將開發板完全斷電(電源線、USB串口和調試器接口),然后重新上電復位再向備份寄存器中寫入試試
參考資料
STM32Cube高效開發教程(基礎篇)
更多內容請瀏覽 OSnotes的CSDN博客
總結
以上是生活随笔為你收集整理的STM32CubeMX教程11 RTC 实时时钟 - 入侵检测和时间戳的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高可用架构,去中心化有多重要?
- 下一篇: uniapp中实现H5录音和上传、实时语