就绪屏障与配置引擎重构,新增串口助手、V0.0网表分析报告及芯片手册,同步更新文档
This commit is contained in:
+97
-14
@@ -8,12 +8,18 @@
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-09-13 王建锋 新增 sys_clock_get_time()(日历时间获取器,供 FatFs 时间钩子等使用)
|
||||
* v1.2 2026-09-13 王建锋 文件移入 App/util/(改归 ④中间件层:通用平台时间服务,多服务共用)
|
||||
* v1.3 2026-09-15 王建锋 修复 49.7 天墙钟回绕:sys_clock_get 每 30 天惰性再基准
|
||||
* (自我参照、无损、零 I2C);修正"未初始化"哨兵判断;
|
||||
* sys_clock_init 检查 RTC 读返回值/写回复验,失败时降级为
|
||||
* 编译期默认时间基准并返回 -1(激活 app_main 时钟芯片自检)
|
||||
*/
|
||||
|
||||
#include "sys_clock.h"
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "FreeRTOS.h" /* taskENTER_CRITICAL:惰性再基准化的并发保护 */
|
||||
#include "task.h"
|
||||
|
||||
#define DBG_TAG "[CLK]"
|
||||
#include "dbg_log.h"
|
||||
@@ -42,6 +48,13 @@
|
||||
static volatile uint32_t s_base_tick = 0; /* HAL_GetTick() 基准 */
|
||||
static volatile uint32_t s_base_unix_secs = UINT32_MAX;
|
||||
|
||||
/*
|
||||
* 再基准化周期(惰性再基准化,见 sys_clock_get 说明):
|
||||
* 32 位 tick 差值窗口必须恒 < 一整圈(2^32 ms ≈ 49.7 天),否则差值混叠、
|
||||
* 墙钟"倒退"一圈。30 天 = 2,592,000,000 ms < 2^32,为惰性触发延迟留 19.7 天余量。
|
||||
*/
|
||||
#define SYS_CLOCK_REBASE_MS (30u * 24u * 3600u * 1000u)
|
||||
|
||||
/* ============================================================
|
||||
* 内部辅助函数:Unix time ↔ sd2506_time_t 转换
|
||||
* ============================================================ */
|
||||
@@ -155,12 +168,17 @@ static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time) {
|
||||
/*
|
||||
* 函数功能:初始化系统墙钟时间基准
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功(始终返回 0)
|
||||
* 返回值:0 - 成功;-1 - RTC 不可用(启用编译期默认值时降级为编译期默认时间
|
||||
* 基准:墙钟仍可用,返回值报告芯片故障;否则不建基准,墙钟保持"未初始化")
|
||||
* 限定条件:CubeMX 外设初始化完成、SD2506 RTC 驱动已初始化
|
||||
* 函数说明:1. 从 SD2506 读取初始时间,必要时按编译期默认宏校时(RTC_DEFAULT_INIT_*)
|
||||
* 2. 将 RTC 时间换算为 Unix epoch 秒数
|
||||
* 函数说明:1. 从 SD2506 读取初始时间(检查返回值:I2C 读失败 → 判 RTC 异常),
|
||||
* 必要时按编译期默认宏校时(RTC_DEFAULT_INIT_*),写回后复验范围
|
||||
* 2. 将 RTC 时间换算为 Unix epoch 秒数(仅合法值进入换算,防垃圾月份在
|
||||
* i < month - 1 处下溢空转约 40 亿次)
|
||||
* 3. 记录当前 HAL Tick 与 Unix 秒数为基准,供 sys_clock_get() 后续推算
|
||||
* 4. 注意:sd2506_get_time() 返回的 year 已是完整年份(驱动内 +2000),
|
||||
* 4. RTC 不可用时:启用编译期默认值则以默认时间建基准(仅 RAM、不依赖芯片)
|
||||
* 并返回 -1;否则不建基准,返回 -1
|
||||
* 5. 注意:sd2506_get_time() 返回的 year 已是完整年份(驱动内 +2000),
|
||||
* 换算时不得再加 2000,否则年份虚增导致 Unix 秒数超 uint32 回绕
|
||||
*/
|
||||
int sys_clock_init(void) {
|
||||
@@ -168,14 +186,17 @@ int sys_clock_init(void) {
|
||||
uint32_t year, month, day, hour, minute, second;
|
||||
int64_t unix_secs = 0;
|
||||
int i;
|
||||
int rtc_ok; /* RTC 读取可用(1)/不可用(0) */
|
||||
int degraded = 0; /* 1 = 以编译期默认时间建基准(RTC 故障,返回值报 -1) */
|
||||
|
||||
/* ============================================================
|
||||
* Step 1:从 SD2506 硬件 RTC 读取初始时间
|
||||
* ============================================================
|
||||
* 注意:sd2506_get_time() 返回的 year 已是完整年份(如 2026,
|
||||
* 驱动内部已 +2000)。此处【禁止】再 +2000,否则年份虚增到 4026,
|
||||
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。 */
|
||||
sd2506_get_time(&sd_time);
|
||||
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。
|
||||
* 返回值必须检查:I2C 失败时字段为垃圾,不得用于建立基准。 */
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == SD2506_OK) ? 1 : 0;
|
||||
|
||||
/* ============================================================
|
||||
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
|
||||
@@ -185,7 +206,8 @@ int sys_clock_init(void) {
|
||||
* 判定"时间无效":年份越界、月/日越界(仅粗判,不细校 2 月 29 日等)。 */
|
||||
#if defined(RTC_DEFAULT_INIT_ENABLE)
|
||||
{
|
||||
int rtc_invalid = (sd_time.year < 2000 || sd_time.year > 2099 ||
|
||||
int rtc_invalid = (rtc_ok == 0) ||
|
||||
(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)
|
||||
@@ -207,7 +229,7 @@ int sys_clock_init(void) {
|
||||
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); /* 重新读取校准后的值,确保后续换算用真实 RTC 数据 */
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == SD2506_OK) ? 1 : 0; /* 重新读取校准后的值(同样检查返回值) */
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_init: RTC default write FAIL");
|
||||
}
|
||||
@@ -215,6 +237,39 @@ int sys_clock_init(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 写回后复验范围:能写入但读回仍越界的芯片同样判故障;
|
||||
* 同时保证进入 Step 2 换算循环的必为合法值(见函数说明第 2 条)。
|
||||
*/
|
||||
if (rtc_ok != 0 &&
|
||||
(sd_time.year < 2000 || sd_time.year > 2099 ||
|
||||
sd_time.month < 1 || sd_time.month > 12 ||
|
||||
sd_time.day < 1 || sd_time.day > 31)) {
|
||||
rtc_ok = 0;
|
||||
}
|
||||
|
||||
if (rtc_ok == 0) {
|
||||
#if (RTC_DEFAULT_INIT_ENABLE == 1)
|
||||
/*
|
||||
* 降级基准:以编译期默认时间建立基准(仅 RAM、不依赖芯片)。
|
||||
* 墙钟保持可用(日志时间戳为默认时间);返回值报告芯片故障,
|
||||
* 触发 app_main 的"时钟芯片异常"自检。
|
||||
*/
|
||||
DBG_ERROR("sys_clock_init: RTC abnormal, fallback to default time");
|
||||
sd_time.year = RTC_DEFAULT_YEAR;
|
||||
sd_time.month = RTC_DEFAULT_MONTH;
|
||||
sd_time.day = RTC_DEFAULT_DAY;
|
||||
sd_time.hour = RTC_DEFAULT_HOUR;
|
||||
sd_time.minute = RTC_DEFAULT_MINUTE;
|
||||
sd_time.second = RTC_DEFAULT_SECOND;
|
||||
sd_time.week = rtc_calc_week(sd_time.year, sd_time.month, sd_time.day);
|
||||
degraded = 1;
|
||||
#else
|
||||
DBG_ERROR("sys_clock_init: RTC read FAIL, wall clock uninitialized");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 取出各时间字段(year 已是完整年份,直接用于下文 Unix 换算) */
|
||||
year = (uint32_t)sd_time.year;
|
||||
month = (uint32_t)sd_time.month;
|
||||
@@ -275,24 +330,52 @@ int sys_clock_init(void) {
|
||||
DBG_INFO("sys_clock_init: wall clock base = unix %lu, base_tick=%lu",
|
||||
(unsigned long)s_base_unix_secs, (unsigned long)s_base_tick);
|
||||
|
||||
return 0;
|
||||
return (degraded != 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取当前系统墙钟时间(Unix 时间戳)
|
||||
* 入口参数:无
|
||||
* 返回值:当前 Unix 秒数 uint32_t 未初始化时返回 UINT32_MAX
|
||||
* 限定条件:sys_clock_init() 已成功调用
|
||||
* 限定条件:sys_clock_init() 已调用
|
||||
* 函数说明:1. 以基准 Tick 与基准 Unix 秒数为起点,按当前 HAL Tick 与 1000ms
|
||||
* 的整除差推算流逝秒数(避免每毫秒累加,减少漂移与溢出风险)
|
||||
* 2. 若推算结果为负(基准未建立),返回 UINT32_MAX 表示无效
|
||||
* 的整除差推算流逝秒数(避免每毫秒累加,减少漂移与溢出风险);
|
||||
* 32 位无符号模减对 Tick 回绕天然无缝
|
||||
* 2. 差值窗口达到 SYS_CLOCK_REBASE_MS(30 天)时触发"惰性再基准化":
|
||||
* 基准前移流逝秒数(墙钟值不变)、差值窗口清零,亚秒余数结转入
|
||||
* 新窗(base_tick = now − 余数)故无损,保证差值永不因一整圈
|
||||
* 2^32 ms(≈49.7 天)而混叠
|
||||
* 3. 再基准化为自我参照:纯内存操作、零 I2C、非阻塞——不重读 RTC
|
||||
* 重锚(RTC 精度远逊 CPU Tick,重锚反而吸收其漂移);临界区 +
|
||||
* 区内复核防多任务并发重复累加
|
||||
* 4. 若基准未建立(init 失败),返回 UINT32_MAX 表示无效
|
||||
*/
|
||||
uint32_t sys_clock_get(void) {
|
||||
uint32_t now_tick = HAL_GetTick();
|
||||
int64_t unix_secs = (int64_t)s_base_unix_secs + (int64_t)((uint32_t)(now_tick - s_base_tick) / 1000uL);
|
||||
uint32_t diff = now_tick - s_base_tick; /* 32 位模减,跨 Tick 回绕无缝 */
|
||||
int64_t unix_secs;
|
||||
|
||||
if (s_base_unix_secs != UINT32_MAX && diff > SYS_CLOCK_REBASE_MS) {
|
||||
uint32_t secs = diff / 1000u;
|
||||
uint32_t rem = diff - secs * 1000u; /* 亚秒余数,结转入新窗 → 无损 */
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
diff = now_tick - s_base_tick; /* 临界区内复核:另一任务可能已先再基准化 */
|
||||
if (diff > SYS_CLOCK_REBASE_MS) {
|
||||
s_base_unix_secs += secs; /* 基准前移:墙钟值本身不变 */
|
||||
s_base_tick = now_tick - rem; /* 新窗自 (now − 余数) 起算,余数续计 */
|
||||
diff = rem;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
if (s_base_unix_secs == UINT32_MAX) {
|
||||
return UINT32_MAX; /* 未初始化(显式哨兵判断) */
|
||||
}
|
||||
|
||||
unix_secs = (int64_t)s_base_unix_secs + (int64_t)(diff / 1000u);
|
||||
if (unix_secs < 0) {
|
||||
return UINT32_MAX; /* 未初始化 */
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
return (uint32_t)unix_secs;
|
||||
|
||||
Reference in New Issue
Block a user