OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 802|回复: 0

《M144Z-M3最小系统板使用指南——STM32F103版》第二十七章 RTC实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2024-4-17 09:37:02 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-4-16 11:31 编辑

第二十七章 RTC实验

1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章介绍STM32F103实时时钟(RTC)的使用,实时时钟能为系统提供一个准确的时间,即时系统复位或主电源断电,RTC依然能够运行,因此RTC也经常用于各种低功耗场景。通过本章的学习,读者将学习到RTC的使用。
本章分为如下几个小节:
27.1 硬件设计
27.2 程序设计
27.3 下载验证

27.1 硬件设计
27.1.1 例程功能
1. 通过LCD实时显示RTC时间,并可通过USMART设置RTC时间和闹钟
2. RTC闹钟串口提示
3. RTC周期唤醒改变LED1状态,周期的1秒
4. LED0闪烁,指示程序正在运行

27.1.2硬件资源
1. LED
       LED0 - PB5
       LED1 - PE5
2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块
3. USART1
       USART1_TX - PA9
       USART1_RX - PA10
4. TIM4
5. RTC

27.1.3 原理图
本章实验使用的RTC为STM32F103的片上资源,因此没有相应的连接原理图。

27.2 程序设计
27.2.1 HAL库的PWR驱动
本章实验要使用到RTC,因此需要对RTC及其相关的寄存器进行配置,但是为了防止误操作,系统复位后备份区域(指RTC、备份寄存器)是被禁止写访问的,备份区域的写访问是由PWR进行配置的,具体的配置步骤如下:
①:使能访问备份区域
在HAL库中对应的驱动函数如下:
①:使能访问备份区域
该函数用于使能访问备份区域,其函数原型如下所示:
  1. voidHAL_PWR_EnableBkUpAccess(void);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112442.png
表27.2.1.1 函数HAL_PWR_EnableBkUpAccess()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112451.png
表27.2.1.2 函数HAL_PWR_EnableBkUpAccess()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 使能访问备份区域 */
  5.     HAL_PWR_EnableBkUpAccess();
  6. }
复制代码

27.2.2 HAL库的RTC驱动
本章实验使用了RTC,RTC最基本的操作就是设置和获取时间和日期,同时还需要读写RTC备份寄存器保存是否已经在初始化过程中设置过时间的标志,其具体的步骤如下:
①:初始化RTC
②:读取RTC备份寄存器判断是否进行设置过时间
③:若未设置过时间,则设置RTC的时间
④:若未设置过时间,则设置RTC的日期
⑤:将设置过时间的标志写入RTC备份寄存器
⑥:读取RTC的时间
⑦:读取RTC的日期
在HAL库中对应的驱动函数如下:
①:初始化RTC
该函数用于初始化RTC,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_Init(RTC_HandleTypeDef *hrtc);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112503.png
表27.2.2.1 函数HAL_RTC_Init()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112513.png
表27.2.2.2 函数HAL_RTC_Init()返回值描述
该函数需要传入RTC的句柄指针,该句柄中就包含了RTC的初始化配置参数结构体,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint32_t AsynchPrediv;  /* 异步预分频器系数 */
  4.     uint32_t OutPut;        /* 信号输出 */
  5. }RTC_InitTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_HandleTypeDef rtc_handle = {0};
  5.    
  6.     /* 初始化RTC */
  7.    rtc_handle.Instance = RTC;
  8.    rtc_handle.Init.AsynchPrediv = 32767;
  9.    rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
  10.    HAL_RTC_Init(&rtc_handle);
  11. }
复制代码
②:读取RTC备份寄存器
该函数用于读取RTC备份寄存器,其函数原型如下所示:
  1. uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112523.png
表27.2.2.3 函数HAL_RTCEx_BKUPRead()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112530.png
表27.2.2.4 函数HAL_RTCEx_BKUPRead()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     uint32_t rtc_backup_reg0;
  5.    
  6.     /* 读取RTC备份寄存器0 */
  7.    rtc_backup_reg0 = HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0);
  8.    
  9.     /* Do something. */
  10. }
复制代码
③:配置RTC时间
该函数用于配置RTC的时间,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_SetTime( RTC_HandleTypeDef *hrtc,
  2.                                      RTC_TimeTypeDef*sTime,
  3.                                      uint32_t Format);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112538.png
表27.2.2.5 函数HAL_RTC_SetTime()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112548.png
表27.2.2.6 函数HAL_RTC_SetTime()返回值描述
该函数使用RTC_TimeTypeDef类型的结构体变量传入RTC的时间参数,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint8_t Hours;          /* 时 */
  4.     uint8_t Minutes;        /* 分 */
  5.     uint8_t Seconds;        /* 秒 */
  6. }RTC_TimeTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_TimeTypeDef time = {0};
  5.    
  6.     /* 配置RTC时间 */
  7.    RTC_TimeTypeDef.Hours = 0;
  8.    RTC_TimeTypeDef.Minutes = 0;
  9.    RTC_TimeTypeDef.Seconds = 0;
  10.    HAL_RTC_SetTime(&rtc_handle, &time, RTC_FORMAT_BIN);
  11. }
