STM32F4 HAL库开发 -- RTC
Posted 聚优致成
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F4 HAL库开发 -- RTC相关的知识,希望对你有一定的参考价值。
一、STM32F407 RTC时钟简介
STM32F407的RTC,是一个独立的BCD定时器/计数器。RTC提供了一个日历时钟(包含年月日时分秒信息)、两个可编程闹钟(ALARM A和ALARM B)中断,以及一个具有中断功能的周期性可编程唤醒标志。
两个32位寄存器(TR和DR)包含二进码十进制格式(BCD)的秒、分钟、小时(12或24小时制)、星期、日期、月份和年份。此外,还可提供二进制格式的亚秒值。
STM32F429 的 RTC 可以自动将月份的天数补偿为 28、 29(闰年)、 30 和 31 天。
二、HAL库配置RTC
1、使能电源时钟,并使能RTC及RTC后备寄存器写访问
电源时钟使能,通过RCC_APB1ENR寄存器来设置:RTC及RTC备份寄存器的写访问,通过PWR_CR寄存器的DBP位设置。
HAL库设置方法为:
__HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟 PWR
HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
2、开启外部低速振荡器LSE,选择RTC时钟,并使能
配置开启LSE的函数为 HAL_RCC_OscConfig,使用方法为:
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);
选择RTC时钟源函数为HAL_RCCEx_PeriphCLKConfig,使用方法为:
PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC;//外设为 RTC
PeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//RTC 时钟源为 LSE
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
使能RTC时钟方法为:
__HAL_RCC_RTC_ENABLE();//RTC 时钟使能
3、初始化RTC,设置RTC的分频,以及配置RTC参数
在HAL中,初始化RTC是通过函数HAL_RTC_Init实现的,该函数声明为:
HAL_StatusTypeDef HAL_RTC_Init(RTC_HandleTypeDef *hrtc);
同样按照以前的方式,我们来看看RTC初始化参数结构体RTC_HandleTypeDef 定义:
typedef struct
{
RTC_TypeDef *Instance;
RTC_InitTypeDef Init;
HAL_LockTypeDef Lock;
__IO HAL_RTCStateTypeDef State;
}RTC_HandleTypeDef;
这里我们着重讲解成员变量Init含义,因为它是真正的RTC初始化变量,它是RTC_InitTypeDef 结构体类型,结构体 RTC_InitTypeDef 定义为:
typedef struct
{
uint32_t HourFormat; //小时格式
uint32_t AsynchPrediv; //异步预分频系数
uint32_t SynchPrediv; //同步预分频系数
uint32_t OutPut; //选择连接到 RTC_ALARM 输出的标志
uint32_t OutPutPolarity; //设置 RTC_ALARM 的输出极性
uint32_t OutPutType; //设置 RTC_ALARM 的输出类型为开漏输出还是推挽输出
}RTC_InitTypeDef;
该结构体有6个成员变量。
成员变量 HourFormat 用来设置小时格式,为12小时制或者24小时制,取值为RTC_HOURFORMAT_12 或者 RTC_HOURFORMAT_24。
AsynchPrediv 用来设置RTC的异步预分频系数,也就是设置RTC_PRER寄存器的PREDIV_A相关位,因为异步分频系数是7位,所以最大值位0x7F,不能超过这个值。
SynchPrediv 用来设置RTC的同步预分频系数,也就是设置 RTC_PRER寄存器的PREDIV_S相关位,因为同步预分频系数也是15位,所以最大值位0x7FFF,不能超过这个值。
OutPut 用来选择要连接到 RTC_ALARM 输出的标志,取值为: RTC_OUTPUT_DISABLE(禁止输出), RTC_OUTPUT_ALARMA(使能闹钟 A 输出), RTC_OUTPUT_ALARMB(使能闹钟 B 输出)和 RTC_OUTPUT_WAKEUP(使能唤醒输出)。
OutPutPolarity 用来设置 RTC_ALARM 的输出极性,与 Output 成员变量配合使用,取值为RTC_OUTPUT_POLARITY_HIGH(高电平)或 RTC_OUTPUT_POLARITY_LOW(低电平)。
OutPutType 用 来 设 置 RTC_ALARM 的 输 出 类 型 为 开 漏 输 出( RTC_OUTPUT_TYPE_OPENDRAIN)还是推挽输出( RTC_OUTPUT_TYPE_PUSHPULL),与成员变量 OutPut 和 OutPutPolarity 配合使用。
RTC初始化的一般格式:
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24; //RTC 设置为 24 小时格式
hrtc.Init.AsynchPrediv = 127; //RTC 异步分频系数(1~0X7F)
hrtc.Init.SynchPrediv = 255; //RTC 同步分频系数(0~7FFF)
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
同样, HAL 库也提供了 RTC 初始化 MSP 函数。函数声明为:
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
该函数内部一般存放时钟使能,时钟源选择等操作程序。
4、设置RTC的时间
HAL库中,设置RTC时间的函数为:
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc,
RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_RTC_SetTime 函数的第三个参数 Format,用来设置输入的时间格式为BIN格式还是BCD格式,可选值为 RTC_FORMAT_BIN(使用十进制 例如 18年你得到到是 18年)和 RTC_FORMAT_BCD(使用16进制 例如 18年你得到的是 0x18年
)。
结构体 RTC_TimeTypeDef 的定义:
typedef struct
{
uint8_t Hours; //时
uint8_t Minutes; //分
uint8_t Seconds; //秒
uint8_t TimeFormat; //AM/PM 符号
uint32_t SubSeconds; //读取保存亚秒
uint32_t SecondFraction; //用来读取保存同步预分频系数
uint32_t DayLightSaving; //用来设置日历时间增加 1 小时,减少 1 小时,还是不变
uint32_t StoreOperation; //用户可对此变量设置以记录是否已对夏令时进行更改
}RTC_TimeTypeDef;
实例:
RTC_TimeTypeDef sTime = {0};
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
5、设置RTC的日期
设置RTC的日期函数为:
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc,
RTC_DateTypeDef *sDate, uint32_t Format);
我们着重讲解第二个入口参数sData,它是结构体
RTC_DateTypeDef 指针类型变量,结构体 RTC_DateTypeDef 定义如下:
typedef struct
{
uint8_t WeekDay; //星期几
uint8_t Month; //月份
uint8_t Date; //日期
uint8_t Year; //年份
}RTC_DateTypeDef;
6、获取RTC当前日期和时间
获取当前 RTC 时间的函数为:
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc,
RTC_TimeTypeDef *sTime, uint32_t Format);
获取当前 RTC 日期的函数为:
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc,
RTC_DateTypeDef *sDate, uint32_t Format);
三、计时器
SYS_DATETIME rtctime = { 0 };
uint32_t Inctime = 0;
uint32_t Networktime = 0;
//设置RTC时间
void RTC_Sync_SYS_Time(SYS_DATETIME *pDTime)
{
RTC_DateTypeDef RTC_DateStruct;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateStruct.Year = pDTime->year - DEFAULT_YEAR;
RTC_DateStruct.Month = pDTime->month;
RTC_DateStruct.Date = pDTime->day;
if (HAL_RTC_SetDate(&hrtc, &RTC_DateStruct, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
RTC_TimeStruct.Hours = pDTime->hour;
RTC_TimeStruct.Minutes = pDTime->minute;
RTC_TimeStruct.Seconds = pDTime->second;
if (HAL_RTC_SetTime(&hrtc, &RTC_TimeStruct, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
}
//得到RTC时间
void SYS_Time_Sync_RTC(SYS_DATETIME *pDTime)
{
RTC_DateTypeDef RTC_DateStruct;
RTC_TimeTypeDef RTC_TimeStruct;
HAL_RTC_GetTime(&hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
pDTime->hour = RTC_TimeStruct.Hours;
pDTime->minute = RTC_TimeStruct.Minutes;
pDTime->second = RTC_TimeStruct.Seconds;
HAL_RTC_GetDate(&hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
pDTime->year = (RTC_DateStruct.Year + DEFAULT_YEAR);
pDTime->month = RTC_DateStruct.Month;
pDTime->day = RTC_DateStruct.Date;
}
//判断闰年
uint8_t Is_Leap_Year(uint16_t year)
{
if(((year % 4) == 0) && ((year % 100) != 0) || ((year % 400) == 0))
{
return 1;
}
else
{
return 0;
}
}
//判断2月
uint8_t Last_Day_Of_Mon(uint8_t month, uint16_t year)
{
const uint8_t Sys_Mon[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if ((month == 0) || (month > 12))
{
return Sys_Mon[1] + Is_Leap_Year(year);
}
if (month != 2)
{
return Sys_Mon[month - 1];
}
else
{
return Sys_Mon[1] + Is_Leap_Year(year);
}
}
//UNIX时间转RTC时间
void Unix2Rtc(uint32_t temp, SYS_DATETIME *rtctime, uint8_t time_zone)
{
uint32_t sec;
uint16_t y;
uint32_t day;
uint8_t m;
uint16_t d;
/* time zone */
temp += (time_zone * SEC_PER_HOUR);
/* hour */
sec = temp % SEC_PER_DAY;
rtctime->hour = sec / SEC_PER_HOUR;
/* min */
sec %= SEC_PER_HOUR;
rtctime->minute = sec / SEC_PER_MIN;
/* sec */
rtctime->second = sec % SEC_PER_MIN;
/* year */
day = temp / SEC_PER_DAY;
for (y = UTC_BASE_YEAR; day > 0; y++)
{
d = (DAY_PER_YEAR + Is_Leap_Year(y));
if (day >= d)
{
day -= d;
}
else
{
break;
}
}
rtctime->year = y;
/* month */
for (m = 1; m < MONTH_PER_YEAR; m++)
{
d = Last_Day_Of_Mon(m, y);
if (day >= d)
{
day -= d;
}
else
{
break;
}
}
rtctime->month = m;
/*day*/
rtctime->day = day + 1;
}
//RTC时间转UNIX时间
uint32_t RTC2Unix(SYS_DATETIME *systime)
{
uint16_t i;
uint32_t no_of_days = 0;
uint32_t utc_time;
if (systime->year < UTC_BASE_YEAR)
{
return 0;
}
/* year */
for (i = UTC_BASE_YEAR; i < systime->year; i++)
{
no_of_days += (DAY_PER_YEAR + Is_Leap_Year(i));
}
/* month */
for (i = 1; i < systime->month; i++)
{
no_of_days += Last_Day_Of_Mon((unsigned char) i, systime->year);
}
/* day */
no_of_days += (systime->day - 1);
/* sec */
utc_time = no_of_days * SEC_PER_DAY + (systime->hour * SEC_PER_HOUR + systime->minute * SEC_PER_MIN + systime->second);
return utc_time;
}
//设置RTC时间
void THC_Set_RTC_Time(uint32_t time)
{
memset(&rtctime, 0, sizeof(rtctime));
Unix2Rtc(time, &rtctime, 8);
RTC_Sync_SYS_Time(&rtctime);
Networktime = time;
Inctime = 0;
}
//得到RTC时间
void THC_Get_RTC_Time(void)
{
memset(&rtctime, 0, sizeof(rtctime));
SYS_Time_Sync_RTC(&rtctime);
Networktime = RTC2Unix(&rtctime);
Inctime = 0;
}
//得到RTC时间
SYS_DATETIME THC_Get_SysTime(void)
{
SYS_DATETIME sRTCtime;
uint32_t time = 0;
time = Networktime + (Inctime/10);
Unix2Rtc(time, &sRTCtime, 8);
return sRTCtime;
}
//TIM中断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//100ms定时中断
if(htim==(&htim7))
{
Inctime++; //RTC计时
}
}
以上是关于STM32F4 HAL库开发 -- RTC的主要内容,如果未能解决你的问题,请参考以下文章