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

Arduino Nano 33 IoT与Jetson Nano I2C双向通信故障排查

Arduino Nano 33 IoT与Jetson Nano I2C双向整数数组传输方案

故障根因

  • SMBus协议不匹配:Jetson端smbus库的块传输接口遵循SMBus规范,块写操作的帧结构为「从机地址 + 1字节命令/寄存器地址 + 1字节数据长度 + N字节有效数据」,原代码把第一个有效整数当命令码传入,导致Arduino端收到的字节流多了长度字段,出现数据移位。块读操作默认要求从机返回的第一个字节是数据长度,原生Arduino Wire库不支持这个格式,会导致主机读溢出、抓取总线垃圾值,也就是出现长串乱码。
  • 中断上下文逻辑错误:Arduino端onRequest/onReceive是I2C硬件中断触发的回调,执行时序要求极严,原代码在回调里加了Serial打印这类毫秒级耗时操作,会直接打乱I2C响应时序,导致传输数据损坏。
  • 数据类型约定不一致:Arduino端int默认是2字节长度,原代码直接按字节流传输整数数组没有做统一拆分,跨平台解析时会出现长度不匹配、字节序错位问题。
  • 接收索引逻辑缺陷:Arduino端接收数据的索引只在读满4字节后才重置,一旦出现半包、错包就会持续错位。

前置硬件校验

  • 必须保证两个设备GND直连,I2C通信需要共地参考电平,否则会出现随机乱码
  • 正确接线(修正原文档的SCL笔误):
    • Jetson Nano 2GB:Pin27(SDA) 接 Arduino A4(SDA),Pin28(SCL) 接 Arduino A5(SCL),GND接GND
    • 两者都是3.3V逻辑,不需要电平转换,禁止接5V上拉避免烧引脚
  • 如果通信不稳定,可以在SDA、SCL引脚各加一个10K上拉电阻到3.3V,补全总线驱动能力。

修正后可直接运行的代码

Arduino端代码

#include <Wire.h>

// 传输层统一用单字节,避免跨平台数据长度不一致问题
uint8_t rx_buf[4];
uint8_t tx_buf[4] = {0, 23, 41, 19}; // 要发送给Jetson的4个整数(0-255范围)
uint8_t rx_idx = 0;

void setup() {
  Serial.begin(115200);
  Wire.begin(0x6a); // 从机地址设置为0x6a
  Wire.onReceive(receiveHandler);
  Wire.onRequest(requestHandler);
}

void loop() {
  delay(100);
}

// 主机请求数据的中断回调,禁止添加任何耗时操作
void requestHandler() {
  Wire.write(tx_buf, 4); // 直接填充发送缓冲区,不要加串口打印
}

// 主机下发数据的中断回调
void receiveHandler(int byteCount) {
  rx_idx = 0; // 每次新传输事件重置索引,从第一个字节开始存
  while (Wire.available() && rx_idx < 4) {
    rx_buf[rx_idx] = Wire.read();
    rx_idx++;
  }
  // 所有字节接收完成后再打印日志
  Serial.print("Recv from Jetson: ");
  Serial.print(rx_buf[0]); Serial.print("\t");
  Serial.print(rx_buf[1]); Serial.print("\t");
  Serial.print(rx_buf[2]); Serial.print("\t");
  Serial.println(rx_buf[3]);
}

Jetson Nano Python端代码

import smbus
import time

# 2GB版Jetson Nano的SDA27/SCL28对应I2C总线0,4GB版需改为SMBus(1)
bus = smbus.SMBus(0)
SLAVE_ADDR = 0x6A
DATA_LENGTH = 4

def i2c_write(data_list):
    # 第二个参数为命令/寄存器地址,无寄存器场景固定填0即可,后面传4字节有效数据
    bus.write_i2c_block_data(SLAVE_ADDR, 0x00, data_list)

def i2c_read():
    # 直接读取原始字节流,跳过SMBus块读要求的长度前缀,和Arduino发送格式匹配
    return list(bus.read_i2c(SLAVE_ADDR, DATA_LENGTH))

if __name__ == "__main__":
    while True:
        send_data = [14, 42, 95, 0]
        i2c_write(send_data)
        print(f"Sent to Arduino: {send_data}")
        time.sleep(0.1) # 留短延时给从机响应
        recv_data = i2c_read()
        print(f"Recv from Arduino: {recv_data}")
        time.sleep(1)

扩展:传输大于255的整数

如果需要传输0-65535范围的16位整数,统一约定小端字节序(和Arduino内存存储顺序一致),发送前拆分、接收后拼接即可:

  • Arduino端拆分16位整数到发送缓冲区:
    int val = 1234; // 2字节整数
    tx_buf[i*2] = val & 0xFF; // 低字节
    tx_buf[i*2+1] = (val >> 8) & 0xFF; // 高字节
    
  • Python端拼接16位整数:
    val = recv_data[i*2] | (recv_data[i*2+1] << 8)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:33:21