• GD32F10X ----RTC


     1. RTC的简介 

    STM32 的实时时钟(RTC)是一个独立的定时器。STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
            RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。


     2. RTC的框图

    这里用的是STM32其实与GD都是差不多。

     RTC 由两个主要部分组成 第一部分(APB1 接口),第二部分是后备区域。

     RT_DIV寄存器设置可编程产生 1 秒的 RTC 时间基准 TR_CLK。每一秒到来RTC_CNT

    寄存器的值就会加1。RTC_CNT是32位的寄存器。1秒到还可以产生中断。以及溢出中断。以及闹钟中断。(当RTC_ALR寄存器与RTC_CNT一样)。

    我们通过读取RTC_CNT的大小(多少秒)然后转换成实时时钟(年,月,日,时,分,秒)

    如果我要设置一个实时时钟转成秒然后设置到RTC_CNT。

    不管是设置与获取都是操作RTC_CNT并且单位是秒。具体怎么转成实时时钟要自己写逻辑。

    实时时钟都是以1970年1月1日00 :00:00为开始。(红色字是重点)

    由于RTC_CNT是32位的,可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右。所以最大:1970 + 136。如果大于这个年就会溢出。

    3. 代码实现

     RTC.h

    1. #ifndef _RTC_H
    2. #define _RTC_H
    3. #include "gd32f10x.h"
    4. #include
    5. //日期时间结构体
    6. typedef struct{
    7. //时间
    8. uint8_t hour;
    9. uint8_t min;
    10. uint8_t sec;
    11. //日期
    12. uint16_t w_year;
    13. uint8_t w_month;
    14. uint8_t w_day;
    15. }_calender_obj;
    16. extern _calender_obj calender; //日期、时间结构体变量
    17. extern uint8_t const month_table[12];
    18. void RTC_Config(void); //RTC配置
    19. uint8_t RTC_Init(void); //RTC初始化
    20. void RTC_NVIC_Config(void); //配置RTC中断
    21. uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec); //将设置时间转化为秒数,给到RTC_CNT
    22. uint8_t RTC_Get(void); //得到RTC_CNT的值并转换为日期时间
    23. uint8_t Is_Leap_Year(uint16_t year); //判断year是否闰年
    24. uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day);
    25. #endif

    RTC.c

    1. #include "RTC.h"
    2. _calender_obj calender;
    3. uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
    4. uint8_t const month_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年月份的天数
    5. uint32_t timecount = 0;
    6. //RTC配置
    7. void RTC_Config(void){
    8. rcu_periph_clock_enable(RCU_BKPI); //备份区域的时钟要先使能
    9. rcu_periph_clock_enable(RCU_PMU); //电源管理时钟使能
    10. pmu_backup_write_enable(); //使能备份域访问允许
    11. bkp_deinit(); //备份域复位
    12. rcu_osci_on(RCU_LXTAL); //使能外部低速时钟
    13. rcu_osci_stab_wait(RCU_LXTAL); //等待外部低速时钟稳定
    14. rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); //时钟源选择
    15. rcu_periph_clock_enable(RCU_RTC); //使能RTC时钟
    16. rtc_register_sync_wait(); //等待寄存器与APB1时钟同步
    17. rtc_lwoff_wait(); //等待RTC的最后一次操作完成
    18. rtc_interrupt_enable(RTC_INT_SECOND);//使能RTC的秒中断
    19. rtc_lwoff_wait(); //等待RTC的最后一次操作完成
    20. rtc_prescaler_set(32767); /* 配置RTC_PRL的值(时钟分频) */
    21. rtc_lwoff_wait(); //等待RTC的最后一次操作完成
    22. }
    23. //RTC初始化
    24. uint8_t RTC_Init(void){
    25. RTC_Config();
    26. RTC_Set(2023, 8, 21, 23, 13, 15);
    27. RTC_NVIC_Config();//配置中断的优先级
    28. return 0;
    29. }
    30. // 配置RTC的中断优先级
    31. void RTC_NVIC_Config(void){
    32. nvic_irq_enable(RTC_IRQn, 2, 0);
    33. }
    34. //RTC的中断服务函数
    35. void RTC_IRQHandler(void){
    36. if(rtc_flag_get(RTC_FLAG_SECOND) != RESET){ //判断是否为秒中断
    37. rtc_flag_clear(RTC_FLAG_SECOND);
    38. RTC_Get();
    39. printf("Now time is: %d-%d-%d %d:%d:%d\r\n", calender.w_year, calender.w_month, calender.w_day, calender.hour, calender.min, calender.sec);
    40. }
    41. }
    42. //将设置时间转化为秒数,给到RTC_CNT
    43. uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec){
    44. uint32_t seccounts = 0;
    45. uint16_t temp_year = 1970;
    46. uint8_t temp_month;
    47. if(syear<1970 || syear>2099){ //设置的时间不合理
    48. return 1;
    49. }
    50. //整年的秒数
    51. while(temp_year < syear){
    52. if(Is_Leap_Year(temp_year))seccounts += 31622400; //闰年,一年的秒数 366* 24 * 60 *60
    53. else seccounts += 31536000; //平年,一年的秒数 365* 24 * 60 *60
    54. temp_year++;
    55. }
    56. //整月的秒数
    57. smonth--;
    58. for(temp_month = 0; temp_month
    59. seccounts += (uint32_t)month_table[temp_month]*86400;
    60. if(Is_Leap_Year(syear)&&temp_month==1)seccounts += 86400; //如果设置的年份是闰年,在二月这个月份要加多一天
    61. }
    62. //日、时、分、秒的处理
    63. seccounts += (uint32_t)(sday-1)*86400; //整日的秒数 24 * 60 * 60
    64. seccounts += (uint32_t)shour*3600;//小时
    65. seccounts += (uint32_t)smin*60; //分
    66. seccounts += ssec; //秒
    67. rtc_lwoff_wait();
    68. rtc_counter_set(seccounts);
    69. return 0;
    70. }
    71. //得到RTC_CNT的值并转换为日期时间
    72. uint8_t RTC_Get(void){
    73. //把timecount转换为日期时间,并赋给calender
    74. uint32_t temp_days = timecount/86400;
    75. uint16_t temp_year = 1970;
    76. uint16_t temp_month;
    77. uint32_t temp_seconds;
    78. timecount = rtc_counter_get();//读取RTC_CNT寄存器的值
    79. //处理天数中的整年,
    80. if(temp_days>0){
    81. while(temp_days>=365){
    82. if(Is_Leap_Year(temp_year)){//如果是闰年
    83. if(temp_days>365){
    84. temp_days -= 366;
    85. }
    86. else{
    87. break;
    88. }
    89. }else{
    90. temp_days -= 365;
    91. }
    92. temp_year++;
    93. }
    94. calender.w_year = temp_year;
    95. //剩下不足一年的,再处理整月
    96. temp_month = 1; //用来临时存放月份
    97. while(temp_days >= 28){ //超过了一个月
    98. if(Is_Leap_Year(calender.w_year) && temp_month == 2){
    99. if(temp_days>=29){ //闰年的2月是29天
    100. temp_days -= 29;
    101. }else{
    102. break;
    103. }
    104. }else{
    105. if(temp_days >= month_table[temp_month-1]){//剩余的天数是不是大于temp_month这个月整月的天数
    106. temp_days -= month_table[temp_month-1];
    107. }else{
    108. break;
    109. }
    110. }
    111. temp_month++;
    112. }
    113. }
    114. calender.w_month = temp_month;
    115. calender.w_day = temp_days+1;
    116. //处理剩下的不足一天的秒数,时:分:秒
    117. temp_seconds = timecount%86400; //不足一天的秒数
    118. calender.hour = temp_seconds/3600;
    119. calender.min = (temp_seconds%3600)/60;
    120. calender.sec = temp_seconds%60;
    121. return 0;
    122. }
    123. uint8_t Is_Leap_Year(uint16_t year){ //判断year是否闰年
    124. if(year%4 == 0){
    125. if(year%100 == 0){
    126. if(year%400 == 0)
    127. return 1;
    128. else
    129. return 0;
    130. }else{
    131. return 1;
    132. }
    133. }else{
    134. return 0;
    135. }
    136. }
    137. //获得现在是星期几
    138. //功能描述:输入公历日期得到星期(只允许1901-2099年)
    139. //year,month,day:公历年月日
    140. //返回值:星期号
    141. uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day)
    142. {
    143. uint16_t temp2;
    144. uint8_t yearH,yearL;
    145. yearH=year/100; yearL=year%100;
    146. // 如果为21世纪,年份数加100
    147. if (yearH>19)yearL+=100;
    148. // 所过闰年数只算1900年之后的
    149. temp2=yearL+yearL/4;
    150. temp2=temp2%7;
    151. temp2=temp2+day+table_week[month-1];
    152. if (yearL%4==0&&month<3)temp2--;
    153. return(temp2%7);
    154. }

    main.c

    1. #include "gd32f10x_eval.h"
    2. #include "LED.h"
    3. #include "SYSTICK_DELAY.h"
    4. #include "RTC.h"
    5. int main(){
    6. gd_eval_com_init(EVAL_COM0); // 初始化USART0
    7. LED_Init();
    8. my_systick_config();
    9. printf("This is a RTC DEMO test.\r\n");
    10. RTC_Init();
    11. while(1){
    12. LED1_Toggle();
    13. my_systick_delay_ms(1000); //delay 1000 ms
    14. }
    15. }
    16. /*重写fputc*/
    17. int fputc(int ch, FILE *f)
    18. {
    19. usart_data_transmit(EVAL_COM0,ch); //通过串口把ch给发送出去
    20. while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE));
    21. return ch;
    22. }

    通过串口工具来显示实时时钟。先设置然后在读取通过串口显示出来。

     

  • 相关阅读:
    从零开始学JAVA(04):数组
    SparkSQL执行流程与Catalyst优化器
    AnimalTFDB v4.0 | OMG!我最爱的转录因子数据库更新啦!~(附使用指南)(二)
    Flink作业任务部署解读
    卡尔曼滤波公式理解
    曝光量近400万,小红书入秋玩出九件套,数据解析如何运用仪式感?
    Jetson tx2记录422测试笔记和wifi信号测试笔记
    如何设计一个完美的权限管理模块
    直播互动流程
    移动通信网络规划:误码率
  • 原文地址:https://blog.csdn.net/qq_41328470/article/details/133468315