[活动专区] 【杰发科技AC7802x测评】5. RTC应用之实现RTC日历

[复制链接]
 楼主| hehung 发表于 2023-6-11 17:58 | 显示全部楼层 |阅读模式
<
#技术资源# #申请原创#
1 RTC介绍

RTC是芯片内部自带的实时时钟芯片,用于精确的自动累计时间,用户字需要设置初始时间,RTC就会自动累计,减少了CPU参与,降低了CPU的负载率,而且RTC的精度很高。本帖将测试AC7802X的RTC。

关于RTC的介绍,可以参考用户手册《ATC_AC7802x_ReferenceManual_CH.pdf》18 实时计数器模块(RTC)

>  AC7802X的RTC支持功能较少,没有硬件上的年月日时分秒设置以及获取功能。只支持实时数以及低功耗唤醒功能。本帖将使用软件的方式实现年月日,星期,时分秒的设置,自动累计以及获取功能

1686476354975.png


本实验通过软件的方式实现了日期时间,星期的自动累计,星期会自动计算,不用手动设置。

2 软件实现

2.1 打开RTC功能

如需正常使用RTC功能,需要现在"Manage Run-Time Enviromment"中加载RTC功能,如下图。

1686476416985.png

2.2 初始化RTC

如下代码,实现了对RTC的初始化,并且设置了1s进入一次RTC中断,中断的回调函数为“Rtc_CallbackFunc”。

其中中断间隔时间设置为"(99+1)*(159999+1)/16000000 = 1s"。

Rtc_UserSetDateTime为软件实现的设置当前时间日期的函数,后面说明。

  1. // 初始化Rtc,设置RTC超时时间为1s
  2. void Rtc_UserInit(void)
  3. {
  4.     uint32_t tmpMod = 159999;                       /*!设置RTC模值 */
  5.     uint32_t tmpPrescalerValue = 99;                /*!设置分频比 */
  6.     RTC_ConfigType RTCConfig;                       /*! (99+1)*(159999+1)/16000000 = 1s, timeout = (pre+1)*(mod+1)/clk */
  7.   
  8.     memset(&RTCConfig,0,sizeof(RTCConfig));

  9.     RTCConfig.clockSource = RTC_CLOCK_APB;          /*! 时钟源选择:RTC_CLOCK_APB,默认为16M */                                                
  10.     RTCConfig.periodValue = tmpMod;                 /*! 模值赋给RTCConfig结构体 */
  11.     RTCConfig.psrInterruptEn = DISABLE;             /*! 关闭实时预分频器中断 */
  12.     RTCConfig.rtcInterruptEn = ENABLE;              /*! RTC中断使能 */
  13.     RTCConfig.psrValue = tmpPrescalerValue;         /*! 预分频比赋值给RTCConfig结构体 */
  14.     RTCConfig.rtcOutEn = DISABLE;                   /*! 禁能RTC输出 */
  15.     RTCConfig.callBack = Rtc_CallbackFunc;           /*! RTC回调函数配置 */
  16.   
  17.     RTC_Init(&RTCConfig);                           /*! RTC初始化函数生效 */

  18.     Rtc_UserSetDateTime();
  19. }



2.3 时间累计函数

由于AC7802X没有提供硬件上的日期时间的自动累计,所以需要以来软件来实现。

>  硬件上没有实现日期时间的累计功能,RTC计时器就显得比较鸡肋,因为定时器可以实现同样的功能。

