實時時鐘是一個獨立的定時器。RTC模塊擁有一組連續計數的計數器,在相應軟件配置下,可提供時鐘日歷的功能。修改計數器的值可以重新設置系統當前的時間和日期。RTC模塊和時鐘配置系統(RCC_BDCR寄存器)處于后備區域,即在系統復位或從待機模式喚醒后,RTC的設置和時間維持不變。系統復位后,對后備寄存器和RTC的訪問被禁止,這是為了防止對后備區域(BKP)的意外寫操作。執行以下操作將使能對后備寄存器和RTC的訪問:
設置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能電源和后備接口時鐘
設置寄存器PWR_CR的DBP位,使能對后備寄存器和RTC的訪問。
其供電部分如圖所示,當VDD斷點之后,需要VBAT管腳為其供電,才能保證RTC的正常工作。
看一下中斷函數,stm32不同系列的中斷函數是不一樣的
stm32F和L系列,比如低功耗這塊
1.使用RTC鬧鐘功能:再進低功耗前先獲取當前RTC的時間,在當前時間上加10分鐘,算出喚醒時間,然后設置RTC鬧鐘喚醒時間,
設置函數:HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD)
RTC鬧鐘中斷函數:void RTC_Alarm_IRQHandler(void)
2.使用RTC的WakeUp功能,最大計數值可以設置0x1FFFF,根據時鐘頻率可以任意調整延時喚醒時間,如果時1HZ的RTC計數頻率,最大延時喚醒時間(0x1FFFF+1)*1/60/60=36小時
設置函數: HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,600,RTC_WAKEUPCLOCK_CK_SPRE_16BITS);//RTC600秒后喚醒
RTC周期喚醒中斷函數 : void RTC_WKUP_IRQHandler(void)
3.RTC全局中斷函數 RTC_IRQHandler()
注意事項:
4.產生鬧鐘中斷的前一瞬間,一定產生了秒中斷,那么會先執行RTC_IRQHandler() 中斷函數, 在RTC_IRQHandler() 執行的過程中,鬧鐘中斷標志又被掛起,
由于RTC_IRQHandler()是全局中斷函數,必須清除所有的中斷標志,程序才能退出該函數, 假如RTC_IRQHandler() 和RTCAlarm_IRQHandler() 是同樣的優先級,
要想讓程序退出RTC_IRQHandler() 函數,那么你必須清除鬧鐘中斷標志(如果不清除鬧鐘中斷標志,程序會死在RTC_IRQHandler() ), 這樣問題又出現了,清除鬧鐘中斷標志后,程序就不會進入RTCAlarm_IRQHandler(),那么RTCAlarm_IRQHandler()函數永遠也不會被執行。
5.STM32F10x有20條中斷線,其中16條用于IO口中斷使用,還有4條用于內部中斷事件。EXTI17就是用于內部RTC鬧鐘喚醒中斷事件時使用,所以初始化中除了打開RTC鬧鐘中斷同時打開了EXTI17中斷線。配置鬧鐘中斷的話,也要開啟EXTI17中斷,特別注意。
6.STM32備份寄存器的配置與使用
嵌入式系統設計中,用來存儲系統運行過程中的數據有很多種方式,而使用STM32的備份寄存器可以實現對少量數據的頻繁存儲。因為這種方式時將數據存儲在RAM中,掉電則數據丟失,所以需要使用備份電源為芯片供電;也由于是在RAM中,理論上可以無限次存取。
代碼如下
u8 RTC_Init()
{
u8 temp = 0;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//電源時鐘和背部時鐘
PWR_BackupAccessCmd(ENABLE); //允許背部區域寫
if (BKP_ReadBackupRegister(BKP_DR1) != 0xC0B4)
{
BKP_DeInit();
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //打開RTC的秒中斷和鬧鐘中斷
RTC_WaitForLastTask();
RTC_EnterConfigMode(); //進入配置RTC模式
RTC_SetPrescaler(32767);
RTC_SetCounter(0); //初始值設定為0s
RTC_WaitForLastTask();
RTC_SetAlarm(40); //鬧鐘值設定為40s
RTC_WaitForLastTask(); //等待上述配置完成
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0XC0B4);
PWR_BackupAccessCmd(DISABLE); //不允許背部區域寫操作
}
else
{
PWR_BackupAccessCmd(DISABLE);
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR,ENABLE); //打開RTC的秒中斷和鬧鐘中斷
RTC_WaitForLastTask();
}
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中斷的中斷配置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
//此初始化函數在主函數中的用法
while(RTC_Init())
{
printf("INIT Programing is ERROR!!\r\n");
}
if (BKP_ReadBackupRegister(BKP_DR1) != 0xC0B4) 的意思就是讓STM32上電后自檢是不是第一次運行這個程序。BKP_ReadBackupRegister(BKP_DR1)代表讀取BKP_DR1的值,如果第一次運行這個程序那這個值一定是0X0000,值和0XC0B4不相等就進入配置初始化程序。BKP_WriteBackupRegister(BKP_DR1, 0XC0B4)代表往BKP_DR1這個寄存器中寫入0XC0B4,注意BKP_DR1這個值被寫入之后就算復位他也不會被清除成0000。這樣的話就算復位或者重新上電,初始程序也不會再執行一遍,所以RTC的值就不會再重新設置了。如果想要RTC的值重新從0開始計數,那就可以吧0XC0B4改成一個新的數字,重新下載一次程序就可以了。
最后在看STM32G系列的,H系列有喜歡的額可以嘗試一下
只有這一個RTC中斷函數,RTC_TAMP_IRQHandler
static void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
RTC_AlarmTypeDef sAlarm = {0};
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
// //重啟后判斷該寄存器是否有值,判定是不是第丿次初始化,是否要裝載初始倿
// if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == 0xAA)
// {
// //已經初始化過了,直接跳出初始化函
// return;
// }
// //第一次初始化,將任意后備寄存器寫任意值,做個標記,標記已經初始化過了,下次系統復位時不用初始匿
// HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xAA);
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 14;
sTime.Minutes = 50;
sTime.Seconds = 0;
sTime.SubSeconds = 0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_THURSDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 28;
sDate.Year = 21;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
/** Enable the Alarm A
*/
sAlarm.AlarmTime.Hours = 14;
sAlarm.AlarmTime.Minutes = 50;
sAlarm.AlarmTime.Seconds = 10; //設置 10s 后產生鬧鐘中斷
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarm.AlarmDateWeekDay = 28;
sAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
/* USER CODE END RTC_Init 2 */
}