You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 20:06:02