- Rtc_IsLeapYear函数用于判断指定年份是否为闰年
- Rtc_FindWeekDay为泰勒公式的软件实现,用于根据年月日计算星期。就算不设置星期,也可以自动计算出来。
- Rtc_DateTimeIncrease为日期时间累计函数,当初试时间设置之后,该函数会自动累计时间。该函数需要放在回调函数中,每一秒钟调用一次。

  1. // 判断某一年是否为闰年
  2. static uint8_t Rtc_IsLeapYear(uint8_t year)
  3. {
  4.     uint16_t full_year = 2000 + year;

  5.     if (((full_year % 4 == 0 ) && (full_year % 100 != 0)) ||
  6.          (year % 400 == 0))
  7.     {
  8.         return 1; // 闰年
  9.     }
  10.     else
  11.     {
  12.         return 0; // 非闰年
  13.     }
  14. }

  15. // 泰勒公式求星期
  16. static uint8_t Rtc_FindWeekDay(void)
  17. {
  18.     int m = current_dt.mon;
  19.     int d = current_dt.mday;
  20.     int year = current_dt.year + 2000;

  21.     // 根据月份对年份和月份进行调整
  22.     if(m <= 2)
  23.     {
  24.         year -= 1;
  25.         m += 12;
  26.     }

  27.     int c = year / 100; // 取得年份前两位
  28.     int y = year % 100; // 取得年份后两位
  29.    
  30.     // 根据泰勒公式计算星期
  31.     int w = (int)(c/4) - 2*c + y + (int)(y/4)
  32.         + (int)(13*(m+1)/5) + d - 1;

  33.   
  34.     return w%7; // 返回星期
  35. }

  36. // 日期时间累计函数,硬件没有实现,需要软件实现
  37. static void Rtc_DateTimeIncrease(void)
  38. {
  39.     // seconds increase
  40.     if (current_dt.sec >= 59U)
  41.     {
  42.         current_dt.sec = 0U;
  43.    
  44.         if (current_dt.min >= 59U)
  45.         {
  46.             current_dt.min = 0;
  47.         
  48.             if (current_dt.hour >= 23)
  49.             {
  50.                 current_dt.hour = 0U;
  51.             
  52.                 if (current_dt.mday >= month_day_num[current_dt.mon-1])
  53.                 {
  54.                     current_dt.mday = 1;
  55.                     if (current_dt.mon >= 12)
  56.                     {
  57.                         current_dt.mon = 1;
  58.                         current_dt.year++;
  59.                         // 年变化时,求2月天数
  60.                         month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
  61.                     }
  62.                     else
  63.                     {
  64.                         current_dt.mon++;
  65.                     }
  66.                 }
  67.                 else
  68.                 {
  69.                     current_dt.mday++;
  70.                 }
  71.             
  72.                 if (current_dt.weekday >= 7U)
  73.                 {
  74.                     current_dt.weekday = 0U;
  75.                 }
  76.                 else
  77.                 {
  78.                     current_dt.weekday++;
  79.                 }
  80.             }
  81.             else
  82.             {
  83.                 current_dt.hour++;
  84.             }
  85.         }
  86.         else
  87.         {
  88.             current_dt.min++;
  89.         }
  90.     }
  91.     else
  92.     {
  93.         current_dt.sec++;
  94.     }

  95. }



2.4 全部代码

下面是全部代码,包括了RTC初始化,时间设置,打印等。

每一秒中打印一次当前时间,日期,星期。

