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 不是多字节原子性工具。
  • 先组装快照,再在短临界区整体发布。
  • 数据一致性边界应与协议字段的业务关联保持一致。

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

相关推荐
蓝天居士12 小时前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(3)
mcu·嵌入式
jianqiang.xue16 小时前
【CStackGUI 实战】画板 drawpad:鼠标拖拽作图、撤销 / 重做、导出 PNG
单片机·嵌入式·cstackgui·c语言gui·可视化拖拽
ly76891 天前
Redis Cluster 集群运维实战:槽位迁移、MOVED/ASK 重定向与扩容缩容的数据一致性
数据一致性·redis cluster·槽位迁移·moved与ask·集群扩容缩容
嵌入式分享2 天前
BSP调试#01:RTC(RK3588)
嵌入式·实时音视频
嵌入式分享2 天前
嵌入式分享#38:踩坑实录!我在 RK3576 HDMI 搭错线
嵌入式
要开心吖ZSH2 天前
系统并发与 QPS 上限:从 200 个线程到每秒几万请求
redis·mysql·tomcat·并发·连接池·qps
oku620892 天前
单片机底层系列:C 库运行时——从 libspace 到多任务与中断安全
嵌入式
Zwawa3 天前
反馈:让系统知道自己做得对不对
c语言·单片机·嵌入式
蓝天居士3 天前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(2)
mcu·嵌入式
橘好き3 天前
将7针的SPI OLED做I2C使用
oled·spi·i2c