實(shí)時(shí)時(shí)鐘是一個(gè)獨(dú)立的定時(shí)器。RTC模塊擁有一組連續(xù)計(jì)數(shù)的計(jì)數(shù)器,在相應(yīng)軟件配置下,可提供時(shí)鐘日歷的功能。修改計(jì)數(shù)器的值可以重新設(shè)置系統(tǒng)當(dāng)前的時(shí)間和日期。RTC模塊和時(shí)鐘配置系統(tǒng)(RCC_BDCR寄存器)處于后備區(qū)域,即在系統(tǒng)復(fù)位或從待機(jī)模式喚醒后,RTC的設(shè)置和時(shí)間維持不變。系統(tǒng)復(fù)位后,對(duì)后備寄存器和RTC的訪問被禁止,這是為了防止對(duì)后備區(qū)域(BKP)的意外寫操作。執(zhí)行以下操作將使能對(duì)后備寄存器和RTC的訪問:
設(shè)置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能電源和后備接口時(shí)鐘
設(shè)置寄存器PWR_CR的DBP位,使能對(duì)后備寄存器和RTC的訪問。
其供電部分如圖所示,當(dāng)VDD斷點(diǎn)之后,需要VBAT管腳為其供電,才能保證RTC的正常工作。
看一下中斷函數(shù),stm32不同系列的中斷函數(shù)是不一樣的
stm32F和L系列,比如低功耗這塊
1.使用RTC鬧鐘功能:再進(jìn)低功耗前先獲取當(dāng)前RTC的時(shí)間,在當(dāng)前時(shí)間上加10分鐘,算出喚醒時(shí)間,然后設(shè)置RTC鬧鐘喚醒時(shí)間,
設(shè)置函數(shù):HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD)
RTC鬧鐘中斷函數(shù):void RTC_Alarm_IRQHandler(void)
2.使用RTC的WakeUp功能,最大計(jì)數(shù)值可以設(shè)置0x1FFFF,根據(jù)時(shí)鐘頻率可以任意調(diào)整延時(shí)喚醒時(shí)間,如果時(shí)1HZ的RTC計(jì)數(shù)頻率,最大延時(shí)喚醒時(shí)間(0x1FFFF+1)*1/60/60=36小時(shí)
設(shè)置函數(shù): HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,600,RTC_WAKEUPCLOCK_CK_SPRE_16BITS);//RTC600秒后喚醒
RTC周期喚醒中斷函數(shù) : void RTC_WKUP_IRQHandler(void)
3.RTC全局中斷函數(shù) RTC_IRQHandler()
注意事項(xiàng):
4.產(chǎn)生鬧鐘中斷的前一瞬間,一定產(chǎn)生了秒中斷,那么會(huì)先執(zhí)行RTC_IRQHandler() 中斷函數(shù), 在RTC_IRQHandler() 執(zhí)行的過程中,鬧鐘中斷標(biāo)志又被掛起,
由于RTC_IRQHandler()是全局中斷函數(shù),必須清除所有的中斷標(biāo)志,程序才能退出該函數(shù), 假如RTC_IRQHandler() 和RTCAlarm_IRQHandler() 是同樣的優(yōu)先級(jí),
要想讓程序退出RTC_IRQHandler() 函數(shù),那么你必須清除鬧鐘中斷標(biāo)志(如果不清除鬧鐘中斷標(biāo)志,程序會(huì)死在RTC_IRQHandler() ), 這樣問題又出現(xiàn)了,清除鬧鐘中斷標(biāo)志后,程序就不會(huì)進(jìn)入RTCAlarm_IRQHandler(),那么RTCAlarm_IRQHandler()函數(shù)永遠(yuǎn)也不會(huì)被執(zhí)行。
5.STM32F10x有20條中斷線,其中16條用于IO口中斷使用,還有4條用于內(nèi)部中斷事件。EXTI17就是用于內(nèi)部RTC鬧鐘喚醒中斷事件時(shí)使用,所以初始化中除了打開RTC鬧鐘中斷同時(shí)打開了EXTI17中斷線。配置鬧鐘中斷的話,也要開啟EXTI17中斷,特別注意。
6.STM32備份寄存器的配置與使用
嵌入式系統(tǒng)設(shè)計(jì)中,用來存儲(chǔ)系統(tǒng)運(yùn)行過程中的數(shù)據(jù)有很多種方式,而使用STM32的備份寄存器可以實(shí)現(xiàn)對(duì)少量數(shù)據(jù)的頻繁存儲(chǔ)。因?yàn)檫@種方式時(shí)將數(shù)據(jù)存儲(chǔ)在RAM中,掉電則數(shù)據(jù)丟失,所以需要使用備份電源為芯片供電;也由于是在RAM中,理論上可以無限次存取。
代碼如下
u8 RTC_Init()
{
u8 temp = 0;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//電源時(shí)鐘和背部時(shí)鐘
PWR_BackupAccessCmd(ENABLE); //允許背部區(qū)域?qū)?
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(); //進(jìn)入配置RTC模式
RTC_SetPrescaler(32767);
RTC_SetCounter(0); //初始值設(shè)定為0s
RTC_WaitForLastTask();
RTC_SetAlarm(40); //鬧鐘值設(shè)定為40s
RTC_WaitForLastTask(); //等待上述配置完成
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0XC0B4);
PWR_BackupAccessCmd(DISABLE); //不允許背部區(qū)域?qū)懖僮? }
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;
}
//此初始化函數(shù)在主函數(shù)中的用法
while(RTC_Init())
{
printf("INIT Programing is ERROR!!\r\n");
}
if (BKP_ReadBackupRegister(BKP_DR1) != 0xC0B4) 的意思就是讓STM32上電后自檢是不是第一次運(yùn)行這個(gè)程序。BKP_ReadBackupRegister(BKP_DR1)代表讀取BKP_DR1的值,如果第一次運(yùn)行這個(gè)程序那這個(gè)值一定是0X0000,值和0XC0B4不相等就進(jìn)入配置初始化程序。BKP_WriteBackupRegister(BKP_DR1, 0XC0B4)代表往BKP_DR1這個(gè)寄存器中寫入0XC0B4,注意BKP_DR1這個(gè)值被寫入之后就算復(fù)位他也不會(huì)被清除成0000。這樣的話就算復(fù)位或者重新上電,初始程序也不會(huì)再執(zhí)行一遍,所以RTC的值就不會(huì)再重新設(shè)置了。如果想要RTC的值重新從0開始計(jì)數(shù),那就可以吧0XC0B4改成一個(gè)新的數(shù)字,重新下載一次程序就可以了。
最后在看STM32G系列的,H系列有喜歡的額可以嘗試一下
只有這一個(gè)RTC中斷函數(shù),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)
// {
// //已經(jīng)初始化過了,直接跳出初始化函
// return;
// }
// //第一次初始化,將任意后備寄存器寫任意值,做個(gè)標(biāo)記,標(biāo)記已經(jīng)初始化過了,下次系統(tǒng)復(fù)位時(shí)不用初始匿
// 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; //設(shè)置 10s 后產(chǎn)生鬧鐘中斷
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 */
}