[单片机芯片] 【CH32V317W-R0开发板】RTC测试

[复制链接]
 楼主| abner_ma 发表于 2025-7-31 18:26 | 显示全部楼层 |阅读模式
   最近有个项目GUI开发,用的迪文屏(DGUS屏),作为一款常用的串口智能显示屏,不内置独立的RTC(实时时钟)模块,迪文屏主打高性价比和易用性,核心功能聚焦于图形界面显示与触控交互。RTC模块需独立晶振、备份电池及低功耗电路,会增加硬件成本和设计复杂度,不符合其市场定位其时间显示功能需依赖外部MCU或主机设备提供时间数据。    CH32V*具备RTC 功能,实时时钟(RTC)是一个独立的定时器模块,其可编程计数器最大可达到 32 位,配合软件即可以实现实时时钟功能,并且可以修改计数器的值来重新配置系统的当前时间和日期。RTC 模块在后备供电区域,系统复位和待机模式唤醒对其不造成影响。

   5.png
     RTC 模块主要是 PB1 总线接口、分频器和计数器、控制和状态寄存器三部分组成,其中分频器和计数器部分在后备区域,可由 VBAT供电。RTCCLK 输入分频器(RTC_DIV)之后,被分频成TR_CLK。值得注意的是,分频器(RTC_DIV)的内部是一个自减计数器,自减到溢出就会输出一个 TR_CLK,然后从重装值寄存器(RTC_PSCR)里取出预设值重装到分频器里,读分频器实际上是读取它的实时值。(read only),写分频系数应该写到重装值寄存器(RTC_PSCR)里。一般 TR_CLK 的周期被设置为 1秒,TR_CLK 会触发秒事件,同时会使主计数器(RTC_CNT)自增 1;当主计数器增加到和闹钟寄存器的值一致时,会触发闹钟事件;当主计数器自增到溢出时,会触发溢出事件。以上三种事件都可以触发中断,并对应相应中断使能位控制。

   供电方案:
   供电.png
  VDD和VBAT均可连接内部模拟开关为备份区域以及PC13、PC14和PC15引脚供电,这个模拟开关只能够通过有限的电流(3mA)。当由VDD供电时:PC14和PC15可用于GPIO或LSE引脚、PC13可作为通用I/O口、TAMPER引脚、RTC校准时钟、RTC闹钟或秒输出;PC13、PC14和PC15作为GPIO输出脚时只能工作在2MHz模式下,
