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

树莓派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;
}

修正说明

  1. 使用系统调用write()一次性发送完整的3字节命令帧,确保所有字节属于同一个I2C写事务,让传感器正确识别测量命令。
  2. 使用系统调用read()一次性读取6字节数据,确保获取传感器返回的完整数据帧,而非重复读取状态字节。
  3. 添加了初始化、设备打开、发送、读取各阶段的错误检查,便于排查问题。

内容的提问来源于stack exchange,提问作者RickG-VT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:45:27