本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的 RTC 外设。
1. 准备工作
硬件准备
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):
软件准备
需要安装好Keil - 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_***提供时钟;
同步预分频器(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工程:
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_*** 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!rn");
/* 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 %drn", status);
}
// read rtc date
status = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
if (status != HAL_OK) {
printf("get date fail, status is %drn", status);
}
printf("%d-%d-%d(%d) %d:%d:%d:%drn", sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay,
sTime.Hours, sTime.Minutes, sTime.Seconds, sTime.SubSeconds);
HAL_Delay(1000);
}
/* USER CODE END 3 */
测试结果为:
设置闹钟
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! <---rn");
}
/* 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! <---rn");
}
/* USER CODE END 0 */
最后在main函数初始化代码之后,注册该回调函数:
/* USER CODE BEGIN 2 */
printf("RTC test on bearpi borad by mculover666!rn");
status = HAL_RTC_RegisterCallback(&hrtc, HAL_RTC_ALARM_A_EVENT_CB_ID, AlarmAEventCallback);
if (status != HAL_OK) {
printf("rtc register callback fail!rn");
} else {
printf("rtc register callback success!rn");
}
/* USER CODE END 2 */
测试结果
上一篇:基于STM32的多路电压测量设计方案
下一篇:Linux下开发stm32(一) | 使用gcc-arm-none-eabi工具链编译
推荐阅读最新更新时间:2024-11-14 00:38
设计资源 培训 开发板 精华推荐
- 具有反馈关断功能的 LT1120IN8 5V 稳压器的典型应用电路
- UFI-POWER——基于CD42的随身WIFI便携电源/供电仓/充电宝
- 树莓派RP2040直驱DVI/HDMI接口显示屏炫酷显示
- 用于按钮控制复位的 LTC2953CDD-1 电压监控器的典型应用电路
- 40%键盘pcb
- 【训练营】【物联网实战】立创EDA灯带-488768A
- 使用使用内部基准的 TC7116A 模数转换器的典型应用(200mV 满量程,每秒 3 个读数 - RPS)
- 使用 Analog Devices 的 LT3468-1 的参考设计
- LTC4367CMS8 过压电源控制器的典型应用
- 【征集令】基于ESP32的墨水屏个人显示终端