武漢網(wǎng)站開發(fā)招聘如何推廣網(wǎng)頁
目錄
RTC實(shí)時時鐘
功能框圖
UNIX時間戳
初始化結(jié)構(gòu)體
RTC時間結(jié)構(gòu)體
RTC日期結(jié)構(gòu)體
RTC鬧鐘結(jié)構(gòu)體
進(jìn)入和退出配置函數(shù)
實(shí)驗(yàn)環(huán)節(jié)1:顯示日歷
常規(guī)配置
RTC配置
測試環(huán)節(jié)
實(shí)驗(yàn)現(xiàn)象
實(shí)驗(yàn)環(huán)節(jié)2:鬧鐘
常規(guī)配置
RTC配置
測試環(huán)節(jié)
實(shí)驗(yàn)現(xiàn)象
RTC實(shí)時時鐘
STM32的RTC外設(shè),實(shí)質(zhì)上是一個掉電后還繼續(xù)運(yùn)行的定時器。類似于通用定時器TIM外設(shè),可以計(jì)時和觸發(fā)中斷。
掉電指的是電源VDD斷開時為了RTC外設(shè)掉電繼續(xù)運(yùn)行,必須接上鋰電池給STM32的RTC、備份發(fā)卡通過Vbat引腳供電。當(dāng)主電源VDD有效時由VDD給RTC外設(shè)供電,當(dāng)CDD掉電后,由Vbat給RTC外設(shè)供電。但無論由什么電源供電,RTC的數(shù)據(jù)都保存在屬于RTC的備份域中,若主電源VDD和Vbat都掉電,則備份域保存的所有數(shù)據(jù)將丟失。備份域除了RTC模塊的寄存器,還有42個16位的寄存器可以在VDD掉電時保存用戶程序的數(shù)據(jù),系統(tǒng)復(fù)位或電源復(fù)位時,數(shù)據(jù)也不會清空。
從RTC的定時器特性來說,是一個32位的遞增計(jì)數(shù)器。時鐘源有三種:HSE/128、LSI和LSE。
使用LSI或HSE/8時鐘源,在主電源VDD掉電時,這兩個時鐘來源都會受到影響,因此無法保證RTC正常工作。因此RTC一般使用LSE。在設(shè)計(jì)中,頻率通常為32.768kHz。
在主電源VDD有效并處于待機(jī)模式時,RTC還可以配置鬧鐘事件時STM32退出待機(jī)模式。
功能框圖
淺灰色部分屬于備份域,VDD掉電時可在Vbat的驅(qū)動下繼續(xù)運(yùn)行。包括了RTC分頻器、計(jì)數(shù)器、鬧鐘控制器。
若VDD電源有效,RTC可以觸發(fā)RTC_Second(秒中斷)、RTC_Overflow(溢出事件)和RTC_Alarm(鬧鐘中斷)。從結(jié)構(gòu)圖可以分析出,定時器溢出中斷無法被配置為中斷。
若STM32處于待機(jī)模式,可由鬧鐘事件或WKUP事件(外部喚醒事件,屬于EXTI模塊,不屬于RTC)退出待機(jī)模式。
鬧鐘事件在計(jì)數(shù)器RTC_CNT的值等于鬧鐘寄存器RTC_ALR的值時觸發(fā)。
在備份域中所有寄存器都是16位的,RTC控制相關(guān)的寄存器也不例外。它的計(jì)數(shù)器RTC_CNT的32位由RTC_CNTL和RTC_CNTH這兩個寄存器組成。
在配置RTC模塊的時鐘時,通常把輸入的32768Hz的RTCCLK進(jìn)行32768分頻得到實(shí)際驅(qū)動計(jì)數(shù)器的時鐘TR_CLK =?RTCCLK / 32768 =1Hz,計(jì)數(shù)周期為1s,計(jì)數(shù)器在TR_CLK的驅(qū)動下計(jì)數(shù),即每秒計(jì)數(shù)器RTC_CNT的值+1。
由于備份域的存在,使得RTC核具有了完全獨(dú)立于APB1接口的特性,也因此對RTC寄存器的訪問要遵守一定的規(guī)則。
系統(tǒng)復(fù)位后,默認(rèn)禁止訪問后備寄存器和RTC,防止對后備區(qū)域(BKP)的意外寫操作。執(zhí)行以下操作使能對后備寄存器和RTC的訪問:
? ? ? ? 設(shè)置RCC_APB1ENR:PWREN、BKPEN位來使能電源和后備接口時鐘。
? ? ? ? 設(shè)置PWR_CR:DBP位使能對后備寄存器和RTC的訪問。
設(shè)置后備寄存器為可寫訪問后,在第一次通過APB1接口訪問RTC時,因?yàn)闀r鐘頻率的差異,所以必須等待APB1和RTC外設(shè)同步,確保被讀取出來的RTC寄存器值是正確的。若在同步后,一直沒有關(guān)閉APB1的RTC外設(shè)接口,就不需要再次同步了。
如果內(nèi)核要對RTC寄存器進(jìn)行任何寫操作,在內(nèi)核寫出寫指令后,RTC模塊在3個RTCCLK時鐘后才開始正式的寫RTC寄存器操作。由于RTCCLK的頻率比內(nèi)核主頻低得多,所以每次操作后都必須檢查RTC關(guān)閉操作標(biāo)志位RTOFF,當(dāng)這個標(biāo)志位被置1,寫操作才正式完成。
當(dāng)然,以上操作都具有對應(yīng)的庫函數(shù),不需要具體的查閱寄存器。?
UNIX時間戳
RTC_CNT是32位寄存器,可存儲的最大值為2^32-1,即約等于136年。
如某個時刻讀取計(jì)數(shù)器的值為2天的秒數(shù),以2011.1.1 0:0:0時間置0計(jì)數(shù)器的,則可以算出是2011.1.3 0:0:0時間,計(jì)數(shù)器會在2011+136年左右溢出。定時器被置0的時間為計(jì)數(shù)元年,相對計(jì)時元年的秒數(shù)為時間戳(計(jì)數(shù)器的值)。
大多數(shù)操作系統(tǒng)都是利用時間戳和計(jì)時元年來計(jì)算當(dāng)前時間的,有個標(biāo)準(zhǔn):UNIX時間戳和UNIX計(jì)時元年。
UNIX計(jì)時元年被設(shè)置為格林威治時間1970.1.1 0:0:0時間。
在這個計(jì)時系統(tǒng)上,使用的是有符號的32位整形變量來保存UNIX時間戳,因此最高位表示符號,時間戳能顯示的范圍更小了,會在2038.1.19 3:14:07時間溢出。
網(wǎng)頁上可搜:UNIX時間戳??蓪?shí)時查看。
初始化結(jié)構(gòu)體
STM32 HAL庫對RTC控制提供了完善的函數(shù)。
typedef struct {uint32_t AsynchPrediv; /* 配置RTC_CLK的異步分頻因子(0x00~0x7F ) ,具體由RTC_PRER:PREDIV_A[6:0]配置 */uint32_t OutPut; /* RTCEx輸出通道設(shè)置,指定哪一路信號作為RTC的輸出,禁止輸出/鬧鐘A輸出/鬧鐘B輸出/喚醒輸出 */} RTC_InitTypeDef;
RTC時間結(jié)構(gòu)體
用來設(shè)置初始時間,配置的是RTC時間寄存器RTC_TR。
typedef struct {uint8_t Hours; /* 小時設(shè)置。12小時制式時,0~11;24小時制式時,0~23 */uint8_t Minutes; /* 分鐘設(shè)置,0~59 */uint8_t Seconds; /* 秒設(shè)置,0~59 */} RTC_TimeTypeDef;
RTC日期結(jié)構(gòu)體
用來設(shè)置初始日期,配置的是RTC日期寄存器RTC_DR。
typedef struct {uint8_t WeekDay; /* 星期幾設(shè)置,1~7 */uint8_t Month; /* 月份設(shè)置,1~12 */uint8_t Date; /* 日期設(shè)置,1~31 */uint8_t Year; /* 年份設(shè)置,0~99 */} RTC_DateTypeDef;
RTC鬧鐘結(jié)構(gòu)體
用來設(shè)置鬧鐘時間,設(shè)置的格式為[星期/日期]-[時]-[分]-[秒],4個字段,每個字段可以設(shè)置為有效或無效(MASK)。如果MASK掉[星期/日期]字段,則每天鬧鐘都會響。
typedef struct {RTC_TimeTypeDef AlarmTime; /* 設(shè)定RTC時間寄存器的值:時/分/秒 */uint32_t Alarm; /* RTC 鬧鐘選擇:鬧鐘A、鬧鐘B */} RTC_AlarmTypeDef;
進(jìn)入和退出配置函數(shù)
/*** @brief 進(jìn)入 RTC 配置模式 .* @param None* @retval None*/void RTC_EnterConfigMode(void){/* 設(shè)置 CNF 位進(jìn)入配置模式 */RTC->CRL |= RTC_CRL_CNF;}/** @brief 退出 RTC 配置模式 .* @param None* @retval None*/void RTC_ExitConfigMode(void){/* 清空 CNF 位退出配置模式 */RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF);}
實(shí)驗(yàn)環(huán)節(jié)1:顯示日歷
常規(guī)配置
USART1:帶中斷,支持printf輸出。
RTC配置
RTC_HandleTypeDef hrtc;/* RTC init function */
void MX_RTC_Init(void)
{RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef DateToUpdate = {0};/* USER CODE BEGIN RTC_Init 1 *//* 判斷是否首次上電 */if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050){HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050); // 插入BKP數(shù)值判斷/* USER CODE END RTC_Init 1 *//** Initialize RTC Only*/hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}/** Initialize RTC and set the Time and Date*/sTime.Hours = 0x0;sTime.Minutes = 0x0;sTime.Seconds = 0x0;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;DateToUpdate.Month = RTC_MONTH_JANUARY;DateToUpdate.Date = 0x1;DateToUpdate.Year = 0x0;if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/* USER CODE BEGIN RTC_Init 2 */}/* USER CODE END RTC_Init 2 */
}void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){HAL_PWR_EnableBkUpAccess(); // 取消BKP區(qū)域?qū)懕Wo(hù),才能進(jìn)行時間保存和計(jì)時/* Enable BKP CLK enable for backup registers */__HAL_RCC_BKP_CLK_ENABLE(); // 開啟BKP時鐘/* RTC clock enable */__HAL_RCC_RTC_ENABLE();}
}void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){__HAL_RCC_RTC_DISABLE();}
}
測試環(huán)節(jié)
#include "string.h"uint8_t RxBuffer[20];void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){// A5 5A 00 01 01 00 00 00if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A){RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;RtcTime.Hours = RxBuffer[5];RtcTime.Minutes = RxBuffer[6];RtcTime.Seconds = RxBuffer[7];if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}// 星期內(nèi)部自動校正RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;RtcDate.Month = RxBuffer[3];RtcDate.Date = RxBuffer[4];RtcDate.Year = RxBuffer[2];if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}memset(RxBuffer, 0, sizeof(RxBuffer));HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);}}
}void test(void)
{RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);while(1){HAL_RTC_GetTime(&hrtc, &RtcTime, RTC_FORMAT_BIN); // 讀出時間值HAL_RTC_GetDate(&hrtc, &RtcDate, RTC_FORMAT_BIN); // 一定要先讀時間后讀日期,這樣才能校正星期參數(shù)printf("實(shí)時時間:%04d-%02d-%02d %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//顯示日期時間HAL_Delay(1000);}
}
實(shí)驗(yàn)現(xiàn)象
實(shí)驗(yàn)環(huán)節(jié)2:鬧鐘
常規(guī)配置
USART1:帶中斷,支持printf輸出。
蜂鳴器配置。
RTC配置
RTC_HandleTypeDef hrtc;void MX_RTC_Init(void)
{RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef DateToUpdate = {0};RTC_AlarmTypeDef sAlarm = {0};/* USER CODE BEGIN RTC_Init 1 *//* 判斷是否首次上電 */if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050){HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050); // 插入BKP數(shù)值判斷/* USER CODE END RTC_Init 1 *//** Initialize RTC Only*/hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}/** Initialize RTC and set the Time and Date*/sTime.Hours = 0x0;sTime.Minutes = 0x0;sTime.Seconds = 0x0;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;DateToUpdate.Month = RTC_MONTH_JANUARY;DateToUpdate.Date = 0x1;DateToUpdate.Year = 0x0;if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/** Enable the Alarm A*/sAlarm.AlarmTime.Hours = 0x0;sAlarm.AlarmTime.Minutes = 0x1;sAlarm.AlarmTime.Seconds = 0x0;sAlarm.Alarm = RTC_ALARM_A;if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/* USER CODE BEGIN RTC_Init 2 */}/* USER CODE END RTC_Init 2 */
}void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){HAL_PWR_EnableBkUpAccess();/* Enable BKP CLK enable for backup registers */__HAL_RCC_BKP_CLK_ENABLE();/* RTC clock enable */__HAL_RCC_RTC_ENABLE();/* RTC interrupt Init */HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);}
}void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){__HAL_RCC_RTC_DISABLE();/* RTC interrupt Deinit */HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);}
}
測試環(huán)節(jié)
#include "string.h"uint8_t RxBuffer[20];void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){// A5 5A 00 01 01 00 00 00if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A){RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;RTC_AlarmTypeDef RtcAlarm;RtcTime.Hours = RxBuffer[5];RtcTime.Minutes = RxBuffer[6];RtcTime.Seconds = RxBuffer[7];if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}// 星期內(nèi)部自動校正RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;RtcDate.Month = RxBuffer[3];RtcDate.Date = RxBuffer[4];RtcDate.Year = RxBuffer[2];if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}RtcAlarm.AlarmTime.Hours = RxBuffer[5];RtcAlarm.AlarmTime.Minutes = RxBuffer[6];RtcAlarm.AlarmTime.Seconds = RxBuffer[7] + 0x10;RtcAlarm.Alarm = RTC_ALARM_A;if (HAL_RTC_SetAlarm_IT(&hrtc, &RtcAlarm, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}memset(RxBuffer, 0, sizeof(RxBuffer));HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);}}
}void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
}void test(void)
{RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;初始化HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);while(1){HAL_RTC_GetTime(&hrtc, &RtcTime, RTC_FORMAT_BIN); // 讀出時間值HAL_RTC_GetDate(&hrtc, &RtcDate, RTC_FORMAT_BIN); // 一定要先讀時間后讀日期,這樣才能校正星期參數(shù)printf("實(shí)時時間:%04d-%02d-%02d %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//顯示日期時間HAL_Delay(1000);}
}
實(shí)驗(yàn)現(xiàn)象
上電運(yùn)行,LED默認(rèn)滅。一分鐘后LED亮綠燈。
通過串口調(diào)試助手發(fā)送A55A231101000000,10秒后LED滅。