Arduino I2C通信onReceive回调内调用beginTransmission发送失败问题
故障根本原因
Arduino的Wire.onReceive()注册的回调函数运行在I2C中断服务上下文里,I2C协议的发送操作(beginTransmission/endTransmission)本身也依赖I2C中断完成,在中断回调中直接发起新的I2C通信会造成中断嵌套冲突,导致通信卡死,因此数据无法发送成功。
除此之外,中断上下文要求执行时间尽可能短,LCD打印这类耗时操作也不建议放在回调中执行。
解决方案
核心逻辑是中断回调仅做数据接收和状态标记,所有业务处理、外设操作都放到主循环loop()中执行,修改后的代码如下:
//Wire Master #include<Wire.h> #include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // 定义全局共享变量,用volatile修饰保证中断和主循环读写一致性 volatile char receivedCmd = 0; volatile bool hasNewData = false; void setup() { lcd.begin(16, 2); lcd.print("Ref Door Status"); Wire.begin(1); Serial.begin(9600); Wire.onReceive(receivedEvent); } void loop() { // 主循环检测是否有新收到的数据 if(hasNewData){ // 先清除标记,避免重复处理 hasNewData = false; if(receivedCmd == 'O'){ lcd.setCursor(0, 1); lcd.print("OPENED"); // 现在在主循环中发起I2C发送,不会有中断冲突 Wire.beginTransmission(8); Wire.write('O'); Wire.endTransmission(); } else if (receivedCmd == 'C'){ lcd.setCursor(0, 1); lcd.print("CLOSED"); } } } void receivedEvent(int howMany){ // 中断回调只做最基础的读数据和标记操作,快速退出 while(Wire.available()){ receivedCmd = Wire.read(); hasNewData = true; } }
额外验证点
- 确认从设备2的I2C地址确实为8,无地址冲突
- I2C总线的SDA、SCL引脚需要各接一个4.7KΩ的上拉电阻到和Arduino供电匹配的3.3V/5V,总线过长或者设备较多时上拉电阻是必须的
- 确认从设备2已经正常烧录了对应I2C接收的代码,能响应地址8的通信请求
内容的提问来源于stack exchange,提问作者Elnathan John Salavarria
相关产品推荐
相关产品推荐

