STM32CubeMX | 40 - 实时时钟RTC的使用(日历和闹钟)
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32CubeMX | 40 - 实时时钟RTC的使用(日历和闹钟)相关的知识,希望对你有一定的参考价值。
本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的 RTC 外设。
本系列教程所编写的驱动源码:https://github.com/Mculover666/HAL_Driver_Lib,好用的话,记得点个Star呀!
1. 准备工作
硬件准备
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):
软件准备
- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码。
2.生成MDK工程
选择芯片型号
打开STM32CubeMX,打开MCU选择器:
搜索并选中芯片STM32L431RCT6
:
配置时钟源
- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;
这里我都使用外部时钟:
配置串口
开发板板载了一个CH340换串口,连接到USART1。
接下来开始配置USART1
:
配置RTC
RTC外设全称 Real-Time Clock,主要用处为:
- 日历:输出年月日、时分秒、星期
- 闹钟:提供闹钟中断
- 唤醒:低功耗模式唤醒中断
① 配置RTC外设的时钟来源
首先选中RTC外设,激活时钟源:
RTC外设的时钟来源有三种:
- 外部低速时钟(LSE):产生32.768KHz的时钟信号
- 内部低速时钟(LSI):产生的32KHz时钟信号
- 外部高速时钟分频(HSE_RTC):产生的32KHz时钟信号
小熊派开发板上设计了外部晶振,切换到 Clock Configuration 页面,配置为使用外部晶振(若无法选择,检查LSE时钟是否配置为外部晶振):
② 配置预分频器
RTC外设时钟源信号进来后经过两个预分频器,如图中红框所示:
- 异步预分频器(async):7bit、默认值为128,产生ck_apre时钟信号,为亚秒级计数器RTC_SSR提供时钟;
- 同步预分频器(sync):15bit、默认值为256,产生ck_spre时钟信号,为日历更新提供时钟;
本文中采用LSE作为RTC外设时钟源,在两个分频器的值都是默认值的情况下,最后产生的时钟节拍为 1Hz。
f
s
p
r
e
=
32768
/
128
/
256
=
1
H
z
f_{spre} = 32768/128/256=1Hz
fspre=32768/128/256=1Hz
所以,此处两个预分频器的值保持默认即可:
③ 配置日历
激活日历功能,并设置日期和时间初始值:
关于二进制码和BCD码,比如当前是36秒(十进制),换算为十六进制就是一个字节0x24,但BCD码是用一个字节的高4位和低4位分别来表示3和6,3的十六进制是0x03,6的十六进制是0x06,合在一起BCD码就是0x36。
配置时钟树
STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz
即可:
生成工程设置
代码生成设置
最后设置生成独立的初始化文件:
生成代码
点击GENERATE CODE
即可生成MDK-V5工程:
3. 在MDK中编写、编译、下载用户代码
printf重定向
STM32CubeMX_09 | 重定向printf函数到串口输出的多种方法
RTC时间/日期设置与读取
时分秒可以从RTC时间寄存器(RTC_TR)中读出:
日期可以从RTC日期寄存器(RTC_DR)中读出:
在HAL库中提供了读取时间、读取日期、设置时间、设置日期的API:
/** @defgroup RTC_Exported_Functions_Group2 RTC Time and Date functions
* @{
*/
/* RTC Time and Date functions ************************************************/
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
/**
* @}
*/
其中时间结构体RTC_TimeTypedef的设计如下:
/**
* @brief RTC Time structure definition
*/
typedef struct
{
uint8_t Hours; /*!< Specifies the RTC Time Hour.
This parameter must be a number between Min_Data = 0 and Max_Data = 12 if the RTC_HourFormat_12 is selected.
This parameter must be a number between Min_Data = 0 and Max_Data = 23 if the RTC_HourFormat_24 is selected */
uint8_t Minutes; /*!< Specifies the RTC Time Minutes.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
uint8_t Seconds; /*!< Specifies the RTC Time Seconds.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
uint8_t TimeFormat; /*!< Specifies the RTC AM/PM Time.
This parameter can be a value of @ref RTC_AM_PM_Definitions */
uint32_t SubSeconds; /*!< Specifies the RTC_SSR RTC Sub Second register content.
This parameter corresponds to a time unit range between [0-1] Second
with [1 Sec / SecondFraction +1] granularity */
uint32_t SecondFraction; /*!< Specifies the range or granularity of Sub Second register content
corresponding to Synchronous pre-scaler factor value (PREDIV_S)
This parameter corresponds to a time unit range between [0-1] Second
with [1 Sec / SecondFraction +1] granularity.
This field will be used only by HAL_RTC_GetTime function */
uint32_t DayLightSaving; /*!< Specifies RTC_DayLightSaveOperation: the value of hour adjustment.
This parameter can be a value of @ref RTC_DayLightSaving_Definitions */
uint32_t StoreOperation; /*!< Specifies RTC_StoreOperation value to be written in the BKP bit
in CR register to store the operation.
This parameter can be a value of @ref RTC_StoreOperation_Definitions */
} RTC_TimeTypeDef;
其中日期结构体RTC_DataTypedef的设计如下:
/**
* @brief RTC Date structure definition
*/
typedef struct
{
uint8_t WeekDay; /*!< Specifies the RTC Date WeekDay.
This parameter can be a value of @ref RTC_WeekDay_Definitions */
uint8_t Month; /*!< Specifies the RTC Date Month (in BCD format).
This parameter can be a value of @ref RTC_Month_Date_Definitions */
uint8_t Date; /*!< Specifies the RTC Date.
This parameter must be a number between Min_Data = 1 and Max_Data = 31 */
uint8_t Year; /*!< Specifies the RTC Date Year.
This parameter must be a number between Min_Data = 0 and Max_Data = 99 */
} RTC_DateTypeDef;
编写测试程序
基于上述API,编写测试程序:每秒读取一次日期和时间。
首先在main函数中创建变量:
/* USER CODE BEGIN 1 */
HAL_StatusTypeDef status;
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
/* USER CODE END 1 */
接着编写初始化部分的代码:
/* USER CODE BEGIN 2 */
printf("RTC test on bearpi borad by mculover666!\\r\\n");
/* USER CODE END 2 */
最后编写循环中的代码:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// read rtc time
status = HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
if (status != HAL_OK) {
printf("get time fail, status is %d\\r\\n", status);
}
// read rtc date
status = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
if (status != HAL_OK) {
printf("get date fail, status is %d\\r\\n", status);
}
printf("%d-%d-%d(%d) %d:%d:%d:%d\\r\\n", sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay,
sTime.Hours, sTime.Minutes, sTime.Seconds, sTime.SubSeconds);
HAL_Delay(1000);
}
/* USER CODE END 3 */
测试结果为:
4. RTC闹钟功能
设置闹钟
RTC外设带有Alarm A和 Alarm B两个闹钟,两个闹钟用法相同,这里我用 Alarm A 演示如何使用。
配置开启闹钟:
设定闹钟值,MASK用来决定闹钟匹配时是否屏蔽该字段:
当RTC当前值和闹钟设定值相同时,会将RTC初始值和状态寄存器(RTC_ISR)中的 ALRAF 标志位硬件置位:
RTC闹钟的中断
RTC外设没有独立的中断,但是ST巧妙的将RTC外设都连接到了外部中断EXTI,通过触发EXTI来产生RTC外设中断。
通过查阅参考手册可以看到使能 RTC 闹钟中断的步骤:
前两步配置并使能EXTI、选择上升沿有效,配置并使能 RTC_Alarm 中断,在cubemx中直接使能即可:
第三步配置RTC生成闹钟中断,在上一小节设置闹钟时间时,cubemx生成的代码中会自动生成该步代码。
至此,配置完成,生成代码。
编写闹钟中断回调函数
cubemx中默认配置了生成外设中断服务函数,并在其中调用HAL的处理函数:
所以在stm32l4xx_it.c
文件中可以看到闹钟中断处理函数:
按照HAL库的中断处理思想,编写回调函数,这里需要注意,因为RTC外设所有的中断都是通过EXTI触发的,所以中断触发后,HAL会根据不同的标志位去调用不同的回调函数。
HAL库提供了两种机制供我们使用,通过宏定义USE_HAL_RTC_REGISTER_CALLBACKS
的值来判断。
① 当宏定义USE_HAL_RTC_REGISTER_CALLBACKS
的值为0时,HAL库默认提供了弱定义的回调函数:
我们只需要重新实现即可,如下:
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// RTC Alarm A Event callback
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hrtc);
printf("---> alarm a callback! <---\\r\\n");
}
/* USER CODE END 0 */
② 当宏定义USE_HAL_RTC_REGISTER_CALLBACKS
的值为1时,HAL库并提供了回调函数注册机制,API如下:
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_RTC_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef HAL_RTC_RegisterCallback(RTC_HandleTypeDef *hrtc, HAL_RTC_CallbackIDTypeDef CallbackID, pRTC_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_RTC_UnRegisterCallback(RTC_HandleTypeDef *hrtc, HAL_RTC_CallbackIDTypeDef CallbackID);
#endif /* (USE_HAL_RTC_REGISTER_CALLBACKS == 1) */
其中CallbackID是一个枚举类型,pCallback 是一个函数指针,定义如下:
所以我们可以在main.c中编写如下的回调函数,用于处理Alarm A闹钟中断:
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// RTC Alarm A Event callback
void AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
printf("---> alarm a callback! <---\\r\\n");
}
/* USER CODE END 0 */
最后在main函数初始化代码之后,注册该回调函数:
/* USER CODE BEGIN 2 */
printf("RTC test on bearpi borad by mculover666!\\r\\n");
status = HAL_RTC_RegisterCallback(&hrtc, HAL_RTC_ALARM_A_EVENT_CB_ID, AlarmAEventCallback);
if (status != HAL_OK) {
printf("rtc register callback fail!\\r\\n");
} else {
printf("rtc register callback success!\\r\\n");
}
/* USER CODE END 2 */
测试结果
更多精彩文章及资源,请关注我的微信公众号:『mculover666』。
以上是关于STM32CubeMX | 40 - 实时时钟RTC的使用(日历和闹钟)的主要内容,如果未能解决你的问题,请参考以下文章