• ARM32开发--RTC内置实时时钟


    知不足而奋进 望远山而前行


    目录

    系列文章目录

    文章目录

    前言

    学习目标

    学习内容

    RTC时钟介绍

    RTC结构框图

    RTC原理图

    RTC时钟电源

    RTC的配置流程

    RTC时钟

    开发流程

    RTC初始化

    时钟配置

    时钟获取

    BCD格式转化

    完整代码

    RTC时钟备份寄存器

    总结


    前言

    在嵌入式系统开发中,实时时钟(RTC)是至关重要的组件,它提供了准确的日期和时间信息,为各种应用场景提供了时间基准。本文将深入探讨RTC的设计原理和初始化过程,帮助您理解RTC在GD32F407上的实现方式以及如何进行初始化、配置和读取时间等操作。


    学习目标

    1. 理解原理图RTC设计部分
    2. 掌握初始化RTC
    3. 掌握设置时间
    4. 掌握读取时间

    学习内容

    RTC时钟介绍

    RTC是实时时钟(Real-Time Clock)的缩写。它是一种硬件模块或芯片,用于提供准确的日期和时间信息。

    GD32F407上有RTC的外设,它提供了一个包含日期(年/月/日)和时间(时/分/秒/亚秒)的日历功能。除亚秒用二进制码显示外,时间和日期都以BCD码的形式显示。

    RTC本质上就是一个1秒计数器,通过秒来换算出时间。因此需要我们提供一个1HZ频率的时钟。

    RTC结构框图

    RTC原理图

    RTC时钟电源

    RTC时钟分为两个电源域。RTC的核心部分位于备份域中。系统复位或从待机模式唤醒时,RTC的设置和时间都保持不变。另一部分包括APB接口及一组控制寄存器位于VDD电源域中。

    RTC的配置流程

    RTC时钟

    开发流程
    1. 加载依赖。gd32f4xx_rtc.c,gd32f4xx_pmu.c
    2. 初始化RTC。
    3. 时钟配置。
    4. 获取时钟。
    RTC初始化
    1. // 电池管理加载
    2. rcu_periph_clock_enable(RCU_PMU);
    3. pmu_backup_write_enable();
    4. // 重置备份域(不重置可能导致无法设置晶振,出现不走字情况)
    5. /* reset backup domain */
    6. rcu_bkp_reset_enable();
    7. rcu_bkp_reset_disable();
    8. // 2. 设置时钟的晶振 LXTAL (低速外部时钟, 需焊接)
    9. // rcu_osci_on(RCU_LXTAL);
    10. // rcu_osci_stab_wait(RCU_LXTAL);
    11. // rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
    12. // 2. 设置时钟的晶振 IRC32K
    13. // rcu_osci_on(RCU_IRC32K);
    14. // // 等待晶振稳定
    15. // rcu_osci_stab_wait(RCU_IRC32K);
    16. // /* 给rtc配置晶振 configure the RTC clock source selection */
    17. // rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);
    18. // 2. 设置时钟的晶振 HXTAL -> 8M
    19. rcu_osci_on(RCU_HXTAL);
    20. // 等待晶振稳定
    21. rcu_osci_stab_wait(RCU_HXTAL);
    22. /* 给rtc配置晶振 configure the RTC clock source selection */
    23. rcu_rtc_clock_config(RCU_RTCSRC_HXTAL_DIV_RTCDIV);
    24. // 分频系数(HXTAL时,需要配置) // DIV25 -> 320K
    25. rcu_rtc_div_config(RCU_RTC_HXTAL_DIV25);
    26. // RTC功能加载
    27. rcu_periph_clock_enable(RCU_RTC);
    28. rtc_register_sync_wait();
    • RTC电源供应为外部独立电路,需要加载电源管理,打开rtc电源供应。
    • 重置备份域,不重置可能导致无法设置晶振,出现时钟不走字情况。
    • RTC的时钟晶振为外部,需要加载外部晶振。
    • 加载完成过程,需要等待同步。

    时钟配置
    1. rtc_parameter_struct rps;
    2. rps.year = 0x23;
    3. rps.month = 0x04;
    4. rps.date = 0x20;
    5. rps.day_of_week = 0x04;
    6. rps.hour = 0x23;
    7. rps.minute = 0x59;
    8. rps.second = 0x55;
    9. rps.display_format = RTC_24HOUR;
    10. rps.am_pm = RTC_AM;
    11. // 配置异步和同步分频器数值 LXTAL
    12. // rps.factor_asyn = 0x7F;
    13. // rps.factor_syn = 0xFF;
    14. // 配置异步和同步分频器数值IRC32K
    15. // rps.factor_asyn = 0x7F; // 7位异步预分频器, 0x0 - 0x7F
    16. // rps.factor_syn = 0xF9; // 15位同步预分频器。0x0 - 0x7FFF
    17. // IRC32K ck_spre = 1 -> 1秒 -> 1个时钟 (1Hz)
    18. // ck_spre = rtc_clk / ((FACTOR_A + 1)*( FACTOR_S + 1))
    19. // 1 = 32K / ((0x7F + 1)*( FACTOR_S + 1))
    20. // FACTOR_S = 32K / 0x80 - 1 = 32K / 128 - 1 = 249
    21. // 配置异步和同步分频器数值HXTAL_DIV25
    22. rps.factor_asyn = 127; // 7位异步预分频器, 0x0 - 0x7F
    23. rps.factor_syn = 2499; // 15位同步预分频器。0x0 - 0x7FFF
    24. // HXTAL_DIV25 -> 8M/25 -> 320K
    25. // ck_spre = 1 -> 1秒 -> 1个时钟 (1Hz)
    26. // ck_spre = rtc_clk / ((FACTOR_A + 1)*( FACTOR_S + 1))
    27. // 1 = 320K / ((0x7F + 1)*( FACTOR_S + 1))
    28. // FACTOR_S = 320K / 0x80 - 1 = 32K / 128 - 1 = 2499
    29. rtc_init(&rps);

    1 = rtc_clk/(asyn + 1)/(syn + 1)

    • asyn取值范围为0到0x7F
    • syn取值范围为0到0x07FF
    时钟获取
    1. rtc_parameter_struct rps;
    2. rtc_current_time_get(&rps);
    3. uint16_t year = READ_BCD(rps.year) + 2000;
    4. uint8_t month = READ_BCD(rps.month);
    5. uint8_t date = READ_BCD(rps.date);
    6. uint8_t week = READ_BCD(rps.day_of_week);
    7. uint8_t hour = READ_BCD(rps.hour);
    8. uint8_t minute = READ_BCD(rps.minute);
    9. uint8_t second = READ_BCD(rps.second);
    10. printf("%d-%d-%d %d %d:%d:%d\r\n", year, month, date, week, hour, minute, second);
    BCD格式转化

    BCD(Binary-Coded Decimal,二进制编码十进制)是一种用于表示十进制数字的二进制编码形式。在RTC(实时时钟)等应用中,BCD格式常用于存储和显示日期和时间信息。它的主要特点是每个十进制数位都被编码成4位二进制数。

    在BCD格式中,一个十进制数的每一位被表示为4位二进制数,其中每个二进制数位都对应一个十进制数位。例如:

    • 十进制数 0 用BCD表示为 0000。
    • 十进制数 1 用BCD表示为 0001。
    • 十进制数 9 用BCD表示为 1001。

    这样,一个BCD字节(8位)可以表示两个十进制数字。

    1. // 十位取出左移4+ 个位 (得到BCD数)
    2. #define WRITE_BCD(val) ((val / 10) << 4) + (val % 10)
    3. // 将高4位乘以10 + 低四位 (得到10进制数)
    4. #define READ_BCD(val) (val >> 4) * 10 + (val & 0x0F)
    完整代码
    1. #include "gd32f4xx.h"
    2. #include "systick.h"
    3. #include <stdio.h>
    4. #include "main.h"
    5. #include "Usart.h"
    6. // 十位取出左移4+ 个位 (得到BCD数)
    7. #define WRITE_BCD(val) ((val / 10) << 4) + (val % 10)
    8. // 将高4位乘以10 + 低四位 (得到10进制数)
    9. #define READ_BCD(val) (val >> 4) * 10 + (val & 0x0F)
    10. void Usart0_recv(uint8_t *data, uint32_t len) {
    11. printf("%s\r\n", data);
    12. }
    13. static void RTC_config() {
    14. // 电池管理加载
    15. rcu_periph_clock_enable(RCU_PMU);
    16. pmu_backup_write_enable();
    17. // 重置备份域(不重置可能导致无法设置晶振,出现不走字情况)
    18. /* reset backup domain */
    19. rcu_bkp_reset_enable();
    20. rcu_bkp_reset_disable();
    21. // 晶振加载
    22. rcu_osci_on(RCU_LXTAL);
    23. rcu_osci_stab_wait(RCU_LXTAL);
    24. rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
    25. // RTC功能加载
    26. rcu_periph_clock_enable(RCU_RTC);
    27. rtc_register_sync_wait();
    28. rtc_parameter_struct rps;
    29. rps.year = WRITE_BCD(23);
    30. rps.month = WRITE_BCD(4);
    31. rps.date = WRITE_BCD(20);
    32. rps.day_of_week = WRITE_BCD(4);
    33. rps.hour = WRITE_BCD(23);
    34. rps.minute = WRITE_BCD(59);
    35. rps.second = WRITE_BCD(55);
    36. rps.display_format = RTC_24HOUR;
    37. rps.am_pm = RTC_AM;
    38. rps.factor_asyn = 0x7F;
    39. rps.factor_syn = 0xFF;
    40. rtc_init(&rps);
    41. }
    42. static void RTC_read() {
    43. rtc_parameter_struct rps;
    44. rtc_current_time_get(&rps);
    45. uint16_t year = READ_BCD(rps.year) + 2000;
    46. uint8_t month = READ_BCD(rps.month);
    47. uint8_t date = READ_BCD(rps.date);
    48. uint8_t week = READ_BCD(rps.day_of_week);
    49. uint8_t hour = READ_BCD(rps.hour);
    50. uint8_t minute = READ_BCD(rps.minute);
    51. uint8_t second = READ_BCD(rps.second);
    52. printf("%d-%d-%d %d %d:%d:%d\r\n", year, month, date, week, hour, minute, second);
    53. }
    54. int main(void)
    55. {
    56. nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    57. systick_config();
    58. Usart0_init();
    59. RTC_config();
    60. while(1) {
    61. RTC_read();
    62. delay_1ms(1000);
    63. }
    64. }

    RTC时钟备份寄存器

    RTC时钟有20个32位(共80字节)通用备份寄存器,能够在省电模式下保存数据。通过备份寄存器可以实现只配置一次时间即可。

    注意:如果在尝试向RTC_BKP0写入数据之前或之后调用了rcu_bkp_reset_enable(),那么这确实会导致写入的数据在系统复位后丢失,因为RTC备份寄存器会被复位重置。因此,要确保在向RTC备份寄存器写入数据并期望这些数据在复位后仍然有效时,不能调用rcu_bkp_reset_enable()。

    1. if( RTC_BKP0 == 0xf234 ){
    2. //说明不是第一次初始化,可以不用重新设置时间
    3. }else{//如果后备寄存器0 的值 不为 0XF234
    4. //设置后备寄存器0的值为 0XF234,标记已经初始化过RTC
    5. RTC_BKP0 = 0xf234;
    6. //初始化RTC时间
    7. RtcTimeConfig(0x23,0x07,0x12,0x03,0x12,0x59,0x50);
    8. }

    总结

    1. 理解了RTC的基本原理,包括其在实时时钟设计中的作用和结构。
    2. 掌握了初始化RTC的步骤,包括加载依赖、配置时钟源和初始化RTC等。
    3. 理解了设置时间的方法,包括配置时钟参数和异步同步分频器的数值等。
    4. 掌握了读取时间的操作,包括BCD格式转换和从RTC中获取日期和时间信息。
  • 相关阅读:
    在nodejs中操作数据库--express项目
    php实战案例记录(11)unset函数的用法
    J3D在UOS+KIRIN崩溃2:深层原因分析
    【React】React-Redux基本使用
    信创操作系统--麒麟Kylin桌面操作系统 (项目十 安全中心)
    #LLM入门|Prompt#1.9_总结_Summary
    怎样在应用中实现自助报表功能
    配置环境的坑坑洼洼
    Lua调试函数 debug.getinfo() namewhat详解
    基于JSP的二手车交易网站
  • 原文地址:https://blog.csdn.net/xuewenyu_/article/details/139628529