STM32 使用HAL庫調(diào)試內(nèi)部RTC經(jīng)驗(yàn)總結(jié)
開篇前先宣布一件很重大的事情,本公眾號(hào)作者最近已經(jīng)成為韋東山系列
產(chǎn)品授權(quán)的官方代理了,韋山老師親筆簽名的授權(quán)證書,如果以后大家有需要學(xué)習(xí),可以隨時(shí)加我微信咨詢,也可以推薦學(xué)員來咨詢,支持正版,拒絕盜版,在此謝謝大家啦。
視頻購買鏈接:
接下來進(jìn)入正文。
本調(diào)試過程基于STM32F429如下型號(hào)。
之前做項(xiàng)目用了正點(diǎn)原子的RTC例程,結(jié)果在應(yīng)用的過程中就出問題了。
原子RTC的例程如下:
//RTC初始化//返回值:0,初始化成功;// 2,進(jìn)入初始化模式失敗;u8 RTC_Init(void){RTC_Handler.Instance=RTC;RTC_Handler.Init.HourFormat=RTC_HOURFORMAT_24;//RTC設(shè)置為24小時(shí)格式RTC_Handler.Init.AsynchPrediv=0X7F; //RTC異步分頻系數(shù)(1~0X7F)RTC_Handler.Init.SynchPrediv=0XFF; //RTC同步分頻系數(shù)(0~7FFF)RTC_Handler.Init.OutPut=RTC_OUTPUT_DISABLE;RTC_Handler.Init.OutPutPolarity=RTC_OUTPUT_POLARITY_HIGH;RTC_Handler.Init.OutPutType=RTC_OUTPUT_TYPE_OPENDRAIN;if(HAL_RTC_Init(&RTC_Handler)!=HAL_OK) return 2;if(HAL_RTCEx_BKUPRead(&RTC_Handler,RTC_BKP_DR0)!=0X5050)//是否第一次配置{RTC_Set_Time(23,59,56,RTC_HOURFORMAT12_PM); //設(shè)置時(shí)間 ,根據(jù)實(shí)際時(shí)間修改RTC_Set_Date(15,12,27,7); //設(shè)置日期HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR0,0X5050);//標(biāo)記已經(jīng)初始化過了}return 0;}//RTC底層驅(qū)動(dòng),時(shí)鐘配置//此函數(shù)會(huì)被HAL_RTC_Init()調(diào)用//hrtc:RTC句柄void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc){RCC_OscInitTypeDef RCC_OscInitStruct;RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;__HAL_RCC_PWR_CLK_ENABLE();//使能電源時(shí)鐘PWRHAL_PWR_EnableBkUpAccess();//取消備份區(qū)域?qū)懕Wo(hù)RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_LSE;//LSE配置RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;RCC_OscInitStruct.LSEState=RCC_LSE_ON; //RTC使用LSEHAL_RCC_OscConfig(&RCC_OscInitStruct);PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC;//外設(shè)為RTCPeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//RTC時(shí)鐘源為LSEHAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);__HAL_RCC_RTC_ENABLE();//RTC時(shí)鐘使能}
故障現(xiàn)象一、RTC設(shè)置日期和時(shí)間成功,按復(fù)位鍵讀取剛剛設(shè)置的日期和時(shí)間也是成功的,但斷電后時(shí)間就復(fù)位成原始值,也就是RTC_Init函數(shù)里下面這段默認(rèn)的日期和時(shí)間
if(HAL_RTCEx_BKUPRead(&RTC_Handler,RTC_BKP_DR0)!=0X5050)//是否第一次配置{RTC_Set_Time(23,59,56,RTC_HOURFORMAT12_PM); //設(shè)置時(shí)間 ,根據(jù)實(shí)際時(shí)間修改RTC_Set_Date(15,12,27,7); //設(shè)置日期HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR0,0X5050);//標(biāo)記已經(jīng)初始化過了}
通過單步調(diào)試跟蹤發(fā)現(xiàn)。
__IO uint32_t ret = 0 ;ret = HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR0) ;
ret的返回值并不是已經(jīng)標(biāo)記過的值0x5050,于是查看手冊(cè)關(guān)于RTC備份寄存器的說明:
問題定位:斷電后再次讀取的數(shù)值為0x0000 0000,而并不是我之前寫入的0x5050,說明VBAT供電出現(xiàn)了問題,于是重新調(diào)整了電路,問題解決。
故障現(xiàn)象二、RTC設(shè)置日期和時(shí)間后,讀取并不是設(shè)置后的日期和時(shí)間,而是設(shè)置之前的數(shù)值
單步調(diào)試跟蹤發(fā)現(xiàn)問題出現(xiàn)在初始化的過程中,發(fā)生了硬件超時(shí),此時(shí)發(fā)現(xiàn),讀出的ISR的值為0x80。
那咱們現(xiàn)在就要重點(diǎn)看看RTC_ISR這個(gè)寄存器了。
也就是說當(dāng)訪問該寄存器數(shù)值的時(shí)候,&上RTC_ISR_INITF值不能等于0,我們?cè)倮^續(xù)往下看手冊(cè)。
我們看到,當(dāng)?shù)?位為1時(shí),RTC才為初始化狀態(tài),可ISR=0x80,明顯第6位為0,也就是說RTC工作不正常了。
兩個(gè)原因:
1、要不RTC壞了(機(jī)率太小,除非芯片內(nèi)部出現(xiàn)故障了,因?yàn)檫@是內(nèi)部RTC)
2、外部晶振出問題了。(機(jī)率很大)
于是更改代碼,配置為內(nèi)部RTC時(shí)鐘:
//RTC底層驅(qū)動(dòng),時(shí)鐘配置//此函數(shù)會(huì)被HAL_RTC_Init()調(diào)用//hrtc:RTC句柄void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc){RCC_OscInitTypeDef RCC_OscInitStruct;RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;__HAL_RCC_PWR_CLK_ENABLE();//使能電源時(shí)鐘PWRHAL_PWR_EnableBkUpAccess();//取消備份區(qū)域?qū)懕Wo(hù)/*RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_LSE;//LSE配置RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;RCC_OscInitStruct.LSEState=RCC_LSE_ON; //RTC使用LSE*///20190710 啟用內(nèi)部晶振 yangyuanxinRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;RCC_OscInitStruct.LSIState = RCC_LSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;HAL_RCC_OscConfig(&RCC_OscInitStruct);/*PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC;//外設(shè)為RTCPeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//RTC時(shí)鐘源為LSE*/PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);__HAL_RCC_RTC_ENABLE();//RTC時(shí)鐘使能}
問題成功解決!
故障現(xiàn)象三、調(diào)用RTC設(shè)置日期和時(shí)間,在使用的過程中出現(xiàn)卡死現(xiàn)象
我調(diào)用的是HAL_RTC_GetTime來獲取時(shí)間,調(diào)用HAL_RTC_GetDate來獲取日期。
我調(diào)用API的順序是:
HAL_RTC_GetDate(xxxxx);
HAL_RTC_GetTime(xxxxx);
結(jié)果軟件卡死,針對(duì)這問題我折騰了很久都找不出問題的根源,后來詳細(xì)看了API上的注釋說明。
1、獲取當(dāng)前的時(shí)間 HAL_RTC_GetTime
/*** @brief Gets RTC current time.* @param hrtc: pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @param sTime: Pointer to Time structure* @param Format: Specifies the format of the entered parameters.* This parameter can be one of the following values:* @arg RTC_FORMAT_BIN: Binary data format* @arg RTC_FORMAT_BCD: BCD data format* @note You can use SubSeconds and SecondFraction (sTime structure fields returned) to convert SubSeconds* value in second fraction ratio with time unit following generic formula:* Second fraction ratio * time_unit= [(SecondFraction-SubSeconds)/(SecondFraction+1)] * time_unit* This conversion can be performed only if no shift operation is pending (ie. SHFP=0) when PREDIV_S >= SS* @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values* in the higher-order calendar shadow registers to ensure consistency between the time and date values.* Reading RTC current time locks the values in calendar shadow registers until current date is read.* @retval HAL status*/HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format){uint32_t tmpreg = 0;/* Check the parameters */assert_param(IS_RTC_FORMAT(Format));/* Get subseconds structure field from the corresponding register */sTime->SubSeconds = (uint32_t)(hrtc->Instance->SSR);/* Get SecondFraction structure field from the corresponding register field*/sTime->SecondFraction = (uint32_t)(hrtc->Instance->PRER & RTC_PRER_PREDIV_S);/* Get the TR register */tmpreg = (uint32_t)(hrtc->Instance->TR & RTC_TR_RESERVED_MASK);/* Fill the structure fields with the read parameters */sTime->Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16);sTime->Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >>8);sTime->Seconds = (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU));sTime->TimeFormat = (uint8_t)((tmpreg & (RTC_TR_PM)) >> 16);/* Check the input parameters format */if(Format == RTC_FORMAT_BIN){/* Convert the time structure parameters to Binary format */sTime->Hours = (uint8_t)RTC_Bcd2ToByte(sTime->Hours);sTime->Minutes = (uint8_t)RTC_Bcd2ToByte(sTime->Minutes);sTime->Seconds = (uint8_t)RTC_Bcd2ToByte(sTime->Seconds);}return HAL_OK;}
2、獲取當(dāng)前的日期 HAL_RTC_GetDate
/*** @brief Gets RTC current date.* @param hrtc: pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @param sDate: Pointer to Date structure* @param Format: Specifies the format of the entered parameters.* This parameter can be one of the following values:* @arg RTC_FORMAT_BIN: Binary data format* @arg RTC_FORMAT_BCD: BCD data format* @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values* in the higher-order calendar shadow registers to ensure consistency between the time and date values.* Reading RTC current time locks the values in calendar shadow registers until Current date is read.* @retval HAL status*/HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format){uint32_t datetmpreg = 0;/* Check the parameters */assert_param(IS_RTC_FORMAT(Format));/* Get the DR register */datetmpreg = (uint32_t)(hrtc->Instance->DR & RTC_DR_RESERVED_MASK);/* Fill the structure fields with the read parameters */sDate->Year = (uint8_t)((datetmpreg & (RTC_DR_YT | RTC_DR_YU)) >> 16);sDate->Month = (uint8_t)((datetmpreg & (RTC_DR_MT | RTC_DR_MU)) >> 8);sDate->Date = (uint8_t)(datetmpreg & (RTC_DR_DT | RTC_DR_DU));sDate->WeekDay = (uint8_t)((datetmpreg & (RTC_DR_WDU)) >> 13);/* Check the input parameters format */if(Format == RTC_FORMAT_BIN){/* Convert the date structure parameters to Binary format */sDate->Year = (uint8_t)RTC_Bcd2ToByte(sDate->Year);sDate->Month = (uint8_t)RTC_Bcd2ToByte(sDate->Month);sDate->Date = (uint8_t)RTC_Bcd2ToByte(sDate->Date);}return HAL_OK;}
結(jié)果看到注釋里note的這段英文:
@note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values
in the higher-order calendar shadow registers to ensure consistency between the time and date values.
Reading RTC current time locks the values in calendar shadow registers until Current date is read.
也就是說,調(diào)用順序應(yīng)該是先調(diào)用HAL_RTC_GetTime,再調(diào)用HAL_RTC_GetDate,否則不能解鎖,即被鎖死。
改一下獲取日期和時(shí)間的順序:
//獲取日期和時(shí)間int Get_Date_Time(void){// //先調(diào)用GetTime,再調(diào)用GetDate,否則會(huì)發(fā)生鎖死if(HAL_OK == HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN)&& HAL_OK == HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN)){current_init_time.year = RTC_DateStruct.Year + 2000;current_init_time.month = RTC_DateStruct.Month ;current_init_time.day = RTC_DateStruct.Date ;current_init_time.hour = RTC_TimeStruct.Hours ;current_init_time.minute = RTC_TimeStruct.Minutes ;return 0 ;}return -1 ;}
問題解決!
所以說,有時(shí)候我們口里念的年月日時(shí)分秒,也會(huì)在程序里,先獲取日期,再獲取時(shí)間,然而HAL庫的規(guī)則恰恰是相反的,這一點(diǎn)要注意了,別被坑了。
END
你“在看”我嗎?
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場,如有問題,請(qǐng)聯(lián)系我們,謝謝!





