本调试过程基于STM32F429如下型号。
之前做项目用了正点原子的RTC例程,结果在应用的过程中就出问题了。
原子RTC的例程如下:
//RTC初始化
//返回值:0,初始化成功;
// 2,进入初始化模式失败;
u8 RTC_Init(void)
{
RTC_Handler.Instance=RTC;
RTC_Handler.Init.HourFormat=RTC_HOURFORMAT_24;//RTC设置为24小时格式
RTC_Handler.Init.AsynchPrediv=0X7F; //RTC异步分频系数(1~0X7F)
RTC_Handler.Init.SynchPrediv=0XFF; //RTC同步分频系数(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); //设置时间 ,根据实际时间修改
RTC_Set_Date(15,12,27,7); //设置日期
HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR0,0X5050);//标记已经初始化过了
}
return 0;
}
//RTC底层驱动,时钟配置
//此函数会被HAL_RTC_Init()调用
//hrtc:RTC句柄
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_LSE;//LSE配置
RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;
RCC_OscInitStruct.LSEState=RCC_LSE_ON; //RTC使用LSE
HAL_RCC_OscConfig(&RCC_OscInitStruct);
PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC;//外设为RTC
PeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//RTC时钟源为LSE
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
__HAL_RCC_RTC_ENABLE();//RTC时钟使能
}
故障现象一、RTC设置日期和时间成功,按复位键读取刚刚设置的日期和时间也是成功的,但断电后时间就复位成原始值,也就是RTC_Init函数里下面这段默认的日期和时间
if(HAL_RTCEx_BKUPRead(&RTC_Handler,RTC_BKP_DR0)!=0X5050)//是否第一次配置
{
RTC_Set_Time(23,59,56,RTC_HOURFORMAT12_PM); //设置时间 ,根据实际时间修改
RTC_Set_Date(15,12,27,7); //设置日期
HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR0,0X5050);//标记已经初始化过了
}
通过单步调试跟踪发现。
__IO uint32_t ret = 0 ;
ret = HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR0) ;
ret的返回值并不是已经标记过的值0x5050,于是查看手册关于RTC备份寄存器的说明:
问题定位:断电后再次读取的数值为0x0000 0000,而并不是我之前写入的0x5050,说明VBAT供电出现了问题,于是重新调整了电路,问题解决。
故障现象二、RTC设置日期和时间后,读取并不是设置后的日期和时间,而是设置之前的数值
单步调试跟踪发现问题出现在初始化的过程中,发生了硬件超时,此时发现,读出的ISR的值为0x80。
那咱们现在就要重点看看RTC_ISR这个寄存器了。
#define RTC_ISR_INITF ((uint32_t)0x00000040)
也就是说当访问该寄存器数值的时候,&上RTC_ISR_INITF值不能等于0,我们再继续往下看手册。
我们看到,当第6位为1时,RTC才为初始化状态,可ISR=0x80,明显第6位为0,也就是说RTC工作不正常了。
两个原因:
1、要不RTC坏了(机率太小,除非芯片内部出现故障了,因为这是内部RTC)
2、外部晶振出问题了。(机率很大)
于是更改代码,配置为内部RTC时钟:
//RTC底层驱动,时钟配置
//此函数会被HAL_RTC_Init()调用
//hrtc:RTC句柄
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
/*
RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_LSE;//LSE配置
RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;
RCC_OscInitStruct.LSEState=RCC_LSE_ON; //RTC使用LSE
*/
//20190710 启用内部晶振 yangyuanxin
RCC_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;//外设为RTC
PeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//RTC时钟源为LSE
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
__HAL_RCC_RTC_ENABLE();//RTC时钟使能
}
问题成功解决!
故障现象三、调用RTC设置日期和时间,在使用的过程中出现卡死现象
我调用的是HAL_RTC_GetTime来获取时间,调用HAL_RTC_GetDate来获取日期。
我调用API的顺序是:
HAL_RTC_GetDate(xxxxx);
HAL_RTC_GetTime(xxxxx);
结果软件卡死,针对这问题我折腾了很久都找不出问题的根源,后来详细看了API上的注释说明。
/**
* @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;
}
/**
* @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;
}
结果看到注释里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.
也就说,调用顺序应该是先调用HAL_RTC_GetTime,再调用HAL_RTC_GetDate,否则不能解锁,即被锁死。
改一下获取日期和时间的顺序:
//获取日期和时间
int Get_Date_Time(void)
{
// //先调用GetTime,再调用GetDate,否则会发生锁死
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 ;
}
问题解决!
所以说,有时候我们口里念的年月日时分秒,也会在程序里,先获取日期,再获取时间,然而HAL库的规则恰恰是相反的,这一点要注意了,别被坑了。