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


浙公网安备 33010602011771号