CH592万年历示例

本篇基于CH592的内部RC实现,其精度约1000ppm,精度较低,不适合对时间敏感的应用;
其次,需要联网获取北京时间进行定时校准,建议每小时一次;
原理:ppm(Parts Per Million)代表“百万分之几”,用于描述时钟频率的精度;1ppm的误差意味着每100万秒会产生大约1秒的误差;

误差计算

  • 每日误差 :精度(ppm) × 每日秒数 ÷ 1,000,000

    • 计算:1000 × (24 × 60 × 60) ÷ 1,000,000 = 86.4秒/天
    • 这意味着,每天的时间误差最大可能达到约86.4秒 。
  • 每月误差 (以30天计) :每日误差 × 30每年误差 (以365天计) :每日误差 × 365

    • 计算:86.4秒/天 × 30天 = 2592秒 (约43.2分钟)
    • 运行一个月后,误差可能累积到接近43分钟 。
  • 每年误差 (以365天计) :每日误差 × 365

    • 计算:86.4秒/天 × 365天 = 31536秒 (约8.76小时)

    • 运行一年后,误差可能累积到接近9小时 

 

校准周期建议

考虑到1000ppm的RTC误差累积速度非常快,不建议将其用于对时间精度有严格要求的“万年历”应用 。它的主要定位通常是满足系统基本的低功耗计时唤醒需求,而非提供高精度日历时间。

如果必须使用,校准周期需要根据您对时间偏差的容忍度来决定:

  • 高频校准 (适用于对时间较敏感的场景) :中低频校准 (适用于容忍度较高的场景) :

    • 建议每天或每周 进行一次校准。例如,如果允许的最大误差是1分钟,那么大约每17小时就需要校准一次。
    • 如果允许误差在几分钟到几十分钟,可以每月 校准一次。

    • 如果允许误差达到数小时,可以每年 校准一次,但这对于“日历”功能来说,体验会非常差。


 

程序部分:

/*将__DATE__中的月份转为阿拉伯数字*/
int monthAbbrToNumberSwitch(const char* month)
{
    if (strcmp(month, "Jan") == 0) return 1;
    if (strcmp(month, "Feb") == 0) return 2;
    if (strcmp(month, "Mar") == 0) return 3;
    if (strcmp(month, "Apr") == 0) return 4;
    if (strcmp(month, "May") == 0) return 5;
    if (strcmp(month, "Jun") == 0) return 6;
    if (strcmp(month, "Jul") == 0) return 7;
    if (strcmp(month, "Aug") == 0) return 8;
    if (strcmp(month, "Sep") == 0) return 9;
    if (strcmp(month, "Oct") == 0) return 10;
    if (strcmp(month, "Nov") == 0) return 11;
    if (strcmp(month, "Dec") == 0) return 12;
    return -1;
}

 

#include <stdio.h>
int
year,month,day,hour,minute,second; /*获取固件编译时的年、月、日、时、分、秒*/ void get_time(int *Year,int *Month,int *Day,int *Hour,int *Minute,int *Second) { // 使用预定义宏 char date[] = __DATE__; // 例如:"Dec 8 2025" char time[] = __TIME__; // 例如:"11:36:12" // 解析日期部分 char temp_month[4]; int temp_day, temp_year; sscanf(date, "%3s %d %d", temp_month, &temp_day, &temp_year); // 解析时间部分 int temp_hour, temp_minute, temp_second; sscanf(time, "%d:%d:%d", &temp_hour, &temp_minute, &temp_second); // 输出结果 PRINT("原始日期: %s\n", date); PRINT("原始时间: %s\n", time); PRINT("解析结果:\n"); PRINT("- 年份: %d\n", temp_year); PRINT("- 月份: %d\n", monthAbbrToNumberSwitch(temp_month)); PRINT("- 日期: %d\n", temp_day); PRINT("- 小时: %d\n", temp_hour); PRINT("- 分钟: %d\n", temp_minute); PRINT("- 秒数: %d\n", temp_second); *Year = temp_year; *Month = monthAbbrToNumberSwitch(temp_month); *Day = temp_day; *Hour = temp_hour; *Minute = temp_minute; *Second = temp_second; }

 

/*********************************************************************
 * @fn      main
 *
 * @brief   主函数
 *
 * @return  none
 */
void main()
{
    SetSysClock(CLK_SOURCE_PLL_60MHz);

    GPIOA_ModeCfg( GPIO_Pin_All, GPIO_ModeIN_PU );
    GPIOB_ModeCfg( GPIO_Pin_All, GPIO_ModeIN_PU );
    /* 配置串口调试 */
    DebugInit();
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);

    get_time(&year,&month,&day,&hour,&minute,&second);//模拟获取当前最新日期、时间
    Calibration_LSI(Level_128);//校准一次内部32K
    RTC_InitTime(year,month,day,hour,minute,second);//非蓝牙程序,可直接初始化
    RTC_TRIGFunCfg(32768);//32768为1s,1s周期触发RTC中断
    PFIC_EnableIRQ(RTC_IRQn);//使能RTC中断

    while(1);
}

 

__INTERRUPT
__HIGH_CODE
void RTC_IRQHandler(void)
{
    RTC_TRIGFunCfg(32768);//再次设置RTC下次1s后触发
    UINT16 py; UINT16 pmon; UINT16 pd; UINT16 ph; UINT16 pm; UINT16 ps;
    RTC_GetTime(&py,&pmon,&pd,&ph,&pm,&ps);
    if (RTC_GetITFlag(RTC_TRIG_EVENT)) //事件触发标志
    {
        PRINT("%d年%d月%d日%d时%d分%d秒\r\n",py,pmon,pd,ph,pm,ps);
        RTC_ClearITFlag(RTC_TRIG_EVENT);
    }
}

 

image

 

posted @ 2025-12-08 13:17  oTvTo  阅读(8)  评论(0)    收藏  举报