复制代码
④:配置RTC日期
该函数用于配置RTC的日期,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_SetDate( RTC_HandleTypeDef *hrtc,
  2.                                      RTC_DateTypeDef*sDate,
  3.                                      uint32_t Format);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112559.png
表27.2.2.7 函数HAL_RTC_SetDate()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112607.png
表27.2.2.8 函数HAL_RTC_SetDate()返回值描述
该函数使用RTC_DateTypeDef类型的结构体变量传入RTC的日期参数,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint8_t WeekDay; /* 星期 */
  4.     uint8_t Month;  /* 月 */
  5.     uint8_t Date;   /* 日 */
  6.     uint8_t Year;   /* 年 */
  7. }RTC_DateTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_DateTypeDef date = {0};
  5.    
  6.     /* 配置RTC日期 */
  7.    date.WeekDay =RTC_WEEKDAY_SATURDAY;
  8.    date.Month = RTC_MONTH_JANUARY;
  9.    date.Date = 1;
  10.    date.Year = 0;
  11.    HAL_RTC_SetDate(&rtc_handle, &date, RTC_FORMAT_BIN);
  12. }
复制代码
⑤:写入RTC备份寄存器
该函数用于写入RTC备份寄存器,其函数原型如下所示:
  1. voidHAL_RTCEx_BKUPWrite(  RTC_HandleTypeDef *hrtc,
  2.                              uint32_t BackupRegister,
  3.                              uint32_t Data);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112621.png
表27.2.2.9 函数HAL_RTCEx_BKUPWrite()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112636.png
表27.2.2.10 函数HAL_RTCEx_BKUPWrite()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 往RTC备份寄存器0写入0x50505050 */
  5.    RTC_WriteBackup(&rtc_handle, RTC_BKP_DR0, 0x50505050);
  6. }
复制代码
⑥:读取RTC时间
该函数用于读取RTC的时间,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_GetTime( RTC_HandleTypeDef *hrtc,
  2.                                      RTC_TimeTypeDef*sTime,
  3.                                      uint32_t Format);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112716.png
表27.2.2.11 函数HAL_RTC_GetTime()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112726.png
表27.2.2.12 函数HAL_RTC_GetTime()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_TimeTypeDef time;
  5.    
  6.     /* 读取RTC的时间 */
  7.    HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BIN);
  8.    
  9.     /* Do something. */
  10. }
复制代码
⑦:读取RTC日期
该函数用于读取RTC的日期,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_GetDate( RTC_HandleTypeDef *hrtc,
  2.                                      RTC_DateTypeDef*sDate,
  3.                                      uint32_t Format);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112745.png
表27.2.2.13 函数HAL_RTC_GetDate()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112751.png
表27.2.2.14 函数HAL_RTC_GetDate()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_DateTypeDef date;
  5.    
  6.     /* 读取RTC的日期 */
  7.    HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BIN);
  8.    
  9.     /* Do something. */
  10. }
复制代码
本实验同时也使能了RTC的闹钟功能,闹钟功能可以在RTC时间到达设定值时触发中断,其具体的使用步骤如下:
①:设置RTC中断闹钟
在HAL库中对应的驱动函数如下:
①:设置RTC中断闹钟
该函数用于设置RTC的中断闹钟,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_RTC_SetAlarm_IT( RTC_HandleTypeDef *hrtc,
  2.                                          RTC_AlarmTypeDef*sAlarm,
  3.                                          uint32_t Format);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240416112801.png
表27.2.2.15 函数HAL_RTC_SetAlarm_IT()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240416112807.png
表27.2.2.16 函数HAL_RTC_SetAlarm_IT()返回值描述
该函数使用RTC_AlarmTypeDef类型的结构体变量传入RTC闹钟的配置参数,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.    RTC_TimeTypeDef AlarmTime;  /* 闹钟时间 */
  4.     uint32_t Alarm;             /* 闹钟对象 */
  5. }RTC_AlarmTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    RTC_AlarmTypeDef alarm = {0};
  5.    
  6.     /* 设置RTC闹钟中断 */
  7.    alarm.AlarmTime.Hours = 0;
  8.    alarm.AlarmTime.Minutes = 0;
  9.    alarm.AlarmTime.Seconds = 0;
  10.    alarm.Alarm = RTC_ALARM_A;
  11.    HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm, RTC_FORMAT_BIN);
  12. }
