树莓派WiringPi读取I2C设备AHT21异常问题排查
AHT21传感器I2C通信问题:Arduino正常但WiringPi读取异常
问题背景
我有一个通过I2C通信的AHT21传感器,发送3字节数据后会返回6字节数据。Arduino代码可正常运行,但树莓派上用WiringPi编写的代码无法正常读取,反复返回状态字节。需要找出WiringPi的I2C语法问题,并将Arduino代码转换为可用的树莓派C++程序。
正常运行的Arduino代码
#include <Wire.h> #define AHT21 0x38 void setup() { Wire.begin(); // 初始化SDA和SCL引脚 Serial.begin(9600); uint8_t rawData[7] = {0,0,0,0,0,0,0}; Wire.beginTransmission(AHT21); Wire.write(0xAC); // 发送测量命令,启动测量 Wire.write(0x33); // 发送测量控制位 Wire.write(0x00); // 发送空操作控制位 Wire.endTransmission(); delay(100); Wire.requestFrom(AHT21, 6); for (uint8_t i = 0; i < 6; i++) { rawData[i] = Wire.read(); Serial.print(i);Serial.print(": "); Serial.println(rawData[i]); } } void loop() {}
Arduino代码输出结果
0: 28 1: 106 2: 90 3: 117 4: 126 5: 70
异常的树莓派WiringPi代码
#include <wiringPi.h> #include <wiringPiI2C.h> #include <stdio.h> #include <stdint.h> #include <math.h> #define Address 0x38 int main (int argc, char **argv) { int fd = wiringPiI2CSetup(Address); uint8_t rawData[7] = {0,0,0,0,0,0,0}; wiringPiI2CWrite(fd,0xAC); // 发送测量命令,启动测量 wiringPiI2CWrite(fd,0x33); // 发送测量控制位 wiringPiI2CWrite(fd,0x00); // 发送空操作控制位 delay(100); for (uint8_t i = 0; i < 6; i++) { rawData[i] = wiringPiI2CRead(fd); printf("%d: %d\n",i,rawData[i]); } }
树莓派代码输出结果
./aht21 0: 28 1: 28 2: 28 3: 28 4: 28 5: 28
问题原因分析
- 写入阶段问题:Arduino的
Wire.beginTransmission()+Wire.write()+Wire.endTransmission()是将多个字节打包成单个I2C写事务发送,符合AHT21需要一次性接收完整3字节命令帧的要求。而单独调用wiringPiI2CWrite(fd, byte)会为每个字节发起独立的I2C写事务,导致传感器无法识别完整的测量命令。 - 读取阶段问题:Arduino的
Wire.requestFrom()发起一个I2C读事务,一次性请求6字节数据并存入缓冲区,后续Wire.read()是从缓冲区取数据。而单独调用wiringPiI2CRead(fd)每次都会发起一个独立的读事务,传感器每次只会返回状态字节(0x1C,对应十进制28),因此反复得到相同值。
修正后的树莓派C++代码(WiringPi)
#include <wiringPi.h> #include <wiringPiI2C.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> // 提供read/write系统调用 #define Address 0x38 int main (int argc, char **argv) { // 初始化wiringPi if (wiringPiSetup() == -1) { printf("wiringPi初始化失败\n"); return 1; } int fd = wiringPiI2CSetup(Address); if (fd == -1) { printf("无法打开I2C设备,地址0x%X\n", Address); return 1; } // 打包完整的测量命令帧 uint8_t cmd_frame[] = {0xAC, 0x33, 0x00}; // 一次性发送3字节命令,保证单个I2C事务 if (write(fd, cmd_frame, sizeof(cmd_frame)) != sizeof(cmd_frame)) { printf("命令发送失败\n"); return 1; } delay(100); // 等待传感器完成测量 uint8_t rawData[6] = {0}; // 一次性读取6字节数据,保证单个I2C事务 if (read(fd, rawData, sizeof(rawData)) != sizeof(rawData)) { printf("数据读取失败\n"); return 1; } // 打印读取结果 for (uint8_t i = 0; i < 6; i++) { printf("%d: %d\n", i, rawData[i]); } return 0; }
修正说明
- 使用系统调用
write()一次性发送完整的3字节命令帧,确保所有字节属于同一个I2C写事务,让传感器正确识别测量命令。 - 使用系统调用
read()一次性读取6字节数据,确保获取传感器返回的完整数据帧,而非重复读取状态字节。 - 添加了初始化、设备打开、发送、读取各阶段的错误检查,便于排查问题。
内容的提问来源于stack exchange,提问作者RickG-VT
相关产品推荐
相关产品推荐

