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

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

相关推荐
LCG米14 小时前
STM32硬件SPI驱动SSD1306 OLED实测:上电黑屏排查、页寻址模式与取模扫描方向陷阱
stm32·单片机·嵌入式硬件·嵌入式
探鸽小钛18 小时前
4G 终端 ES8311 - 挂断一次,下次说话就没声了之解决办法
物联网·音视频·嵌入式·esp32·webrtc
数据库小学妹18 小时前
MySQL binlog格式怎么选?ROW、STATEMENT、MIXED的区别与坑
数据库·mysql·binlog·主从复制·数据一致性
我不会起名字3221 天前
Redis 从 0 到 1(十二):先删缓存还是先更新库?4 种方案的 Go 并发实测
redis·go·并发·延迟双删·缓存一致性
Zwawa2 天前
编译器工具链:GCC、Clang、MSVC、ICC、armclang
c语言·嵌入式·编译器
用户8351414049492 天前
Makefile从零写起:每条规则在干什么
嵌入式
pipixia200332 天前
C++继承面试四连问:内存叠加、私有继承、sizeof、权限重映射
c++·面试·嵌入式·继承·面向对象·对象模型
shimly1234562 天前
(undone) 野火嵌入式 (1) 打开例程工程
嵌入式
Zwawa3 天前
一只按键不只“按下”:短按、长按和连按如何共存
c语言·单片机·嵌入式
Zevalin爱灰灰3 天前
Linux入门第一部分(Ubuntu入门篇) 第三章 shell脚本入门(一)
linux·ubuntu·嵌入式