因为嵌入式环境受限,需绕过libc直接对接硬件时钟源;get_ms_tick()是唯一必须实现的原子、O(1)、无锁毫秒单调计数器,其他时间函数均由此派生。

为什么不用 time.h 而要自己实现?
因为嵌入式环境、裸机或静态链接限制下,time.h 依赖 libc 的完整实现(比如 gettimeofday、clock_gettime 系统调用),而你可能只有个滴答定时器(systick)或 RTC 寄存器可读。这时需要绕过 libc,直接对接硬件时钟源——不是“重写标准”,而是“最小可信时间基元”。
get_ms_tick() 是唯一必须实现的底层函数
所有时间计算都从它派生:毫秒级单调递增计数器,不回跳、不依赖系统时钟同步。其他函数(如模拟 clock() 或 time())只是它的线性变换或封装。
- 它必须是原子读取(尤其在中断/多核场景),推荐用
volatile uint32_t+ 内存屏障,或硬件寄存器映射 - 溢出处理交给上层:若用 32 位计数器(常见于 systick),每 ~49.7 天溢出一次,
get_ms_tick()返回原值,上层用差值计算((now - start) & 0xFFFFFFFFU)即可防误判 - 不要在该函数里做浮点或除法——它会被高频调用(如超时轮询),保持 O(1) 和无锁
如何安全导出类似 time_t 和 struct tm 的语义?
只在需要日历时间时才转换,且明确区分“相对时间”和“绝对时间”。例如:
// 假设已知 epoch 是 2024-01-01 00:00:00 UTC(可配置)
constexpr uint32_t EPOCH_MS = 1704067200000U;
<p>uint32_t time_ms() {
return get_ms_tick() + EPOCH_MS; // 单调 + 偏移
}</p><p>time_t time(time_t<em> t) {
auto tms = time_ms();
time_t sec = tms / 1000;
if (t) </em>t = sec;
return sec;
}</p>注意:struct tm 解析(localtime_r 级别)涉及闰年、月份天数、时区偏移——若不需要显示给人看,就别实现;若需要,用查表法(12 个月长度数组 + 累加)比除法更轻量,且避免 gmtime 的 libc 依赖。
立即学习“C++免费学习笔记(深入)”;
避免把“轻量”做成“残缺”的关键边界
真正轻量的核心是:不引入动态内存、不依赖文件系统、不假设 POSIX 环境。但容易被忽略的是时区和夏令时——它们会让代码体积指数增长。所以明确约定:
- 所有时间戳统一为 UTC 毫秒整数,不存时区字段
- 格式化输出(如串口打印)由业务层决定是否加
+0800偏移,库本身不提供strftime - 不实现
sleep()或usleep():那是调度器职责,库只提供wait_ms(uint32_t ms)这种忙等封装(仅用于调试或单任务环境)
最常被低估的复杂点是:跨平台滴答源注册方式不同(ARM Cortex-M 的 systick、RISC-V 的 mtime、ESP32 的 esp_timer_get_time()),get_ms_tick() 必须作为弱符号或宏可覆盖,不能硬编码。


