复制代码
27.2.4 RTC驱动
本章实验的RTC驱动主要负责向应用层提供RTC的初始化和配置自动唤醒及闹钟的函数。本章实验中,RTC的驱动代码包括rtc.c和rtc.h两个文件。
RTC驱动中,RTC的初始化函数,如下所示:
  1. /**
  2. *@brief   初始化RTC
  3. *@param   无
  4. *@retval  初始化结果
  5. *@arg     0: 初始化成功
  6. *@arg     1: 初始化失败
  7. */
  8. uint8_t rtc_init(void)
  9. {
  10.     uint16_t flag;
  11.    
  12.    __HAL_RCC_PWR_CLK_ENABLE();
  13.    __HAL_RCC_BKP_CLK_ENABLE();
  14.    HAL_PWR_EnableBkUpAccess();
  15.    
  16.     /* 从后备寄存器读取RTC初始化标志 */
  17.    flag = rtc_read_bkr(1);
  18.    
  19.     /* 初始化RTC */
  20.    g_rtc_handle.Instance = RTC;
  21.    g_rtc_handle.Init.AsynchPrediv = LSE_VALUE - 1;
  22.    g_rtc_handle.Init.OutPut =RTC_OUTPUTSOURCE_NONE;
  23.     if (HAL_RTC_Init(&g_rtc_handle) != HAL_OK)
  24.     {
  25.          return 1;
  26.     }
  27.    
  28.     /* RTC第一次初始化 */
  29.     if ((flag != 0x5051) && (flag != 0x5050))
  30.     {
  31.          /* 设置RTC时间信息 */
  32.          rtc_set_time(2023, 6, 8, 8, 0, 0);
  33.     }
  34.    
  35.     /* 更新时间信息 */
  36.    rtc_get_time();
  37.    
  38.     return 0;
  39. }
复制代码
从上面的代码中可以看出,RTC的初始化函数中,会先读取RTC备份寄存器来判断是否初始化过RTC然后再初始化RTC,RTC初始化完成后,若是第一次初始化RTC,则设置RTC的时间,否则不设置RTC的时间。
RTC驱动中设置、获取RTC时间、日期的两个函数,如下所示:
  1. /**
  2. *@brief   获取RTC时间信息
  3. *@param   无
  4. *@retval  无
  5. */
  6. void rtc_get_time(void)
  7. {
  8.    RTC_DateTypeDef rtc_date_struct = {0};
  9.    RTC_TimeTypeDef rtc_time_struct = {0};
  10.    
  11.     /* 获取并更新RTC日期信息 */
  12.    HAL_RTC_GetDate(&g_rtc_handle, &rtc_date_struct, RTC_FORMAT_BCD);
  13.    g_calendar.year =rtc_date_struct.Year + 2000;
  14.    g_calendar.month =rtc_date_struct.Month;
  15.    g_calendar.date =rtc_date_struct.Date;
  16.    g_calendar.week = rtc_get_week(g_calendar.year,
  17.    g_calendar.month,
  18.    g_calendar.date);
  19.    
  20.     /* 获取并更新RTC时间信息 */
  21.    HAL_RTC_GetTime(&g_rtc_handle, &rtc_time_struct, RTC_FORMAT_BCD);
  22.    g_calendar.hour =rtc_time_struct.Hours;
  23.    g_calendar.minute =rtc_time_struct.Minutes;
  24.    g_calendar.second =rtc_time_struct.Seconds;
  25. }
  26. /**
  27. *@brief   设置RTC时间信息
  28. *@param   year: 年
  29. *@param   month: 月
  30. *@param   date: 日
  31. *@param   hour: 时
  32. *@param   minute: 分
  33. *@param   second: 秒
  34. *@retval  设置结果
  35. *@arg     0: 设置成功
  36. *@arg     1: 设置失败
  37. */
  38. uint8_t rtc_set_time(  uint16_t year,
  39.                          uint8_t month,
  40.                          uint8_t date,
  41.                          uint8_t hour,
  42.                          uint8_t minute,
  43.                          uint8_t second)
  44. {
  45.    RTC_DateTypeDef rtc_date_struct = {0};
  46.    RTC_TimeTypeDef rtc_time_struct = {0};
  47.    
  48.     /* 设置RTC日期信息 */
  49.    rtc_date_struct.WeekDay = rtc_get_week(year, month, date);
  50.    rtc_date_struct.Month = month;
  51.    rtc_date_struct.Date = date;
  52.    rtc_date_struct.Year = year - 2000;
  53.     if (HAL_RTC_SetDate(    &g_rtc_handle,
  54.                              &rtc_date_struct,
  55.                              RTC_FORMAT_BCD) != HAL_OK)
  56.     {
  57.          return 1;
  58.     }
  59.    
  60.     /* 设置RTC时间信息 */
  61.    rtc_time_struct.Hours = hour;
  62.    rtc_time_struct.Minutes = minute;
  63.    rtc_time_struct.Seconds = second;
  64.     if (HAL_RTC_SetTime(    &g_rtc_handle,
  65.                              &rtc_time_struct,
  66.                              RTC_FORMAT_BCD) != HAL_OK)
  67.     {
  68.          return 1;
  69.     }
  70.    
  71.     return 0;
  72. }
