Raspberry Pi Pico无法通过I2C检测MS5611模块的求助
问题分析与解决方案
核心问题定位
你的代码存在几个语法/逻辑错误,直接导致MS5611无法响应Pico的I2C请求:
- 数组初始化错误:
ms5611_read函数中,D2_G被定义为单字节变量,却用双字节数组初始化,发送的命令完全无效。 - 缓冲区越界:读取MS5611的3字节D1/D2数据时,仅定义了2字节数组,会破坏内存引发逻辑异常。
- 缺少转换等待时间:发送D1/D2转换命令后未等待传感器完成模数转换,直接读取会得到无效数据。
ESP32的I2C驱动容错性较强,这些错误未暴露,但Pico对时序和数据格式的敏感度更高,直接引发通信失败。
修正后的代码
main.h
#ifndef __MAIN_H_ #define __MAIN_H_ #define MS5611_ADDRESS 0x77 // 改用#define避免static变量作用域问题 #define MS5611_CMD_RESET 0x1E #define MS5611_CMD_READ_CMD 0x00 #define MS5611_CMD_READ_PROM 0xA0 #define MS5611_CMD_D1 0x48 // OSR=4096,转换时间约8.2ms #define MS5611_CMD_D2 0x58 // OSR=4096,转换时间约8.2ms struct MS5611_CALIBRATION { uint16_t C[10]; uint32_t D1; uint32_t D2; int32_t dT; // dT为有符号值,需用int32_t int32_t TEMP; // 温度计算结果为有符号 int64_t OFF; int64_t SENS; uint32_t P; }; void ms5611_init(); void ms5611_read(); void ms5611_calculate(); // 新增温度/压力计算函数 #endif
main.c
#include <stdio.h> #include "hardware/i2c.h" #include "pico/binary_info.h" #include "pico/stdlib.h" #include "hardware/gpio.h" #include "main.h" #define I2C_PORT i2c0 struct MS5611_CALIBRATION ms5611_calibration; void ms5611_init() { uint8_t src; uint8_t buffer[2]; int ret; // 发送复位命令 src = MS5611_CMD_RESET; sleep_ms(10); // 复位需至少2.8ms,10ms足够 ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, &src, 1, false); if (ret != 1) { printf("MS5611复位失败\n"); return; } sleep_ms(10); // 读取PROM校准数据 for(int offset = 0; offset < 7; offset++) { src = MS5611_CMD_READ_PROM + (offset * 2); ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, &src, 1, true); if (ret != 1) { printf("读取PROM命令发送失败,offset=%d\n", offset); continue; } ret = i2c_read_blocking(I2C_PORT, MS5611_ADDRESS, buffer, 2, false); if (ret != 2) { printf("读取PROM数据失败,offset=%d\n", offset); continue; } ms5611_calibration.C[offset] = (uint16_t)buffer[0] << 8 | buffer[1]; printf("PROM C[%d]: 0x%04X\n", offset, ms5611_calibration.C[offset]); sleep_ms(1); // 短延时即可,无需10ms } } void ms5611_read() { uint8_t cmd[2]; uint8_t data[3]; // 改为3字节缓冲区,适配D1/D2的3字节数据 int ret; // 读取D1(压力) cmd[0] = MS5611_CMD_D1; ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, cmd, 1, false); if (ret != 1) { printf("发送D1命令失败\n"); return; } sleep_ms(10); // 等待转换完成(OSR=4096需8.2ms) cmd[0] = MS5611_CMD_READ_CMD; ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, cmd, 1, true); if (ret != 1) { printf("发送D1读取命令失败\n"); return; } ret = i2c_read_blocking(I2C_PORT, MS5611_ADDRESS, data, 3, false); if (ret != 3) { printf("读取D1数据失败\n"); return; } ms5611_calibration.D1 = (uint32_t)data[0] << 16 | (uint32_t)data[1] << 8 | data[2]; printf("D1: %lu\n", ms5611_calibration.D1); // 读取D2(温度) cmd[0] = MS5611_CMD_D2; ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, cmd, 1, false); if (ret != 1) { printf("发送D2命令失败\n"); return; } sleep_ms(10); // 等待转换完成 cmd[0] = MS5611_CMD_READ_CMD; ret = i2c_write_blocking(I2C_PORT, MS5611_ADDRESS, cmd, 1, true); if (ret != 1) { printf("发送D2读取命令失败\n"); return; } ret = i2c_read_blocking(I2C_PORT, MS5611_ADDRESS, data, 3, false); if (ret != 3) { printf("读取D2数据失败\n"); return; } ms5611_calibration.D2 = (uint32_t)data[0] << 16 | (uint32_t)data[1] << 8 | data[2]; printf("D2: %lu\n", ms5611_calibration.D2); } void ms5611_calculate() { // 按照MS5611 datasheet公式计算温度和压力 ms5611_calibration.dT = (int32_t)ms5611_calibration.D2 - ((int32_t)ms5611_calibration.C[5] << 8); ms5611_calibration.TEMP = 2000 + ((int64_t)ms5611_calibration.dT * ms5611_calibration.C[6]) / 0x800000; int64_t OFF = (int64_t)ms5611_calibration.C[2] << 16 + ((int64_t)ms5611_calibration.C[4] * ms5611_calibration.dT) / 0x2000; int64_t SENS = (int64_t)ms5611_calibration.C[1] << 15 + ((int64_t)ms5611_calibration.C[3] * ms5611_calibration.dT) / 0x4000; // 低温补偿(温度低于20℃时) if (ms5611_calibration.TEMP < 2000) { int32_t T2 = ((int64_t)ms5611_calibration.dT * ms5611_calibration.dT) / 0x80000000; int64_t OFF2 = 5 * ((int64_t)(ms5611_calibration.TEMP - 2000) * (ms5611_calibration.TEMP - 2000)) / 2; int64_t SENS2 = 5 * ((int64_t)(ms5611_calibration.TEMP - 2000) * (ms5611_calibration.TEMP - 2000)) / 4; if (ms5611_calibration.TEMP < -1500) { OFF2 += 7 * ((int64_t)(ms5611_calibration.TEMP + 1500) * (ms5611_calibration.TEMP + 1500)); SENS2 += 11 * ((int64_t)(ms5611_calibration.TEMP + 1500) * (ms5611_calibration.TEMP + 1500)) / 2; } ms5611_calibration.TEMP -= T2; OFF -= OFF2; SENS -= SENS2; } ms5611_calibration.OFF = OFF; ms5611_calibration.SENS = SENS; ms5611_calibration.P = ((ms5611_calibration.D1 * SENS) / 0x200000 - OFF) / 0x100; } int main() { stdio_init_all(); sleep_ms(2000); // 等待USB串口初始化 // 初始化I2C,改用100kHz波特率(兼容性更好) i2c_init(I2C_PORT, 100000); gpio_set_function(8, GPIO_FUNC_I2C); gpio_set_function(9, GPIO_FUNC_I2C); gpio_pull_up(8); gpio_pull_up(9); bi_decl(bi_2pins_with_func(8, 9, GPIO_FUNC_I2C)); sleep_ms(100); ms5611_init(); while(1) { ms5611_read(); ms5611_calculate(); // 执行温度压力计算 printf("#############################\n"); printf("温度: %.2f ℃\n", ms5611_calibration.TEMP / 100.0); printf("压力: %.2f hPa\n", ms5611_calibration.P / 100.0); printf("D1: %lu\n", ms5611_calibration.D1); printf("D2: %lu\n", ms5611_calibration.D2); sleep_ms(1000); } }
CMakeLists.txt
无需修改,保持原文件即可。
额外建议
- 先执行I2C扫描:在初始化MS5611前,运行Pico官方I2C扫描代码,确认0x77地址能被检测到,排除硬件连接问题。
- 检查引脚连接:再次确认Pico的SDA/SCL引脚与PCB对应引脚连接正确,避免接反。
- 波特率调整:若仍有问题,可将I2C波特率降至50kHz,进一步提升兼容性。
内容的提问来源于stack exchange,提问作者Erkan Erdal
相关产品推荐
相关产品推荐

