时间终于同步,存储成功

This commit is contained in:
2026-08-28 21:10:51 +08:00
parent 4e9ac0d9c3
commit 02a05a7a63
13 changed files with 606 additions and 78 deletions

View File

@@ -25,7 +25,7 @@
* 修改日期/时间时同步更新下列宏。
* ============================================================ */
#define RTC_DEFAULT_INIT_ENABLE 1
#define RTC_DEFAULT_INIT_FORCE 1
#define RTC_DEFAULT_INIT_FORCE 0
#define RTC_DEFAULT_YEAR 2026
#define RTC_DEFAULT_MONTH 8
#define RTC_DEFAULT_DAY 24
@@ -134,10 +134,10 @@ static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
(uint32_t)(((uint64_t)26 * (wm + 1uL)) / 10uLL) +
(uint32_t)(d % 100uLL)) % 7uLL;
/* SD2506 year 字段为 0~99+2000 */
uint16_t sd_year = y >= 2000 ? (y - 2000) : (y + 2000);
p_sd_time->year = sd_year;
/* sd2506_time_t.year 采用完整年份(如 2026与 sd2506_set_time
* 中 year-2000、sd2506_get_time 中 +2000 的约定保持一致;
* 此处绝不能再减 2000否则写 RTC 时会被二次减 2000 导致年份错乱。 */
p_sd_time->year = (uint16_t)y;
p_sd_time->month = m + 1uL; /* 月份从 0 → 1 */
p_sd_time->day = d;
p_sd_time->hour = h;
@@ -169,22 +169,31 @@ int sys_clock_init(void)
int64_t unix_secs = 0;
int i;
/* Step 1: 从 SD2506 RTC 读取初始时间
* 注意sd2506_get_time() 返回的 year 已是完整年份(如 2026驱动内 +2000
* 此处不得再加 2000否则年份虚增 → unix 秒数超 uint32 被截断回绕。 */
/* ============================================================
* Step 1从 SD2506 硬件 RTC 读取初始时间
* ============================================================
* 注意sd2506_get_time() 返回的 year 已是完整年份(如 2026
* 驱动内部已 +2000。此处【禁止】再 +2000否则年份虚增到 4026
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。 */
sd2506_get_time(&sd_time);
/* RTC 校时(可选):时间无效时,或 FORCE=1 强制时,写入编译期默认时间 */
/* ============================================================
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
* ============================================================
* 作用RTC 出厂/掉电异常导致时间非法时,写入一个可用初始值;
* FORCE=1 时每次启动都强制写入,便于首次烧录或硬件时钟丢失。
* 判定"时间无效":年份越界、月/日越界(仅粗判,不细校 2 月 29 日等)。 */
#if defined(RTC_DEFAULT_INIT_ENABLE)
{
int rtc_invalid = (sd_time.year < 2000 || sd_time.year > 2099 ||
sd_time.month < 1 || sd_time.month > 12 ||
sd_time.day < 1 || sd_time.day > 31);
#if (RTC_DEFAULT_INIT_FORCE == 1)
rtc_invalid = 1; /* 强制每次启动校时 */
rtc_invalid = 1; /* 强制每次启动校时:会覆盖用户/网络已设的正确时间,仅限开发期 */
#endif
if (rtc_invalid) {
sd2506_time_t init_t;
/* 用编译期默认宏构造初始时间week 由 Zeller 公式rtc_calc_week推算 */
init_t.year = RTC_DEFAULT_YEAR;
init_t.month = RTC_DEFAULT_MONTH;
init_t.day = RTC_DEFAULT_DAY;
@@ -193,11 +202,12 @@ int sys_clock_init(void)
init_t.second = RTC_DEFAULT_SECOND;
init_t.week = rtc_calc_week(init_t.year, init_t.month, init_t.day);
/* 写回 SD2506断电后该时间被保留下次上电即为有效值 */
if (sd2506_set_time(&init_t) == SD2506_OK) {
DBG_INFO("sys_clock_init: RTC set %04d-%02d-%02d %02d:%02d:%02d",
init_t.year, init_t.month, init_t.day,
init_t.hour, init_t.minute, init_t.second);
sd2506_get_time(&sd_time); /* 重新读取校准后的时间 */
sd2506_get_time(&sd_time); /* 重新读取校准后的值,确保后续换算用真实 RTC 数据 */
} else {
DBG_ERROR("sys_clock_init: RTC default write FAIL");
}
@@ -205,6 +215,7 @@ int sys_clock_init(void)
}
#endif
/* 取出各时间字段year 已是完整年份,直接用于下文 Unix 换算) */
year = (uint32_t)sd_time.year;
month = (uint32_t)sd_time.month;
day = (uint32_t)sd_time.day;
@@ -212,13 +223,19 @@ int sys_clock_init(void)
minute = (uint32_t)sd_time.minute;
second = (uint32_t)sd_time.second;
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
year, month, day, hour, minute, second);
/* Step 2: Unix epoch → seconds简化算法适用于 1970~2038 */
/* ============================================================
* Step 2将 RTC 日历时间换算为 Unix epoch 秒数
* ============================================================
* Unix 时间定义:自 1970-01-01 00:00:00 起经过的秒数。
* 算法总天数1970~去年整年 + 今年到上月 + 本月已过天)× 86400
* + 当天时分秒。
* 用 int64_t / uint64_t 累加避免中间溢出;最终结果(受 uint32_t 限制)
* 有效范围约 1970-01-01 ~ 2038-01-19超出将回绕见文档第 12 节)。 */
{
uint64_t total_days = 0;
/* 2.1 累加 1970 至(今年-1的整年天数闰年 366、平年 365
* 闰年规则:能被 4 整除但不能被 100 整除,或能被 400 整除 */
for (i = 1970; i < year; i++) {
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0)) {
total_days += 366uLL;
@@ -227,22 +244,34 @@ int sys_clock_init(void)
}
}
/* 2.2 判断今年是否闰年构造各月天数表2 月特判) */
int is_leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (is_leap) mdays[1] = 29;
if (is_leap) mdays[1] = 29; /* 闰年 2 月 29 天 */
/* 2.3 累加今年 1 月至(本月-1月的天数 */
for (i = 0; i < month - 1; i++) {
total_days += mdays[i];
}
/* 2.4 加上本月已过天数day-1从今天 0 点起算) */
total_days += day - 1uL;
/* 2.5 总天数×86400一天秒数+ 当日时分秒 = 绝对 Unix 秒数 */
unix_secs = (int64_t)(total_days * 86400LL) + hour * 3600LL + minute * 60LL + second;
}
/* Step 3: 设置系统墙钟基准 */
/* ============================================================
* Step 3建立"墙钟基准"(基准法核心)
* ============================================================
* 记录当前 HAL Tick 与对应的 Unix 秒数。此后 sys_clock_get() 仅用
* now_unix = s_base_unix_secs + (HAL_GetTick() - s_base_tick)/1000
* 推算当前时间,运行期不再访问 I2C RTC零阻塞、无累积误差。 */
s_base_tick = HAL_GetTick();
s_base_unix_secs = (uint32_t)unix_secs;
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
year, month, day, hour, minute, second);
DBG_INFO("sys_clock_init: wall clock base = unix %lu, base_tick=%lu",
(unsigned long)s_base_unix_secs, (unsigned long)s_base_tick);
@@ -293,7 +322,7 @@ char *sys_clock_get_str(char *buf, size_t buf_size)
unix_to_sd2506(unix_secs, &sd);
snprintf(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d",
sd.year + 2000uL, sd.month, sd.day,
sd.year, sd.month, sd.day,
sd.hour, sd.minute, sd.second);
return buf;
}
@@ -318,8 +347,11 @@ int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
/* Step 2: 写回 SD2506 RTC若提供了 sd_time */
if (sd_time != NULL) {
sd2506_set_time(sd_time);
DBG_INFO("sys_clock_set: SD2506 RTC written");
if (sd2506_set_time(sd_time) == SD2506_OK) {
DBG_INFO("sys_clock_set: SD2506 RTC written");
} else {
DBG_ERROR("sys_clock_set: SD2506 RTC write FAIL");
}
}
return 0;