设定的初始时间为2023年12月31日23点59分50秒,为了测试时间累计函数是否成功。

  1. /*
  2. @hehung
  3. 2023-6-11
  4. email: 1398660197@qq.com
  5. wechat: hehung95
  6. reproduced and please indicate the source @hehung
  7. */

  8. #include <stdbool.h>
  9. #include <string.h>
  10. #include <stdio.h>
  11. #include "ac780x_gpio.h"
  12. #include "ac780x_rtc.h"
  13. #include "ac7802x.h"
  14. #include "app_rtc.h"

  15. static rtc_time_t current_dt = {0, 0, 0, 0, 0, 0, 0};
  16. static uint8_t month_day_num[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


  17. static void Rtc_CallbackFunc(void *device, uint32_t wpara, uint32_t lpara);
  18. static uint8_t Rtc_IsLeapYear(uint8_t year);
  19. static void Rtc_DateTimeIncrease(void);


  20. // Rtc 回调函数
  21. static void Rtc_CallbackFunc(void *device, uint32_t wpara, uint32_t lpara)
  22. {
  23.     if (wpara & RTC_SC_RTIF_Msk)                                          /*! RTC溢出中断 */
  24.     {
  25.         Rtc_DateTimeIncrease();
  26.         Rtc_ShowTime();
  27.     }   
  28. }


  29. // 判断某一年是否为闰年
  30. static uint8_t Rtc_IsLeapYear(uint8_t year)
  31. {
  32.     uint16_t full_year = 2000 + year;

  33.     if (((full_year % 4 == 0 ) && (full_year % 100 != 0)) ||
  34.          (year % 400 == 0))
  35.     {
  36.         return 1; // 闰年
  37.     }
  38.     else
  39.     {
  40.         return 0; // 非闰年
  41.     }
  42. }

  43. // 泰勒公式求星期
  44. static uint8_t Rtc_FindWeekDay(void)
  45. {
  46.     int m = current_dt.mon;
  47.     int d = current_dt.mday;
  48.     int year = current_dt.year + 2000;

  49.     // 根据月份对年份和月份进行调整
  50.     if(m <= 2)
  51.     {
  52.         year -= 1;
  53.         m += 12;
  54.     }

  55.     int c = year / 100; // 取得年份前两位
  56.     int y = year % 100; // 取得年份后两位
  57.    
  58.     // 根据泰勒公式计算星期
  59.     int w = (int)(c/4) - 2*c + y + (int)(y/4)
  60.         + (int)(13*(m+1)/5) + d - 1;

  61.   
  62.     return w%7; // 返回星期
  63. }

  64. // 日期时间累计函数,硬件没有实现,需要软件实现
  65. static void Rtc_DateTimeIncrease(void)
  66. {
  67.     // seconds increase
  68.     if (current_dt.sec >= 59U)
  69.     {
  70.         current_dt.sec = 0U;
  71.    
  72.         if (current_dt.min >= 59U)
  73.         {
  74.             current_dt.min = 0;
  75.         
  76.             if (current_dt.hour >= 23)
  77.             {
  78.                 current_dt.hour = 0U;
  79.             
  80.                 if (current_dt.mday >= month_day_num[current_dt.mon-1])
  81.                 {
  82.                     current_dt.mday = 1;
  83.                     if (current_dt.mon >= 12)
  84.                     {
  85.                         current_dt.mon = 1;
  86.                         current_dt.year++;
  87.                         // 年变化时,求2月天数
  88.                         month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
  89.                     }
  90.                     else
  91.                     {
  92.                         current_dt.mon++;
  93.                     }
  94.                 }
  95.                 else
  96.                 {
  97.                     current_dt.mday++;
  98.                 }
  99.             
  100.                 if (current_dt.weekday >= 7U)
  101.                 {
  102.                     current_dt.weekday = 0U;
  103.                 }
  104.                 else
  105.                 {
  106.                     current_dt.weekday++;
  107.                 }
  108.             }
  109.             else
  110.             {
  111.                 current_dt.hour++;
  112.             }
  113.         }
  114.         else
  115.         {
  116.             current_dt.min++;
  117.         }
  118.     }
  119.     else
  120.     {
  121.         current_dt.sec++;
  122.     }

  123. }


  124. // 设置当前时间
  125. void Rtc_DateTimeInit(rtc_time_t dt)
  126. {
  127.     current_dt.year    = dt.year;
  128.     current_dt.mon     = dt.mon;
  129.     current_dt.mday    = dt.mday;
  130.     current_dt.weekday = dt.weekday;
  131.     current_dt.hour    = dt.hour;
  132.     current_dt.min     = dt.min;
  133.     current_dt.sec     = dt.sec;

  134.     // 2月天数
  135.     month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
  136.     // 根据泰勒公式计算真实星期
  137.     current_dt.weekday = Rtc_FindWeekDay();
  138. }

  139. void Rtc_GetDateTime(rtc_time_t *date_time)
  140. {
  141.     *date_time = current_dt;
  142. }

  143. void Rtc_ShowTime(void)
  144. {
  145.     static const char *week[7] = {"Sun.", "Mon.", "Tue.", "Wed.", "Thu.", "Fri.", "Sat."};  
  146.     rtc_time_t date_time;

  147.     Rtc_GetDateTime(&date_time);


  148.     printf ("DateTime:20%02d-%02d-%02d(%s) %02d:%02d:%02d\r\n", date_time.year,
  149.                                           date_time.mon,
  150.                                           date_time.mday,
  151.                                           week[(uint8_t)date_time.weekday],
  152.                                           date_time.hour,
  153.                                           date_time.min,
  154.                                           date_time.sec);
  155. }



  156. // 初始化Rtc,设置RTC超时时间为1s
  157. void Rtc_UserInit(void)
  158. {
  159.     uint32_t tmpMod = 159999;                       /*!设置RTC模值 */
  160.     uint32_t tmpPrescalerValue = 99;                /*!设置分频比 */
  161.     RTC_ConfigType RTCConfig;                       /*! (99+1)*(159999+1)/16000000 = 1s, timeout = (pre+1)*(mod+1)/clk */
  162.   
  163.     memset(&RTCConfig,0,sizeof(RTCConfig));

  164.     RTCConfig.clockSource = RTC_CLOCK_APB;          /*! 时钟源选择:RTC_CLOCK_APB,默认为16M */                                                
  165.     RTCConfig.periodValue = tmpMod;                 /*! 模值赋给RTCConfig结构体 */
  166.     RTCConfig.psrInterruptEn = DISABLE;             /*! 关闭实时预分频器中断 */
  167.     RTCConfig.rtcInterruptEn = ENABLE;              /*! RTC中断使能 */
  168.     RTCConfig.psrValue = tmpPrescalerValue;         /*! 预分频比赋值给RTCConfig结构体 */
  169.     RTCConfig.rtcOutEn = DISABLE;                   /*! 禁能RTC输出 */
  170.     RTCConfig.callBack = Rtc_CallbackFunc;           /*! RTC回调函数配置 */
  171.   
  172.     RTC_Init(&RTCConfig);                           /*! RTC初始化函数生效 */

  173.     Rtc_UserSetDateTime();
  174. }

  175. void Rtc_UserSetDateTime(void)
  176. {
  177.     rtc_time_t dt;

  178.     dt.year = 23;
  179.     dt.mon = 12;
  180.     dt.mday = 31;
  181.     dt.hour = 23;
  182.     dt.min = 59;
  183.     dt.sec = 50;

  184.     Rtc_DateTimeInit(dt);
  185. }


  186. ```

  187. 2.5 主函数

  188. 主函数中只需要调用Rtc_UserInit即可。

  189. ```cpp
  190. int main(void)
  191. {
  192.     InitDelay();
  193.   
  194.     UART_Cfg_Init();                          /*! 串口1初始化 */

  195.     Rtc_UserInit();

  196.     while(1)
  197.     {

  198.     }
  199. }




3 实验效果

下面展示了实现效果。

初始时间为2023年12月31日23点59分50秒,目的是测试时间累计功能是否成功。

不用设置星期参数,就算设置了也不会识别,因为星期参数会通过泰勒公式自动计算出来,实验验证无误。

1686477132740.png

2023年12月31的日历如下:

1686477179326.png




xixi2017 发表于 2023-6-13 09:11 | 显示全部楼层
厂家做的好啊,外设驱动都集成到RTE里面了,方便配置和修改工程。

评论

是的,这点做的很好,集成开发起来很方便,都不需要花时间搭建开发环境  发表于 2023-6-13 17:28
sperper 发表于 2024-7-26 10:39 | 显示全部楼层
本帖最后由 sperper 于 2024-8-16 17:48 编辑

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

本版积分规则

10

主题

66

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部