abner_ma 发表于 2025-7-31 18:26

【CH32V317W-R0开发板】RTC测试

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


   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;当主计数器增加到和闹钟寄存器的值一致时,会触发闹钟事件;当主计数器自增到溢出时,会触发溢出事件。以上三种事件都可以触发中断,并对应相应中断使能位控制。

   供电方案:

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闹钟或秒输出。

#include "debug.h"

/* Global define */

/* Global Variable */
typedef struct
{
    vu8 hour;
    vu8 min;
    vu8 sec;

    vu16 w_year;
    vu8w_month;
    vu8w_date;
    vu8week;
} _calendar_obj;

_calendar_obj calendar;

u8 const table_week = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
const u8 mon_table = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* Exported_Functions */
u8 RTC_Init(void);
u8 Is_Leap_Year(u16 year);
u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);
u8 RTC_Get(void);
u8 RTC_Get_Week(u16 year, u8 month, u8 day);
u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);

/*********************************************************************
* @fn      RTC_NVIC_Config
*
* @brief   Initializes RTC Int.
*
* @returnnone
*/
static void RTC_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure = {0};
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*********************************************************************
* @fn      RTC_Init
*
* @brief   Initializes RTC collection.
*
* @return1 - Init Fail
*          0 - Init Success
*/
u8 RTC_Init(void)
{
    u8 temp = 0;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_ClearITPendingBit(RTC_IT_ALR);
    RTC_ClearITPendingBit(RTC_IT_SEC);

    /* Is it the first configuration */

    BKP_DeInit();
    RCC_LSEConfig(RCC_LSE_ON);
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)
    {
      temp++;
      Delay_Ms(20);
    }
    if(temp >= 250)
      return 1;
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    RCC_RTCCLKCmd(ENABLE);
    RTC_WaitForLastTask();
    RTC_WaitForSynchro();
    //                RTC_ITConfig(RTC_IT_ALR, ENABLE);
    RTC_ITConfig(RTC_IT_SEC, ENABLE);
    RTC_WaitForLastTask();
    RTC_EnterConfigMode();
    RTC_SetPrescaler(32767);
    RTC_WaitForLastTask();
    RTC_Set(2019, 10, 8, 13, 58, 55); /* Setup Time */
    RTC_ExitConfigMode();
    BKP_WriteBackupRegister(BKP_DR1, 0XA1A1);

    RTC_NVIC_Config();
    RTC_Get();

    return 0;
}

/*********************************************************************
* @fn      Is_Leap_Year
*
* @brief   Judging whether it is a leap year.
*
* @param   year
*
* @return1 - Yes
*          0 - No
*/
u8 Is_Leap_Year(u16 year)
{
    if(year % 4 == 0)
    {
      if(year % 100 == 0)
      {
            if(year % 400 == 0)
                return 1;
            else
                return 0;
      }
      else
            return 1;
    }
    else
      return 0;
}

/*********************************************************************
* @fn      RTC_Set
*
* @brief   Set Time.
*
* @param   Struct of _calendar_obj
*
* @return1 - error
*          0 - success
*/
u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
    u16 t;
    u32 seccount = 0;
    if(syear < 1970 || syear > 2099)
      return 1;
    for(t = 1970; t < syear; t++)
    {
      if(Is_Leap_Year(t))
            seccount += 31622400;
      else
            seccount += 31536000;
    }
    smon -= 1;
    for(t = 0; t < smon; t++)
    {
      seccount += (u32)mon_table * 86400;
      if(Is_Leap_Year(syear) && t == 1)
            seccount += 86400;
    }
    seccount += (u32)(sday - 1) * 86400;
    seccount += (u32)hour * 3600;
    seccount += (u32)min * 60;
    seccount += sec;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_SetCounter(seccount);
    RTC_WaitForLastTask();
    return 0;
}

/*********************************************************************
* @fn      RTC_Alarm_Set
*
* @brief   Set Alarm Time.
*
* @param   Struct of _calendar_obj
*
* @return1 - error
*          0 - success
*/
u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
    u16 t;
    u32 seccount = 0;
    if(syear < 1970 || syear > 2099)
      return 1;
    for(t = 1970; t < syear; t++)
    {
      if(Is_Leap_Year(t))
            seccount += 31622400;
      else
            seccount += 31536000;
    }
    smon -= 1;
    for(t = 0; t < smon; t++)
    {
      seccount += (u32)mon_table * 86400;
      if(Is_Leap_Year(syear) && t == 1)
            seccount += 86400;
    }
    seccount += (u32)(sday - 1) * 86400;
    seccount += (u32)hour * 3600;
    seccount += (u32)min * 60;
    seccount += sec;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_SetAlarm(seccount);
    RTC_WaitForLastTask();

    return 0;
}

/*********************************************************************
* @fn      RTC_Get
*
* @brief   Get current time.
*
* @return1 - error
*          0 - success
*/
u8 RTC_Get(void)
{
    static u16 daycnt = 0;
    u32      timecount = 0;
    u32      temp = 0;
    u16      temp1 = 0;
    timecount = RTC_GetCounter();
    temp = timecount / 86400;
    if(daycnt != temp)
    {
      daycnt = temp;
      temp1 = 1970;
      while(temp >= 365)
      {
            if(Is_Leap_Year(temp1))
            {
                if(temp >= 366)
                  temp -= 366;
                else
                {
                  break;
                }
            }
            else
                temp -= 365;
            temp1++;
      }
      calendar.w_year = temp1;
      temp1 = 0;
      while(temp >= 28)
      {
            if(Is_Leap_Year(calendar.w_year) && temp1 == 1)
            {
                if(temp >= 29)
                  temp -= 29;
                else
                  break;
            }
            else
            {
                if(temp >= mon_table)
                  temp -= mon_table;
                else
                  break;
            }
            temp1++;
      }
      calendar.w_month = temp1 + 1;
      calendar.w_date = temp + 1;
    }
    temp = timecount % 86400;
    calendar.hour = temp / 3600;
    calendar.min = (temp % 3600) / 60;
    calendar.sec = (temp % 3600) % 60;
    calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month, calendar.w_date);
    return 0;
}

/*********************************************************************
* @fn      RTC_Get_Week
*
* @brief   Get the current day of the week.
*
* @param   year/month/day
*
* @returnweek
*/
u8 RTC_Get_Week(u16 year, u8 month, u8 day)
{
    u16 temp2;
    u8yearH, yearL;

    yearH = year / 100;
    yearL = year % 100;
    if(yearH > 19)
      yearL += 100;
    temp2 = yearL + yearL / 4;
    temp2 = temp2 % 7;
    temp2 = temp2 + day + table_week;
    if(yearL % 4 == 0 && month < 3)
      temp2--;
    return (temp2 % 7);
}

/*********************************************************************
* @fn      main
*
* @brief   Main program.
*
* @returnnone
*/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);       
    printf("SystemClk:%d\r\n", SystemCoreClock);
    printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
    printf("RTC Test\r\n");
    RTC_Init();

    while(1)
    {
      Delay_Ms(1000);
      printf("year/month/day/week/hour/min/sec:\r\n");
      printf("%d-%d-%d%d%d:%d:%d\r\n", calendar.w_year, calendar.w_month, calendar.w_date,
               calendar.week, calendar.hour, calendar.min, calendar.sec);
    }
}

风之呢喃 发表于 2025-8-1 13:33

rtc不内置的串口屏还是很常见的,毕竟不是刚需。

ningling_21 发表于 2025-8-1 15:47

芯片内置的RTC还是蛮好用的
页: [1]
查看完整版本: 【CH32V317W-R0开发板】RTC测试