最大驱动负载为30pF,并且不能作为电流源(如驱动LED)。而当由VBAT供电时:PC14和PC15只能用于LSE引脚、PC13可作为TAMPER引脚、RTC闹钟或秒输出。

  1. #include "debug.h"

  2. /* Global define */

  3. /* Global Variable */
  4. typedef struct
  5. {
  6.     vu8 hour;
  7.     vu8 min;
  8.     vu8 sec;

  9.     vu16 w_year;
  10.     vu8  w_month;
  11.     vu8  w_date;
  12.     vu8  week;
  13. } _calendar_obj;

  14. _calendar_obj calendar;

  15. u8 const table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
  16. const u8 mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  17. /* Exported_Functions */
  18. u8 RTC_Init(void);
  19. u8 Is_Leap_Year(u16 year);
  20. u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);
  21. u8 RTC_Get(void);
  22. u8 RTC_Get_Week(u16 year, u8 month, u8 day);
  23. u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);

  24. /*********************************************************************
  25. * @fn      RTC_NVIC_Config
  26. *
  27. * [url=home.php?mod=space&uid=247401]@brief[/url]   Initializes RTC Int.
  28. *
  29. * [url=home.php?mod=space&uid=266161]@return[/url]  none
  30. */
  31. static void RTC_NVIC_Config(void)
  32. {
  33.     NVIC_InitTypeDef NVIC_InitStructure = {0};
  34.     NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
  35.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  36.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  37.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  38.     NVIC_Init(&NVIC_InitStructure);
  39. }

  40. /*********************************************************************
  41. * @fn      RTC_Init
  42. *
  43. * @brief   Initializes RTC collection.
  44. *
  45. * @return  1 - Init Fail
  46. *          0 - Init Success
  47. */
  48. u8 RTC_Init(void)
  49. {
  50.     u8 temp = 0;
  51.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
  52.     PWR_BackupAccessCmd(ENABLE);
  53.     RTC_ClearITPendingBit(RTC_IT_ALR);
  54.     RTC_ClearITPendingBit(RTC_IT_SEC);

  55.     /* Is it the first configuration */

  56.     BKP_DeInit();
  57.     RCC_LSEConfig(RCC_LSE_ON);
  58.     while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)
  59.     {
  60.         temp++;
  61.         Delay_Ms(20);
  62.     }
  63.     if(temp >= 250)
  64.         return 1;
  65.     RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
  66.     RCC_RTCCLKCmd(ENABLE);
  67.     RTC_WaitForLastTask();
  68.     RTC_WaitForSynchro();
  69.     //                RTC_ITConfig(RTC_IT_ALR, ENABLE);
  70.     RTC_ITConfig(RTC_IT_SEC, ENABLE);
  71.     RTC_WaitForLastTask();
  72.     RTC_EnterConfigMode();
  73.     RTC_SetPrescaler(32767);
  74.     RTC_WaitForLastTask();
  75.     RTC_Set(2019, 10, 8, 13, 58, 55); /* Setup Time */
  76.     RTC_ExitConfigMode();
  77.     BKP_WriteBackupRegister(BKP_DR1, 0XA1A1);

  78.     RTC_NVIC_Config();
  79.     RTC_Get();

  80.     return 0;
  81. }

  82. /*********************************************************************
  83. * @fn      Is_Leap_Year
  84. *
  85. * @brief   Judging whether it is a leap year.
  86. *
  87. * @param   year
  88. *
  89. * @return  1 - Yes
  90. *          0 - No
  91. */
  92. u8 Is_Leap_Year(u16 year)
  93. {
  94.     if(year % 4 == 0)
  95.     {
  96.         if(year % 100 == 0)
  97.         {
  98.             if(year % 400 == 0)
  99.                 return 1;
  100.             else
  101.                 return 0;
  102.         }
  103.         else
  104.             return 1;
  105.     }
  106.     else
  107.         return 0;
  108. }

  109. /*********************************************************************
  110. * @fn      RTC_Set
  111. *
  112. * @brief   Set Time.
  113. *
  114. * @param   Struct of _calendar_obj
  115. *
  116. * @return  1 - error
  117. *          0 - success
  118. */
  119. u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
  120. {
  121.     u16 t;
  122.     u32 seccount = 0;
  123.     if(syear < 1970 || syear > 2099)
  124.         return 1;
  125.     for(t = 1970; t < syear; t++)
  126.     {
  127.         if(Is_Leap_Year(t))
  128.             seccount += 31622400;
  129.         else
  130.             seccount += 31536000;
  131.     }
  132.     smon -= 1;
  133.     for(t = 0; t < smon; t++)
  134.     {
  135.         seccount += (u32)mon_table[t] * 86400;
  136.         if(Is_Leap_Year(syear) && t == 1)
  137.             seccount += 86400;
  138.     }
  139.     seccount += (u32)(sday - 1) * 86400;
  140.     seccount += (u32)hour * 3600;
  141.     seccount += (u32)min * 60;
  142.     seccount += sec;

  143.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
  144.     PWR_BackupAccessCmd(ENABLE);
  145.     RTC_SetCounter(seccount);
  146.     RTC_WaitForLastTask();
  147.     return 0;
  148. }

  149. /*********************************************************************
  150. * @fn      RTC_Alarm_Set
  151. *
  152. * @brief   Set Alarm Time.
  153. *
  154. * @param   Struct of _calendar_obj
  155. *
  156. * @return  1 - error
  157. *          0 - success
  158. */
  159. u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
  160. {
  161.     u16 t;
  162.     u32 seccount = 0;
  163.     if(syear < 1970 || syear > 2099)
  164.         return 1;
  165.     for(t = 1970; t < syear; t++)
  166.     {
  167.         if(Is_Leap_Year(t))
  168.             seccount += 31622400;
  169.         else
  170.             seccount += 31536000;
  171.     }
  172.     smon -= 1;
  173.     for(t = 0; t < smon; t++)
  174.     {
  175.         seccount += (u32)mon_table[t] * 86400;
  176.         if(Is_Leap_Year(syear) && t == 1)
  177.             seccount += 86400;
  178.     }
  179.     seccount += (u32)(sday - 1) * 86400;
  180.     seccount += (u32)hour * 3600;
  181.     seccount += (u32)min * 60;
  182.     seccount += sec;

  183.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
  184.     PWR_BackupAccessCmd(ENABLE);
  185.     RTC_SetAlarm(seccount);
  186.     RTC_WaitForLastTask();

  187.     return 0;
  188. }

  189. /*********************************************************************
  190. * @fn      RTC_Get
  191. *
  192. * @brief   Get current time.
  193. *
  194. * @return  1 - error
  195. *          0 - success
  196. */
  197. u8 RTC_Get(void)
  198. {
  199.     static u16 daycnt = 0;
  200.     u32        timecount = 0;
  201.     u32        temp = 0;
  202.     u16        temp1 = 0;
  203.     timecount = RTC_GetCounter();
  204.     temp = timecount / 86400;
  205.     if(daycnt != temp)
  206.     {
  207.         daycnt = temp;
  208.         temp1 = 1970;
  209.         while(temp >= 365)
  210.         {
  211.             if(Is_Leap_Year(temp1))
  212.             {
  213.                 if(temp >= 366)
  214.                     temp -= 366;
  215.                 else
  216.                 {
  217.                     break;
  218.                 }
  219.             }
  220.             else
  221.                 temp -= 365;
  222.             temp1++;
  223.         }
  224.         calendar.w_year = temp1;
  225.         temp1 = 0;
  226.         while(temp >= 28)
  227.         {
  228.             if(Is_Leap_Year(calendar.w_year) && temp1 == 1)
  229.             {
  230.                 if(temp >= 29)
  231.                     temp -= 29;
  232.                 else
  233.                     break;
  234.             }
  235.             else
  236.             {
  237.                 if(temp >= mon_table[temp1])
  238.                     temp -= mon_table[temp1];
  239.                 else
  240.                     break;
  241.             }
  242.             temp1++;
  243.         }
  244.         calendar.w_month = temp1 + 1;
  245.         calendar.w_date = temp + 1;
  246.     }
  247.     temp = timecount % 86400;
  248.     calendar.hour = temp / 3600;
  249.     calendar.min = (temp % 3600) / 60;
  250.     calendar.sec = (temp % 3600) % 60;
  251.     calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month, calendar.w_date);
  252.     return 0;
  253. }

  254. /*********************************************************************
  255. * @fn      RTC_Get_Week
  256. *
  257. * @brief   Get the current day of the week.
  258. *
  259. * @param   year/month/day
  260. *
  261. * @return  week
  262. */
  263. u8 RTC_Get_Week(u16 year, u8 month, u8 day)
  264. {
  265.     u16 temp2;
  266.     u8  yearH, yearL;

  267.     yearH = year / 100;
  268.     yearL = year % 100;
  269.     if(yearH > 19)
  270.         yearL += 100;
  271.     temp2 = yearL + yearL / 4;
  272.     temp2 = temp2 % 7;
  273.     temp2 = temp2 + day + table_week[month - 1];
  274.     if(yearL % 4 == 0 && month < 3)
  275.         temp2--;
  276.     return (temp2 % 7);
  277. }

  278. /*********************************************************************
  279. * @fn      main
  280. *
  281. * @brief   Main program.
  282. *
  283. * @return  none
  284. */
  285. int main(void)
  286. {
  287.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  288.     SystemCoreClockUpdate();
  289.     Delay_Init();
  290.     USART_Printf_Init(115200);       
  291.     printf("SystemClk:%d\r\n", SystemCoreClock);
  292.     printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
  293.     printf("RTC Test\r\n");
  294.     RTC_Init();

  295.     while(1)
  296.     {
  297.         Delay_Ms(1000);
  298.         printf("year/month/day/week/hour/min/sec:\r\n");
  299.         printf("%d-%d-%d  %d  %d:%d:%d\r\n", calendar.w_year, calendar.w_month, calendar.w_date,
  300.                calendar.week, calendar.hour, calendar.min, calendar.sec);
  301.     }
  302. }
6666.png

风之呢喃 发表于 2025-8-1 13:33 | 显示全部楼层
rtc不内置的串口屏还是很常见的,毕竟不是刚需。
ningling_21 发表于 2025-8-1 15:47 | 显示全部楼层
芯片内置的RTC还是蛮好用的
瞌睡虫本虫 发表于 2025-8-7 08:36 | 显示全部楼层
这个RTC模块的介绍很详细,特别是供电方案和编程部分,对于项目开发来说非常有帮助。
穷得响叮当侠 发表于 2025-8-8 17:40 | 显示全部楼层
看起来你的项目对RTC模块的需求很明确,CH32V系列确实提供了很好的RTC支持。你的代码示例很详细,应该可以很好地满足迪文屏的时间显示需求。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

95

主题

181

帖子

3

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