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
相关产品推荐
相关产品推荐

