I²C 从机接收总被覆盖?双缓冲要在 STOP 时交接


tags: 嵌入式,I2C,ISR,双缓冲,并发

category: 嵌入式开发
type: original
series: peripheral-practice
hook: I2C 从机收到下一帧时,上一帧为什么突然变了
data: 单缓冲: 生产者覆盖消费者 | 双缓冲: STOP 时一次性交接完整事务

I²C 从机接收总被覆盖?双缓冲要在 STOP 时交接

一句话: 中断负责收字节,主循环负责处理命令时,单缓冲无法同时保护"正在接收"和"等待消费"的两份数据;应在事务结束时一次性交接缓冲区和元数据。
适合谁读:I²C 从机、串口或其他中断接收路径出现偶发丢帧、数据错配的嵌入式开发者。

先画出两个所有者

先不要急着加 volatile。把接收区的写入者和读取者列出来:

对象 生产者 消费者 允许什么时候修改
当前接收缓冲区 ISR 当前事务进行中
已发布缓冲区 主循环 主循环处理期间只读
地址、长度、完成标志 ISR 发布 主循环读取 事务结束时一次更新

如果 ISR 和主循环都能改同一块数组,主循环读到的可能是半帧。即使每个字节的读写是原子的,整帧仍然不是原子的。

双缓冲的最小结构

c 复制代码
typedef struct {
    uint8_t data[RX_CAPACITY];
    uint16_t length;
    uint16_t address;
} rx_frame_t;

static rx_frame_t rx_buffer[2];
static uint8_t rx_write_index;
static uint8_t rx_ready_index;
static bool rx_ready;
static bool rx_overflow;

ISR 只写当前缓冲区。收到 STOP 后,先补齐长度和地址,再一次性发布索引;主循环只消费已经发布的那一块。

text 复制代码
ISR 写 A → STOP → 发布 A → 主循环读 A
ISR 改写 B → STOP → 发布 B → 主循环读 B

这样,主循环处理 A 时,ISR 可以准备 B,不会把 A 的内容改到一半。

STOP 和 REPEATED START 不是一回事

许多 I²C 主机先写寄存器地址,再用 REPEATED START 读取数据,中间没有 STOP。此时不能把"上一帧已经发布的地址"当成当前读取地址。

推荐为当前事务保留独立地址快照:

c 复制代码
void on_address_phase(uint16_t address)
{
    current_transaction_address = address;
}

void on_repeated_start_read(void)
{
    tx_address = current_transaction_address;
    start_read_response(tx_address);
}

STOP 适合提交"这一帧已经完整接收"的事实;REPEATED START 则表示总线方向变了,不代表前一帧的业务数据可以被重新解释。

缓冲区满了怎么办

双缓冲并不能让消费者无限变快。上一帧还没处理完,新帧又结束时,必须有明确策略:

策略 优点 风险
丢弃新帧 保留已发布的完整帧 需要记录溢出,主机要能重试
覆盖旧帧 不阻塞接收 主循环可能处理到被破坏的数据
增加队列 吞吐更高 需要容量、回收和背压策略

对配置命令,通常宁可丢弃并报告溢出,也不要静默覆盖一帧已经交给主循环的数据。

回归测试至少覆盖三条路径

text 复制代码
连续两帧快速写入 → 第一帧内容不被第二帧改写
写地址 + REPEATED START + READ → 读取当前事务地址
主循环故意阻塞 → 溢出有记录,已发布帧仍完整

如果只有静态检查和编译证据,应写明"所有权交接路径已检查,真实主机互操作仍待验证"。不要把没有波形或抓包日志的场景写成已经复现的现场故障。

总结

  • 单缓冲的问题不是少一个数组,而是生产者和消费者没有所有权边界。
  • STOP 时发布完整帧,主循环只消费已发布缓冲区。
  • REPEATED START 要使用当前事务地址快照。
  • 缓冲区满时必须选择丢弃、覆盖或排队,不能让策略隐含在指针操作里。

有用的话点个收藏,下次 I²C 偶发错帧时,先问"这块缓冲区现在到底归谁"。

相关推荐
Lhan.zzZ1 小时前
QML 组件库:VS2022 + Qt 静态库方案
开发语言·c++·qt·visual studio
敲代码的嘎仔1 小时前
28届后端开发-海康威视日常实习一面(已OC)
java·开发语言·后端·面试·海康威视·实习·大厂
AOI小白新手上路2 小时前
江科协51单片机16-1 AD/DA ADC 三通道综合显示(LCD1602 版)改造说明
单片机·嵌入式硬件·51单片机
xx~t2 小时前
嵌入式——Linux软件编程——网络1
linux·c语言·网络·vscode·网络协议
sakiko_2 小时前
OC基础语法(与Swift对比)-1
开发语言·ios·objective-c·swift
HUI-4742 小时前
EG1135|SSOP‑16 同步整流降压控制器,大功率外置 MOS 降压优选
单片机·嵌入式硬件·硬件工程
迷迭香yy3 小时前
基金档案数据工程实战从收入分析到持仓穿透的Python解析 IG50免费开源股票数据API接口
开发语言·python
晊晌_h3 小时前
嵌入式从0到精通——线程
java·开发语言·jvm
单片机仿真设计3 小时前
【proteus仿真】基于 STM32 单片机恒温箱控制系统设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·毕设