1.背景
这一期要把 SEK-SEN66 连接到 UNO R4 WiFi 开发板,读取温湿度信息。
SEK-SEN66 支持 Arduino 开发环境并提供了对应的驱动代码,我按照之前的经验添加代码并烧录到开发板,发现读取不到温湿度信息。怀疑是I2C通信出现了问题。
2.I2C扫描
写了一个 I2C 扫描的代码,果然没有扫描到 SEK-SEN66,并且使用逻辑分析仪连接到 UNO R4 WiFi 开发板的 SCL/SDA ,逻辑分析已采集的信号一直都是低电平。
I2C 扫描代码如下:
#include <Arduino.h>
#include <Wire.h>
/**
* @brief 执行 I2C 地址扫描并打印结果
* @param wireInstance 指向 Wire 或 Wire1 的指针
* @param name 接口名称字符串
*/
void scanI2C(TwoWire &wireInstance, String name) {
byte error, address;
int nDevices = 0;
Serial.println("\n--- Scanning I2C [" + name + "] ---");
Serial.println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
for (address = 1; address < 127; address++) {
// 每行开始打印地址头
if (address % 16 == 0) {
if (address < 16) Serial.print("0");
Serial.print(address, HEX);
Serial.print(": ");
} else if (address == 1) {
Serial.print("00: ");
}
// 开始传输并检查 ACK
wireInstance.beginTransmission(address);
error = wireInstance.endTransmission();
if (error == 0) {
// 发现器件
if (address < 16) Serial.print("0");
Serial.print(address, HEX);
Serial.print(" ");
nDevices++;
} else if (error == 4) {
// 未知错误
Serial.print("ER ");
} else {
// 无响应
Serial.print("-- ");
}
if ((address + 1) % 16 == 0) Serial.println();
}
Serial.println();
if (nDevices == 0)
Serial.println("[Result] No I2C devices found.");
else {
Serial.print("[Result] Found ");
Serial.print(nDevices);
Serial.println(" device(s).");
}
}
void setup() {
Serial.begin(DEMO_UART_BAUDRATE);
while (!Serial);
Serial.println("========================================");
Serial.println(" Uno R4 WiFi I2C Scanner");
Serial.println("========================================");
// 初始化 I2C 接口
Wire.begin(); // 默认 SDA/SCL
Wire1.begin(); // 如果你使用的是 Qwiic 接口,请取消此行注释
}
void loop() {
// 扫描默认 Wire 接口
scanI2C(Wire, "Wire - SDA/SCL");
// 如果需要扫描 Qwiic 接口,取消下面一行的注释
scanI2C(Wire1, "Wire1 - Qwiic");
Serial.println("\nScan finished. Waiting 5 seconds for next scan...");
delay(1000);
}
然后把逻辑分析仪接到 A4/A5,采集的信号也一直都是低电平。
2.1Uno R4 WiFi 示例程序
参考官方教程,I2C发送数据
https://docs.arduino.cc/tutorials/uno-r4-wifi/cheat-sheet/#i2c
void setup() {
bsp_serial_init(DEMO_UART_BAUDRATE);
Serial.println("========================================");
Serial.println(" Uno R4 WiFi I2C Scanner");
Serial.println("========================================");
// 初始化 I2C 接口
Wire.begin(); // 默认 SDA/SCL
// 强制开启内部上拉(仅限调试用途)
// pinMode(SDA, INPUT_PULLUP);
// pinMode(SCL, INPUT_PULLUP);
Wire1.begin(); // 如果你使用的是 Qwiic 接口,请取消此行注释
}
void loop() {
// // 扫描默认 Wire 接口
// scanI2C(Wire, "Wire - SDA/SCL");
// // 如果需要扫描 Qwiic 接口,取消下面一行的注释
// scanI2C(Wire1, "Wire1 - Qwiic");
Wire.beginTransmission(1); // begin transmit to device 1
Wire.write(byte(0x00)); // send instruction byte
Wire.write(0x02); // send a value
Wire.endTransmission(); // stop transmit
Serial.println("\nScan finished. Waiting 1 seconds for next scan...");
delay(1000);
}
逻辑分析仪接 SDA, SCL,采集的信号一直都是低电平。
3.追踪
在 Wire.h 中找到 I2C 声明, UNO R4 WiFi 有两个 I2C,分别是 Wire 和 Wire1
在 Wire.cpp 中找到它们的定义
注意这里的 I2C 引脚定义,有两组:
-
第一组对应管脚 SDA–18, SCL–19
-
第二组对应管脚 SDA–27, SCL-26 (即 Qiic 管脚)
4.软件 I2C
在PlatformIO IDE中搜索由 Steve Marple 编写的 SoftWire 库,安装到此工程。
添加一个软件 I2C 扫描的程序。
首先在 SDA/SCL 管脚上扫描从设备,没有扫描到。换其他几组关键,依然没有扫描到。
4.1对应的软件 I2C 扫描程序
#include <SoftWire.h>
#include <AsyncDelay.h>
#define SW_SDA_PIN SDA
#define SW_SCL_PIN SCL
SoftWire sw(SW_SDA_PIN, SW_SCL_PIN);
void setup(void)
{
#if F_CPU >= 12000000UL
Serial.begin(921600);
#else
Serial.begin(9600);
#endif
sw.setTimeout_ms(40);
sw.begin();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Set how long we are willing to wait for a device to respond
sw.setTimeout_ms(10);
const uint8_t firstAddr = 1;
const uint8_t lastAddr = 0x7F;
Serial.println();
Serial.print("Interrogating all addresses in range 0x");
Serial.print(firstAddr, HEX);
Serial.print(" - 0x");
Serial.print(lastAddr, HEX);
Serial.println(" (inclusive) ...");
for (uint8_t addr = firstAddr; addr <= lastAddr; addr++) {
digitalWrite(LED_BUILTIN, HIGH);
delayMicroseconds(50);
uint8_t startResult = sw.llStart((addr << 1) + 1); // Signal a read
sw.stop();
if (startResult == 0) {
Serial.print("\rDevice found at 0x");
Serial.println(addr, HEX);
Serial.flush();
}
digitalWrite(LED_BUILTIN, LOW);
delay(50);
}
Serial.println("Finished");
}
void loop(void)
{
;
}
4.2 接上拉电阻之后,扫描成功
这个章节是在硬件 I2C 调通了之后,再次验证软件 I2C 功能。
5.硬件I2C 问题解决
参考论坛帖子
https://docs.arduino.cc/tutorials/uno-r4-wifi/cheat-sheet/#i2c
5. 1UNO R4 WiFi 原理图
从原理图看,SDA/SCL 对应的I2C的确没有接上拉电阻,需要外接上拉电阻,否则一直都是低电平。
5.2解决办法
在 SDA/SCL 管脚上接外部上拉电阻,电阻值范围 2.2K 欧姆到 10K 欧姆之间均可。
接法:
-
将一个电阻的一端接到 SDA,另一端接到 3.3V
-
将一个电阻的一端接到 SCL,另一端接到 3.3V
5.3我的接线
-
SDA/SCL 接到传感器
-
A4/A5 接外部上拉电阻10K欧码,接入到3.3V电源
5.4实测结果
I2C扫描,终于扫描到了正确的传感器地址,SEN66 传感器的I2C地址就是 0x6B,终于成功了。
6.SEN66读取温湿度
在调通了I2C 通讯之后,运行读取 SEN66 读取温湿度的程序,终于读取成功。
对应的代码:
#include <Arduino.h>
#include <SensirionI2cSen66.h>
#include <Wire.h>
// macro definitions
// make sure that we use the proper definition of NO_ERROR
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0
SensirionI2cSen66 sensor;
static char errorMessage[64];
static int16_t error;
void setup() {
Serial.begin(DEMO_UART_BAUDRATE);
while (!Serial) {
delay(100);
}
Wire.begin();
sensor.begin(Wire, SEN66_I2C_ADDR_6B);
error = sensor.deviceReset();
if (error != NO_ERROR) {
Serial.print("Error trying to execute deviceReset(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
delay(1200);
int8_t serialNumber[32] = {0};
error = sensor.getSerialNumber(serialNumber, 32);
if (error != NO_ERROR) {
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("serialNumber: ");
Serial.print((const char*)serialNumber);
Serial.println();
error = sensor.startContinuousMeasurement();
if (error != NO_ERROR) {
Serial.print("Error trying to execute startContinuousMeasurement(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
}
void loop() {
float massConcentrationPm1p0 = 0.0;
float massConcentrationPm2p5 = 0.0;
float massConcentrationPm4p0 = 0.0;
float massConcentrationPm10p0 = 0.0;
float humidity = 0.0;
float temperature = 0.0;
float vocIndex = 0.0;
float noxIndex = 0.0;
uint16_t co2 = 0;
delay(1000);
error = sensor.readMeasuredValues(
massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
massConcentrationPm10p0, humidity, temperature, vocIndex, noxIndex,
co2);
if (error != NO_ERROR) {
Serial.print("Error trying to execute readMeasuredValues(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("pm1p0: ");
Serial.print(massConcentrationPm1p0);
Serial.print("\t");
Serial.print("pm2p5: ");
Serial.print(massConcentrationPm2p5);
Serial.print("\t");
Serial.print("pm4p0: ");
Serial.print(massConcentrationPm4p0);
Serial.print("\t");
Serial.print("pm10p0: ");
Serial.print(massConcentrationPm10p0);
Serial.print("\t");
Serial.print("humidity: ");
Serial.print(humidity);
Serial.print("\t");
Serial.print("temperature: ");
Serial.print(temperature);
Serial.print("\t");
Serial.print("vocIndex: ");
Serial.print(vocIndex);
Serial.print("\t");
Serial.print("noxIndex: ");
Serial.print(noxIndex);
Serial.print("\t");
Serial.print("co2: ");
Serial.print(co2);
Serial.println();
}
7.总结
回顾整个调试过程:
-
添加 SEN66 驱动程序,通过I2C通信读取传感器数据,读取失败,怀疑I2C通信有问题或者传感器出了问题;
-
把 SEN66 传感器连接到其他主控(例如esp32s3开发板),一样的程序,能读取传感器数据,说明传感器是正常的,那说明 UNO R4 WiFi 的 I2C 通信有问题;
-
怀疑 UNO R4 WiFi 的 Wire (对应 SDA/SCL 管脚)通信有问题,连接逻辑分析观察管脚,发现两个管脚一直都是低电平,测试了 A4/A5 管脚,也是低电平,说明确实I2C有问题;
-
搜索论坛,发现 SDA/SCL 没有接上拉电阻,我在面包板上把 A4/A5 连接 10K 欧姆电阻到3.3V,然后重新运行硬件I2C扫描,成功扫描到SEN66传感器的I2C地址,解决了问题;
-
再次运行软件I2C扫描,也成功扫描到SEN66传感器的I2C地址;
-
最后运行读取SEN66传感器的程序,成功读取到9种传感器数值,I2C通信正常;
I2C通讯出故障,应该先通过逻辑分析仪或者示波器查看信号质量,然后检查原理图确认问题。










