前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STM32CubeMX | 40-实时时钟RTC的使用(日历和闹钟)

STM32CubeMX | 40-实时时钟RTC的使用(日历和闹钟)

作者头像
Mculover666
发布2021-07-23 15:20:59
3.2K1
发布2021-07-23 15:20:59
举报
文章被收录于专栏:TencentOS-tinyTencentOS-tiny

本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的 RTC 外设。

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_{spre} = 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」

代码语言:javascript
复制
/** @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的设计如下:

代码语言:javascript
复制
/**
  * @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的设计如下:

代码语言:javascript
复制
/**
  * @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函数中创建变量:

代码语言:javascript
复制
/* USER CODE BEGIN 1 */
HAL_StatusTypeDef status;
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;

/* USER CODE END 1 */

接着编写初始化部分的代码:

代码语言:javascript
复制
/* USER CODE BEGIN 2 */
printf("RTC test on bearpi borad by mculover666!\r\n");  

/* USER CODE END 2 */

最后编写循环中的代码:

代码语言:javascript
复制
/* 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库默认提供了弱定义的回调函数:

我们只需要重新实现即可,如下:

代码语言:javascript
复制
/* 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如下:

代码语言:javascript
复制
/* 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闹钟中断:

代码语言:javascript
复制
/* 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函数初始化代码之后,注册该回调函数:

代码语言:javascript
复制
/* 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 */

测试结果

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mculover666 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 准备工作
    • 硬件准备
      • 软件准备
      • 2.生成MDK工程
        • 选择芯片型号
          • 配置时钟源
            • 配置串口
              • 配置RTC
                • 配置时钟树
                  • 生成工程设置
                    • 代码生成设置
                      • 生成代码
                      • 3. 在MDK中编写、编译、下载用户代码
                        • printf重定向
                          • RTC时间/日期设置与读取
                            • 编写测试程序
                            • 4. RTC闹钟功能
                              • 设置闹钟
                                • RTC闹钟的中断
                                  • 编写闹钟中断回调函数
                                    • 测试结果
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档