时间同步已经完成

This commit is contained in:
2026-08-28 16:42:44 +08:00
parent 615a9b13e9
commit 4e9ac0d9c3
20 changed files with 771 additions and 1282 deletions

View File

@@ -44,7 +44,16 @@ static volatile uint32_t s_base_unix_secs = UINT32_MAX;
* 内部辅助函数Unix time ↔ sd2506_time_t 转换
* ============================================================ */
/* 计算星期Zeller 公式0=Sunday与下方 unix_to_sd2506 中算法一致 */
/*
* 函数功能:根据公历日期计算星期几
* 入口参数y - 年份 uint16_t 1970~2099
* m - 月份 uint8_t 1~12
* d - 日 uint8_t 1~31
* 返回值:星期编号 uint8_t 0=Sunday, 1=Monday, ... 6=Saturday
* 限定条件:输入为公历合法日期
* 函数说明1. 采用 Zeller 公式(变种),结果 0 表示周日
* 2. 与 unix_to_sd2506() 内星期算法保持一致
*/
static uint8_t rtc_calc_week(uint16_t y, uint8_t m, uint8_t d)
{
uint32_t wy = y;
@@ -54,10 +63,22 @@ static uint8_t rtc_calc_week(uint16_t y, uint8_t m, uint8_t d)
wy--;
wm += 12u;
}
/* Zeller 公式:世纪项 + 月项 + 日项,模 7 得星期0=周日) */
return (uint8_t)(((uint32_t)(wy % 100u + wy / 100u / 4u - wy / 100u / 15u) +
(uint32_t)((26u * (wm + 1u)) / 10u) + (uint32_t)(d % 100u)) % 7u);
}
/*
* 函数功能:将 Unix 时间戳(自 1970-01-01 00:00:00 起的秒数)转换为 SD2506 RTC 时间结构
* 入口参数unix_secs - Unix 时间戳 uint32_t 0~0x7FFFFFFF~2038
* p_sd_time - 输出时间结构指针 sd2506_time_t* 不得为 NULL
* 出口参数p_sd_time - 填充年/月/日/时/分/秒/星期
* 返回值:无
* 限定条件p_sd_time 必须指向有效缓冲区
* 函数说明1. 先取模拆分时分秒,再按累计天数推算年月日
* 2. 星期由 Zeller 公式(与 rtc_calc_week 同算法)得出
* 3. SD2506 年份字段为相对值0~99+2000故回写时减 2000
*/
static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
{
uint32_t y, m, d;
@@ -108,6 +129,7 @@ static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
wy--;
wm += 12uL;
}
/* Zeller 公式同 rtc_calc_week():世纪项 + 月项 + 日项,模 7 得星期0=周日) */
uint8_t week = ((uint32_t)(wy % 100uLL + wy / 100uLL / 4uLL - wy / 100uLL / 15uLL) +
(uint32_t)(((uint64_t)26 * (wm + 1uL)) / 10uLL) +
(uint32_t)(d % 100uLL)) % 7uLL;
@@ -129,6 +151,17 @@ static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
* 系统墙钟时间维护
* ============================================================ */
/*
* 函数功能:初始化系统墙钟时间基准
* 入口参数:无
* 返回值0 - 成功(始终返回 0
* 限定条件CubeMX 外设初始化完成、SD2506 RTC 驱动已初始化
* 函数说明1. 从 SD2506 读取初始时间必要时按编译期默认宏校时RTC_DEFAULT_INIT_*
* 2. 将 RTC 时间换算为 Unix epoch 秒数
* 3. 记录当前 HAL Tick 与 Unix 秒数为基准,供 sys_clock_get() 后续推算
* 4. 注意sd2506_get_time() 返回的 year 已是完整年份(驱动内 +2000
* 换算时不得再加 2000否则年份虚增导致 Unix 秒数超 uint32 回绕
*/
int sys_clock_init(void)
{
sd2506_time_t sd_time;
@@ -216,6 +249,15 @@ int sys_clock_init(void)
return 0;
}
/*
* 函数功能获取当前系统墙钟时间Unix 时间戳)
* 入口参数:无
* 返回值:当前 Unix 秒数 uint32_t 未初始化时返回 UINT32_MAX
* 限定条件sys_clock_init() 已成功调用
* 函数说明1. 以基准 Tick 与基准 Unix 秒数为起点,按当前 HAL Tick 与 1000ms
* 的整除差推算流逝秒数(避免每毫秒累加,减少漂移与溢出风险)
* 2. 若推算结果为负(基准未建立),返回 UINT32_MAX 表示无效
*/
uint32_t sys_clock_get(void)
{
uint32_t now_tick = HAL_GetTick();
@@ -228,6 +270,17 @@ uint32_t sys_clock_get(void)
return (uint32_t)unix_secs;
}
/*
* 函数功能获取当前墙钟时间的可读字符串YYYY-MM-DD HH:MM:SS
* 入口参数buf - 输出字符串缓冲区 char* 不得为 NULL
* buf_size - 缓冲区大小 size_t 须 ≥ 20含结尾 '\0'
* 出口参数buf - 填充格式化时间字符串
* 返回值buf 指针(便于链式调用)
* 限定条件buf 指向至少 buf_size 字节的有效缓冲区sys_clock_init() 已调用
* 函数说明1. 内部先取 Unix 秒数,未初始化时输出占位串 "----/--/-- --:--:--"
* 2. 通过 unix_to_sd2506() 转换为 SD2506 结构后格式化
* 3. 使用 snprintf 防止缓冲区溢出
*/
char *sys_clock_get_str(char *buf, size_t buf_size)
{
uint32_t unix_secs = sys_clock_get();
@@ -245,6 +298,16 @@ char *sys_clock_get_str(char *buf, size_t buf_size)
return buf;
}
/*
* 函数功能:设置(更新)系统墙钟时间基准,并可写回 SD2506 RTC
* 入口参数unix_secs - 新的 Unix 时间戳 uint32_t 0~0x7FFFFFFF
* sd_time - 对应的 RTC 时间结构指针 sd2506_time_t* 可为 NULL仅更新基准
* 出口参数:无
* 返回值0 - 成功(始终返回 0
* 限定条件unix_secs 为合法 Unix 时间戳
* 函数说明1. 重置基准 Tick 与基准 Unix 秒数,使后续 sys_clock_get() 以此为准
* 2. 若 sd_time 非 NULL将其写回 SD2506 RTC实现断电保持
*/
int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
{
/* Step 1: 更新墙钟基准 */
@@ -261,3 +324,14 @@ int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
return 0;
}
int sys_clock_set_unix(uint32_t unix_secs)
{
sd2506_time_t sd;
/* 内部静态函数:将 Unix 秒数拆分为 SD2506 时间结构 */
unix_to_sd2506(unix_secs, &sd);
/* 更新运行期基准并写回 RTC实现掉电保持 */
return sys_clock_set(unix_secs, &sd);
}