4字节long型ARM MCU下获取毫秒级时间的可行方案咨询
问题根源
现有实现的核心问题是4字节有符号整数的取值范围不足以存储纪元时间对应的毫秒值:
- 4字节有符号
long最大值为2^31 - 1 = 2147483647,对应毫秒数仅能覆盖约24.8天的时间跨度,无论使用上电相对时间戳还是1970纪元时间戳,都会快速溢出。 - 若使用1970纪元秒,4字节有符号
time_t仅能支撑到2038年,转换为毫秒后取值会远超出4字节有符号数范围,直接计算就会溢出。
可行替代方案
方案1:使用64位整数存储毫秒时间戳
ARM架构MCU常用的编译器(ARM GCC、Keil MDK AC5/AC6)均原生支持64位整数类型int64_t,引入stdint.h头文件即可使用,修改后实现如下:
#include <stdint.h> #include <sys/time.h> static inline int64_t EpochTimeInMs(void) { struct timeval tv; gettimeofday(&tv, NULL); // 先将tv_sec强转为64位再计算,避免计算过程中途溢出 int64_t ms = (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; return ms; }
该方案兼容标准纪元时间格式,64位有符号整数存储毫秒值可覆盖近3亿年的时间跨度,完全满足嵌入式场景需求。
方案2:拆分时间戳输出(必须使用4字节变量时适用)
如果业务逻辑中无法直接传递64位变量,比如要通过仅支持4字节参数的接口传输时间,可以将秒和毫秒部分拆分返回:
#include <stdint.h> #include <sys/time.h> // 输出参数p_ms返回0~999范围的毫秒值,函数返回值为纪元秒 static inline time_t EpochTimeSplitMs(uint16_t *p_ms) { struct timeval tv; gettimeofday(&tv, NULL); *p_ms = tv.tv_usec / 1000; return tv.tv_sec; }
使用时分别处理秒和毫秒部分即可,不需要修改现有time_t的类型定义。
方案3:使用相对时间戳(不需要全局纪元时间时适用)
如果业务仅需要测量时间间隔、不需要和全球标准时间同步,可以直接用MCU的定时器做上电相对毫秒时间戳,性能远高于调用gettimeofday:
#include <stdint.h> // 假设已配置1ms触发一次的SysTick中断,中断内对s_tick执行加1操作 static volatile uint32_t s_tick; // 32位无符号相对毫秒可覆盖49.7天跨度,满足大部分嵌入式场景需求 uint32_t GetRelativeMs(void) { return s_tick; } // 需要更长时间跨度时,可通过溢出计数扩展为64位 static volatile uint32_t s_tick_high; void SysTick_Handler(void) { s_tick++; // 计数器溢出时高位加1 if (s_tick == 0) { s_tick_high++; } } uint64_t GetRelativeMs64(void) { uint32_t high, low; // 读取时临时关中断,避免两次读取的高低位不匹配 __disable_irq(); high = s_tick_high; low = s_tick; __enable_irq(); return ((uint64_t)high << 32) | low; }
内容的提问来源于stack exchange,提问作者xyf
相关产品推荐
相关产品推荐

