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

树莓派5通过smbus2向Arduino Mega发送双字节I2C数据故障排查

问题根源与解决办法

1. I2C数据传输格式不匹配

树莓派端每次仅发送2字节(单个参数),且分多次发送,但Arduino的receiveEvent期望一次性读取3个电机的所有参数(共18字节),导致Arduino无法正确触发回调或解析数据。

修改树莓派代码:一次性发送完整数据块

将所有参数打包成连续字节流,一次性发送,避免多次小数据传输的时序问题:

from smbus2 import SMBus
from time import sleep
import struct

addr = 0x8  # I2C从机地址
bus = SMBus(1)  # /dev/i2c-1

steps = [600, 600, 600]
speeds = [500, 500, 500]
accels = [200, 200, 200]

while True:
    # 打包所有参数为大端双字节格式的连续字节流
    data_buffer = b""
    for idx in range(3):
        data_buffer += struct.pack(">h", steps[idx])
        data_buffer += struct.pack(">h", speeds[idx])
        data_buffer += struct.pack(">h", accels[idx])
    # 一次性发送所有数据
    bus.write_i2c_block_data(addr, 0, list(data_buffer))
    sleep(2)

2. Arduino回调注册时机与逻辑错误

  • Wire.onReceive(receiveEvent)注册在home()之后,虽不影响初始化,但回调逻辑存在多个问题:
    • digitalWrite(ledPin, '1')错误,应使用整数HIGH或1
    • 假设一次性收到18字节,但实际每次仅收到2字节,导致parse_Integer返回0
    • 用int解析long类型参数,存在溢出风险

修改Arduino关键代码:

void setup() {
  // 先注册I2C回调,确保通信就绪
  Wire.begin(0x8);
  Wire.onReceive(receiveEvent);
  
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  // 电机初始化
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setMaxSpeed(100000);
    steppers[i].setAcceleration(800);
    steppers[i].disableOutputs();
    steppers[i].setCurrentPosition(0);
  }
  home();
}

void receiveEvent(int howMany) {
  digitalWrite(ledPin, HIGH);
  // 校验数据长度是否符合预期
  if (howMany != 18) {
    while(Wire.available()) Wire.read(); // 清空无效数据
    digitalWrite(ledPin, LOW);
    return;
  }

  for (int i = 0; i < MAX_STEPPERS; i++) {
    // 读取双字节大端数据并转换为long类型
    byte high_byte = Wire.read();
    byte low_byte = Wire.read();
    receivedSteps[i] = ((long)high_byte << 8) | low_byte;

    high_byte = Wire.read();
    low_byte = Wire.read();
    receivedSpeed[i] = ((long)high_byte << 8) | low_byte;

    high_byte = Wire.read();
    low_byte = Wire.read();
    receivedAcceleration[i] = ((long)high_byte << 8) | low_byte;
  }

  // 更新电机参数
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setAcceleration(receivedAcceleration[i]);
    steppers[i].setMaxSpeed(receivedSpeed[i]);
    steppers[i].move(receivedSteps[i]);
  }
  runAllowed = true;

  delay(100); // 留时间观察LED状态
  digitalWrite(ledPin, LOW);
}

// 修正runMotors的循环判断逻辑,使用AccelStepper自带的distanceToGo()
void runMotors() {
  if (runAllowed) {
    bool all_motors_done = false;
    while (!all_motors_done) {
      all_motors_done = true;
      for (int i = 0; i < MAX_STEPPERS; i++) {
        steppers[i].enableOutputs();
        steppers[i].run();
        if (steppers[i].distanceToGo() != 0) {
          all_motors_done = false;
        }
      }
    }
    runAllowed = false;
  }
  if (!runAllowed) {
    for (int i = 0; i < MAX_STEPPERS; i++) {
      steppers[i].disableOutputs();
    }
  }
}

3. 硬件与权限检查

  • 确认树莓派I2C已启用:通过raspi-config的Interfacing Options开启I2C
  • 确认I2C连线正确:SDA接SDA、SCL接SCL、GND共地
  • 树莓派运行代码需权限:要么用sudo执行,要么将用户加入i2c组:sudo usermod -aG i2c pi,重启后生效

内容的提问来源于stack exchange,提问作者FlamingIce

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:57:01