I²C 主机读到的 U16 偶尔跳变?先修复多字节快照发布

I²C 主机读到的 U16 偶尔跳变?先修复多字节快照发布

一句话: 多字节监测值被后台任务逐字节更新时,I²C 中断可能夹在中间读取,得到"新高字节 + 旧低字节";先组装完整快照,再用短临界区整体发布。
适合谁读:设备内部持续刷新温度、电压、功率或计数值,同时允许主机通过 I²C 读取的嵌入式开发者。

先检查读写是否共享同一份数组

排查动态数据时,把写入和读取的时间线画出来:

text 复制代码
后台任务:写高字节 → 写低字节
I²C 中断:       读高字节 → 读低字节

只要两条线可以交错,就存在撕裂窗口。读出的数值未必是随机数,它可能是一个格式完全合法、但从未真正采样过的组合,所以更难发现。

volatile 解决不了整块一致性

volatile 主要告诉编译器:每次访问都要按内存中的最新值处理。它不能让两个独立字节的更新变成一个不可分割的事务,也不能阻止中断在两个写操作之间插入。

下面的写法即使变量声明为 volatile,仍可能被读到混合值:

c 复制代码
volatile uint8_t monitor_bytes[2];

void update_value(uint16_t value)
{
    monitor_bytes[0] = (uint8_t)(value >> 8);
    monitor_bytes[1] = (uint8_t)value;
}

快照发布的三步

更稳妥的方式是把"准备数据"和"对外可见"分成两步:

c 复制代码
static uint8_t work_snapshot[MONITOR_SIZE];
static uint8_t published_snapshot[MONITOR_SIZE];

void update_monitor(const source_t *source)
{
    build_snapshot(work_snapshot, source);

    disable_irq();
    memcpy(published_snapshot, work_snapshot, MONITOR_SIZE);
    enable_irq();
}

关键点不是一定要 memcpy,而是:

  1. 在临时区域组装完整数据;
  2. 让临界区只覆盖最终提交;
  3. 读取端只访问已发布区域。

临界区越短越好。若数据块很大,可以使用指针交换或版本号方案,但必须确保读取端不会拿到正在被重用的缓冲区。

什么时候需要整块发布

数据类型 推荐策略
单字节状态位 单次写入通常足够,但仍要明确清除语义
U16/U32 数值 快照、双读校验或临界区整体发布
多字段相关数据 整组发布,避免字段之间来自不同采样周期
可独立变化的字段 可以分开发布,但要在协议中说明一致性边界

不要只看 CPU 是否"能一次写完一个字节"。协议读者关心的是一组字段是否来自同一个采样时刻。

验证顺序

text 复制代码
静态检查:所有多字节字段都有一致性边界
构建检查:临界区和缓冲区大小正确
压力测试:后台高频更新时连续读取,不出现混合值
总线实测:抓取主机读取时序,确认长时间运行稳定

如果只有源码路径和静态门禁证据,应把结论写成"已消除代码层撕裂窗口,实际总线压力测试待完成"。

总结

  • 合法格式不代表数据来自同一次采样。
  • volatile 不是多字节原子性工具。
  • 先组装快照,再在短临界区整体发布。
  • 数据一致性边界应与协议字段的业务关联保持一致。

有用的话点个收藏,下次监测值偶尔跳变时,先查它是不是由两个不同采样周期拼出来的。

相关推荐
AAA代码批发商6 小时前
Days 46 i.MX6ULL 裸机开发|从内核切入外设,汇编点亮 LED(补充)
arm开发·嵌入式·裸机开发
嵌入式阿蔡10 小时前
嵌入式安全认证2026:PSA Certified从Arm主导到全球标准
嵌入式
阿源-18 小时前
PCI 总线协议解析
嵌入式·x86·固件
2301_8150910121 小时前
ARM汇编入门笔记(二):启动代码、指令详解与C语言混合编程
嵌入式·arm汇编
DaTou大头1 天前
STM32第三篇
单片机·嵌入式·电路基础
纯音乐sjc1 天前
SPI 通信协议详解:从四线制到 ADXL345
面试·嵌入式
纯音乐sjc1 天前
嵌入式面试复习准备
面试·嵌入式
潘潘的嵌入式日记1 天前
位操作怎么写才不出错?置位、清位、翻转、取位一次讲清
c语言·嵌入式·寄存器·位操作
金士顿1 天前
ASP.NET Core BackgroundService 深度解析:设备后台任务到底应该怎么设计
linux·嵌入式·asp.net core·arm64