You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 09:54:06