STM32F1中RTC 不像 F4中,是一个单独模块。其就是一个计数器,查看HAL库中时间和日期的设置发现,在日期设置的时候,HAL库并没有将日期换算为计数器的值。
库源码如下
日期设置:
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
{
uint32_t counter_time = 0U, counter_alarm = 0U;
/* Check input parameters */
if((hrtc == NULL) || (sTime == NULL))
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_RTC_FORMAT(Format));
/* Process Locked */
__HAL_LOCK(hrtc);
hrtc->State = HAL_RTC_STATE_BUSY;
if(Format == RTC_FORMAT_BIN)
{
assert_param(IS_RTC_HOUR24(sTime->Hours));
assert_param(IS_RTC_MINUTES(sTime->Minutes));
assert_param(IS_RTC_SECONDS(sTime->Seconds));
counter_time = (uint32_t)(((uint32_t)sTime->Hours * 3600U) + \ //HAL库这里计算的时间,后面写入计数器的
((uint32_t)sTime->Minutes * 60U) + \ //也就是这个值
((uint32_t)sTime->Seconds));
}
else
{
assert_param(IS_RTC_HOUR24(RTC_Bcd2ToByte(sTime->Hours)));
assert_param(IS_RTC_MINUTES(RTC_Bcd2ToByte(sTime->Minutes)));
assert_param(IS_RTC_SECONDS(RTC_Bcd2ToByte(sTime->Seconds)));
counter_time = (((uint32_t)(RTC_Bcd2ToByte(sTime->Hours)) * 3600U) + \
((uint32_t)(RTC_Bcd2ToByte(sTime->Minutes)) * 60U) + \
((uint32_t)(RTC_Bcd2ToByte(sTime->Seconds))));
}
/* Write time counter in RTC registers */
if (RTC_WriteTimeCounter(hrtc, counter_time) != HAL_OK) //更新计数器,并没设计到日期
{
/* Set RTC state */
hrtc->State = HAL_RTC_STATE_ERROR;
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return HAL_ERROR;
}
else
{
/* Clear Second and overflow flags */
CLEAR_BIT(hrtc->Instance->CRL, (RTC_FLAG_SEC | RTC_FLAG_OW));
/* Read current Alarm counter in RTC registers */
counter_alarm = RTC_ReadAlarmCounter(hrtc);
/* Set again alarm to match with new time if enabled */
if (counter_alarm != RTC_ALARM_RESETVALUE)
{
if(counter_alarm < counter_time)
{
/* Add 1 day to alarm counter*/
counter_alarm += (uint32_t)(24U * 3600U);
/* Write new Alarm counter in RTC registers */
if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
{
/* Set RTC state */
hrtc->State = HAL_RTC_STATE_ERROR;
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return HAL_ERROR;
}
}
}
hrtc->State = HAL_RTC_STATE_READY;
__HAL_UNLOCK(hrtc);
return HAL_OK;
}
}
HAL日期设置:
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
{
uint32_t counter_time = 0U, counter_alarm = 0U, days_elapsed = 0U, hours = 0U;
/* Check input parameters */
if((hrtc == NULL) || (sTime == NULL))
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_RTC_FORMAT(Format));
/* Check if counter overflow occurred */
if (__HAL_RTC_OVERFLOW_GET_FLAG(hrtc, RTC_FLAG_OW))
{
return HAL_ERROR;
}
/* Read the time counter*/
counter_time = RTC_ReadTimeCounter(hrtc);
/* Fill the structure fields with the read parameters */
hours = counter_time / 3600U;
sTime->Minutes = (uint8_t)((counter_time % 3600U) / 60U);
sTime->Seconds = (uint8_t)((counter_time % 3600U) % 60U);
if (hours >= 24U)
{
/* Get number of days elapsed from last calculation */
days_elapsed = (hours / 24U);
/* Set Hours in RTC_TimeTypeDef structure*/
sTime->Hours = (hours % 24U);
/* Read Alarm counter in RTC registers */
counter_alarm = RTC_ReadAlarmCounter(hrtc);
/* Calculate remaining time to reach alarm (only if set and not yet expired)*/
if ((counter_alarm != RTC_ALARM_RESETVALUE) && (counter_alarm > counter_time))
{
counter_alarm -= counter_time;
}
else
{
/* In case of counter_alarm < counter_time */
/* Alarm expiration already occurred but alarm not deactivated */
counter_alarm = RTC_ALARM_RESETVALUE;
}
/* Set updated time in decreasing counter by number of days elapsed */
counter_time -= (days_elapsed * 24U * 3600U);
/* Write time counter in RTC registers */
if (RTC_WriteTimeCounter(hrtc, counter_time) != HAL_OK)
{
return HAL_ERROR;
}
/* Set updated alarm to be set */
if (counter_alarm != RTC_ALARM_RESETVALUE)
{
counter_alarm += counter_time;
/* Write time counter in RTC registers */
if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
{
return HAL_ERROR;
}
}
else
{
/* Alarm already occurred. Set it to reset values to avoid unexpected expiration */
if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
{
return HAL_ERROR;
}
}
/* Update date */
RTC_DateUpdate(hrtc, days_elapsed);
}
else
{
sTime->Hours = hours;
}
/* Check the input parameters format */
if(Format != RTC_FORMAT_BIN)
{
/* Convert the time structure parameters to BCD format */
sTime->Hours = (uint8_t)RTC_ByteToBcd2(sTime->Hours);
sTime->Minutes = (uint8_t)RTC_ByteToBcd2(sTime->Minutes);
sTime->Seconds = (uint8_t)RTC_ByteToBcd2(sTime->Seconds);
}
return HAL_OK;
}
HAL在对日期设置的时候,只对RTC计数器进行减,就不谈加了。减的意思是,把大于一天的值去掉(days_elapsed * 24U * 3600U),只留下不满一天的时间
现在我们已经知道了问题出在HAL库在设置RTC计数器的时候,没有计算日期,所以这里我们就不用HAL的时间和日期设置。当然,获取的方法也要改变
RTC 初始化:
void RTC_Init(void)
{
rtc_handle.Instance = RTC;
rtc_handle.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
if(HAL_RTC_Init(&rtc_handle) != HAL_OK)
{
while(1 == 1)
{
;
}
}
if(HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR1) != 0x32F2)
{
rtc_time.Hours = 8;
rtc_time.Minutes = 0x01;
rtc_time.Seconds = 0x01;
if(HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN) != HAL_OK)
{
while(1 == 1)
{
;
}
}
rtc_date.WeekDay = RTC_WEEKDAY_MONDAY;
rtc_date.Month = RTC_MONTH_MAY;
rtc_date.Date = 0x01;
rtc_date.Year = 0x12;
if(HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN) != HAL_OK)
{
while(1 == 1)
{
;
}
}
HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR1, 0x32F2);
}
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_BKP_CLK_ENABLE();
RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;
RCC_OscInitStruct.LSEState=RCC_LSE_ON;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
__HAL_RCC_RTC_ENABLE();
}
实现自己的RTC 关于时间日期的获取和设置.这里用到了HAL库中的RTC计数器读(RTC_ReadTimeCounter)和写(RTC_WriteTimeCounter)函数。在库中,两个函数是静态函数,把static去掉,在头文件申明,以便我们使用
#define RTC_BASE_YEAR 2000
static const uint32_t day_sec = 86400;
static const uint32_t leap_y_s = 31622400;
static const uint32_t norm_y_s = 31536000;
static const int month_days[]={0,31,59,90,120,151,181,212,243,273,304,334,365};
//功能:获取传入的日期与基准日期的时间差,单位秒
uint32_t getSecformBase(int y,int m,int d)
{
int days =0;
uint32_t sec;
if(isLeapyear(y) > 0)
{
if( m > 2)
{
days = 1;
}
}
days += month_days[m-1];
days = days + d -1;
sec = ((uint32_t)days)*day_sec;
y--;
while(y >= 2000)
{
if(isLeapyear(y) > 0)
{
sec += leap_y_s;
}
else
{
sec += norm_y_s;
}
y--;
}
return sec;
}
//闰年判断
uint8_t isLeapyear(int year)
{
if(year%4==0)
{
if(year%100==0)
{
if(year%400==0)
{
return 1;
}
else
{
return 0;
}
}
else
{
return 1;
}
}
else
{
return 0;
}
}
//时间设置
void RTC_setTime(RTC_TimeTypeDef * time)
{
uint32_t t_c = RTC_ReadTimeCounter(&rtc_handle);
t_c = t_c - t_c%day_sec;
t_c += time->Hours*3600;
t_c += time->Minutes*60;
t_c += time->Seconds;
RTC_WriteTimeCounter(&rtc_handle,t_c);
}
//日期设置
void RTC_setDate(RTC_DateTypeDef * date)
{
uint32_t diff_sec;
uint32_t t_c = RTC_ReadTimeCounter(&rtc_handle);
t_c %= day_sec;
diff_sec = getSecformBase(date->Year + RTC_BASE_YEAR,date->Month, date->Date);
t_c = t_c + diff_sec;
RTC_WriteTimeCounter(&rtc_handle,t_c);
}
//获取时间
void RTC_getTime(RTC_TimeTypeDef * time) {
uint32_t t_c = RTC_ReadTimeCounter(&rtc_handle);
t_c %= day_sec;
time->Hours = (uint8_t)(t_c /3600);
t_c %= 3600;
time->Minutes = (uint8_t)(t_c /60);
t_c %= 60;
time->Seconds = t_c;
}
//获取日期
void RTC_getDate(RTC_DateTypeDef * date) {
int year,yday,mon,mday,wday;
uint32_t t_c = RTC_ReadTimeCounter(&rtc_handle);
int days =(int)(t_c / day_sec);
days = days - (31 + 28) + 730484; // 730484 为0 到 2000-1-1的天数
year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
yday = days - (365 * year + year / 4 - year / 100 + year / 400);
if (yday < 0)
{
if((year % 4 == 0) && (year % 100 || (year % 400 == 0)))
{
yday = 366 + yday;
}
else
{
yday = 365 + yday;
}
year--;
}
mon = (yday + 31) * 10 / 306;
mday = yday - (367 * mon / 12 - 30) + 1;
if (yday >= 306)
{
year++;
mon -= 10;
}
else
{
mon += 2;
}
wday = (6 + days) % 7; //2000-1-1 是星期六
date->Date =(uint8_t) mday;
date->Month =(uint8_t) mon;
date->Year = (uint8_t)(year - RTC_BASE_YEAR);
date->WeekDay =(uint8_t) wday;
}