复制代码
以上两个获取、设置RTC时间、日期的函数,均是对HAL库中RTC驱动的简单封装。
RTC驱动中,配置RTC闹钟中断及其对应的闹钟事件回调函数,如下所示:
  1. /**
  2. *@brief   设置RTC闹钟时间信息
  3. * @param    hour:时
  4. *@param   minute: 分
  5. *@param   second: 秒
  6. *@retval  无
  7. */
  8. voidrtc_set_alarm(uint8_t hour, uint8_t minute, uint8_t second)
  9. {
  10.    RTC_AlarmTypeDef rtc_alarm_struct = {0};
  11.    
  12.    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
  13.    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
  14.    
  15.     /* 设置闹钟中断 */
  16.    rtc_alarm_struct.AlarmTime.Hours = hour;
  17.    rtc_alarm_struct.AlarmTime.Minutes = minute;
  18.    rtc_alarm_struct.AlarmTime.Seconds = second;
  19.    rtc_alarm_struct.Alarm = RTC_ALARM_A;
  20.    HAL_RTC_SetAlarm_IT(&g_rtc_handle, &rtc_alarm_struct, RTC_FORMAT_BIN);
  21. }
  22. /**
  23. *@brief   HAL库RTC闹钟中断回调函数
  24. *@param   hrtc: RTC句柄
  25. *@retval  无
  26. */
  27. voidHAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
  28. {
  29.    printf("AlarmA!\r\n");
  30. }
复制代码
从上面的代码中可以看到,在RTC的闹钟中断中通过串口打印了闹钟的提示。

27.2.5 实验应用代码
本章实验的应用代码,如下所示:
  1. int main(void)
  2. {
  3.     uint8_t t = 0;
  4.     char tbuf[40];
  5.     uint8_t hour, minute, second, ampm;
  6.     uint8_t year, month, date, week;
  7.    
  8.    HAL_Init();                         /* 初始化HAL库 */
  9.    sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
  10.    delay_init(72);                     /* 初始化延时 */
  11.    usart_init(115200);                 /* 初始化串口 */
  12.    usmart_dev.init(72);                /* 初始化USMART */
  13.    led_init();                         /* 初始化LED */
  14.    lcd_init();                         /* 初始化LCD */
  15.    rtc_init();                         /* 初始化RTC */
  16.    
  17.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  18.    lcd_show_string(30, 70, 200, 16, 16, "RTCTEST", RED);
  19.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  20.    
  21.     /* 设置RTC周期性唤醒中断 */
  22.    rtc_set_wakeup(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 0);
  23.    
  24.     while (1)
  25.     {
  26.          if ((t % 10) == 0)
  27.          {
  28.              /* 获取RTC时间信息 */
  29.              rtc_get_time(&hour, &minute, &second, &m);
  30.              sprintf(tbuf, "Time:%02d:%02d:%02d", hour, minute, second);
  31.              lcd_show_string(30, 120, 210, 16, 16, tbuf, RED);
  32.              /* 获取RTC日期信息 */
  33.              rtc_get_date(&year, &month, &date, &week);
  34.              sprintf(tbuf, "Date:20%02d-%02d-%02d", year, month, date);
  35.              lcd_show_string(30, 140, 210, 16, 16, tbuf, RED);
  36.              sprintf(tbuf, "Week:%d", week);
  37.              lcd_show_string(30, 160, 210, 16, 16, tbuf, RED);
  38.          }
  39.          
  40.          if (++t == 20)
  41.          {
  42.              t = 0;
  43.              LED0_TOGGLE();
  44.          }
  45.          
  46.          delay_ms(10);
  47.     }
  48. }
复制代码
从上面的代码中可以看到,在初始化完RTC后,配置了RTC周期性地唤醒,且唤醒周期为1Hz,因此应该能看到LED1以0.5Hz的频率闪烁,随后便每间隔100毫秒获取一次RTC的时间和日期,并在LCD上进行显示。
本实验同时也使用的USMART调试组件,并在usart_config.c文件中添加了RTC驱动中相关的函数,以便调试。、

27.3 下载验证
在完成编译和烧录操作后,可以看到LCD上实时地显示着RTC的时间,并且可以看到LED1在RTC周期性唤醒的驱动下以0.5Hz的频率闪烁着,此时可以通过串口调试助手调用USMART调试组件的rtc_set_alarma()函数来设置RTC的闹钟,当通过LCD观察到RTC的时间达到设置的闹钟时间后,可以看到串口调试助手上打印了“ALARM A!\r\n”的字符串提示。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 00:59

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表