Skip to content

BMP280 气压传感器

手册

BMP280 数据手册
Orangepi zero 3 开发板
ESP32-C3 中 I2C 驱动
ESP32_IDF I2C 知识

一.接口说明

1.BMP280 芯片接口

BMP280 芯片支持 I²C 和 SPI 两种数据接口。

  • I²C 接口:支持标准、快速和高速模式,其 7 位设备地址为 111011x,其中 6 个 MSB 位固定,最后一位由 SDO 值决定,连接 SDO 到 GND 时地址为 1110110(0x76),连接到 VDDIO 时地址为 1110111(0x77)。在 I²C 模式下,最大速率可到 3.4 MHz。使用的引脚包括 SCK(串行时钟,对应 SCL)、SDI(数据,对应 SDA)、SDO(用于确定从设备地址 LSB),且 CSB 必须连接到 VDDIO 以选择 I²C 接口,SDI 是双向的,需通过上拉电阻连接到 VDDIO。
  • SPI 接口:支持 4 线和 3 线配置下的 SPI 模式 '00'(CPOL = CPHA = '0')和模式 '11'(CPOL = CPHA = '1'),自动选择模式由 CSB 下降沿后的 SCK 值确定。在 SPI 模式下,最大频率为 10 MHz。使用的引脚包括 CSB(芯片选择,低电平有效)、SCK(串行时钟)、SDI(串行数据输入,在 3 线模式下为数据输入 / 输出)、SDO(串行数据输出,在 3 线模式下为高阻态)。

2.接口选择

是根据 CSB(芯片选择)状态自动完成的。如果 CSB 连接到 VDDIO,则 I²C 接口处于活动状态;如果 CSB 被拉低,则 SPI 接口被激活。 一旦 CSB 被拉低(无论是否发生时钟周期),I²C 接口将被禁用,直到下一次上电复位。 这是为了避免将 SPI 流量误解码为另一个从设备的 I²C 数据。由于只有在 VDD 和 VDDIO 都建立时才会执行上电复位,因此不会因上电顺序而导致错误的协议检测风险。 但是,如果要使用 I²C 且 CSB 不是直接连接到 VDDIO 而是通过可编程引脚连接,则必须确保在设备上电复位期间该引脚已经输出 VDDIO 电平。 如果不是这种情况,设备将锁定在 SPI 模式并且不会响应 I²C 命令。

二.编程说明:

1. 整体概述

BMP280 芯片的 I²C 接口支持标准、快速和高速模式,最大速率可达 3.4 MHz。7 位设备地址为 111011x,其中 6 个 MSB 位固定,最后一位由 SDO 值决定,连接 SDO 到 GND 时地址为 1110110(0x76),连接到 VDDIO 时地址为 1110111(0x77)。

2. 基本时序信号

起始条件(Start Condition)

在 I²C 总线上,当 SCL 线为高电平时,SDI 线由高电平变为低电平,这表示一个起始条件。起始条件标志着一次数据传输的开始。

停止条件(Stop Condition)

当 SCL 线为高电平时,SDI 线由低电平变为高电平,这表示一个停止条件。停止条件标志着一次数据传输的结束。

应答位(ACK)

在每传输 8 位数据后,接收方需要发送一个应答位(ACK)来表示已经成功接收到数据。当 SCL 为高电平时,SDI 线为低电平表示 ACK;SDI 线为高电平表示非应答(NACK)。

3. 数据传输过程

写操作时序

  • 发送起始条件:主设备首先在总线上发送起始条件,启动一次写操作。
  • 发送从设备地址和写标志:主设备发送 7 位的 BMP280 从设备地址,紧接着发送一个写标志位(逻辑 0)。
  • 等待从设备应答:BMP280 作为从设备在接收到地址和写标志后,会发送一个 ACK 信号。
  • 发送寄存器地址:主设备发送要写入数据的 BMP280 内部寄存器地址。
  • 等待从设备应答:BMP280 再次发送 ACK 信号表示已成功接收寄存器地址。
  • 发送数据:主设备依次发送要写入寄存器的数据字节,每发送一个字节,BMP280 都会发送一个 ACK 信号进行确认。
  • 发送停止条件:主设备在完成数据发送后,发送停止条件,结束本次写操作。

读操作时序

  • 发送起始条件:主设备发送起始条件,启动一次读操作。
  • 发送从设备地址和写标志:主设备先发送 7 位的 BMP280 从设备地址和写标志位(逻辑 0),用于指定要读取数据的 BMP280 设备。
  • 等待从设备应答:BMP280 发送 ACK 信号确认接收。
  • 发送寄存器地址:主设备发送要读取数据的 BMP280 内部寄存器地址。
  • 等待从设备应答:BMP280 发送 ACK 信号表示已接收寄存器地址。
  • 重复起始条件:主设备发送一个重复起始条件(Repeated Start),用于切换到读模式。
  • 发送从设备地址和读标志:主设备发送 7 位的 BMP280 从设备地址和读标志位(逻辑 1)。
  • 等待从设备应答:BMP280 发送 ACK 信号确认。
  • 接收数据:BMP280 开始发送指定寄存器中的数据字节,主设备每接收一个字节后,除了最后一个字节外,都要发送 ACK 信号;对于最后一个字节,主设备发送 NACK 信号表示不再接收数据。
  • 发送停止条件:主设备发送停止条件,结束本次读操作。

4. 时序参数

不同的 I²C 模式(标准、快速、高速)有不同的时序参数要求,例如时钟信号的高低电平时间、起始和停止条件的建立和保持时间等。这些参数需要根据 BMP280 芯片的数据手册中的具体规定来设置,以确保 I²C 通信的稳定性和正确性。例如,在标准模式下,时钟频率一般为 100 kHz,在快速模式下可以达到 400 kHz,高速模式下能达到 3.4 MHz,主设备需要根据所选模式来调整时钟信号的频率。

三.编程要点:

  • BMP280 需要初始化,包括读取校准参数,设置工作模式、采样率和滤波器。校准参数是存储在传感器中的,需要读取并保存,用于后续的温度和压力计算。这部分是关键,如果校准参数读取错误,计算结果会不准确。
  • 然后,需要配置传感器的测量模式。BMP280 有不同模式,比如睡眠模式、强制模式和正常模式。用户可能需要单次测量或者连续测量。这里可能需要设置为强制模式,每次触发一次测量,然后读取数据。
  • 读取温度和压力的原始数据后,需要进行补偿计算。BMP280 的补偿算法比较复杂,涉及多个校准系数,需要按照数据手册中的公式处理。这部分需要仔细实现,避免计算错误。
  • 需要考虑单位转换,比如压力通常以 hPa 或 kPa 为单位,而 BMP280 的输出是 Pa,可能需要除以 100 得到 hPa。

四.编程步骤:

  1. 确定 BMP280 的 I2C 地址 0x77。
  2. 初始化 I2C 总线,连接到传感器。
  3. 读取校准参数。
  4. 配置传感器的工作模式和采样率。
  5. 触发测量,等待数据就绪。
  6. 读取原始温度和压力数据。
  7. 使用校准参数进行补偿计算。
  8. 输出结果,处理单位转换。
  9. 添加循环和异常处理。

五.代码如下:

这是使用 OrangePi zero 3 的 I2C 接口连接的 BMP280 传感器的 Python 代码示例。代码中包括了初始化传感器、读取校准参数、配置传感器、读取温度和压力数据、进行补偿计算以及输出结果等步骤。

1. Python 代码示例:

python
import smbus2
import time

# BMP280 默认 I2C 地址(如果 SDO 接地则为 0x76,接高电平为 0x77)
BMP280_ADDR = 0x77  # 根据硬件连接调整

# BMP280 校准参数寄存器地址
REG_CALIB = 0x88    # 校准参数起始地址(共24字节)
REG_ID = 0xD0       # 芯片ID寄存器
REG_CTRL_MEAS = 0xF4  # 控制测量寄存器
REG_CONFIG = 0xF5   # 配置寄存器
REG_PRESS_MSB = 0xF7  # 压力数据高位寄存器
REG_TEMP_MSB = 0xFA   # 温度数据高位寄存器

class BMP280:
    def __init__(self, bus_number=3):
        self.bus = smbus2.SMBus(bus_number)
        self.dig_T = []  # 温度校准参数
        self.dig_P = []  # 气压校准参数
        self._read_calibration()  # 读取校准参数
        self._configure_sensor()  # 配置传感器



    def _read_calibration(self):
        def bytes_to_signed_short(msb, lsb):
            value = (msb << 8) | lsb
            return value - 0x10000 if value > 0x7FFF else value
        """读取校准参数(24字节)"""
        calib_data = self.bus.read_i2c_block_data(BMP280_ADDR, REG_CALIB, 24)

        # 解析温度校准参数(参考数据手册)
        self.dig_T = [
            (calib_data[1] << 8) | calib_data[0],  # T1(无符号)]
            bytes_to_signed_short(calib_data[3], calib_data[2]),  # T2(有符号)
            bytes_to_signed_short(calib_data[5], calib_data[4])   # T3(有符号)
        ]
        # 解析气压校准参数
        self.dig_P = [
            (calib_data[7] << 8) | calib_data[6],  # P1(无符号)
            bytes_to_signed_short(calib_data[9], calib_data[8]),   # P2(有符号)
            bytes_to_signed_short(calib_data[11], calib_data[10]), # P3(有符号)
            bytes_to_signed_short(calib_data[13], calib_data[12]), # P4(有符号)
            bytes_to_signed_short(calib_data[15], calib_data[14]), # P5(有符号)
            bytes_to_signed_short(calib_data[17], calib_data[16]), # P6(有符号)
            bytes_to_signed_short(calib_data[19], calib_data[18]), # P7(有符号)
            bytes_to_signed_short(calib_data[21], calib_data[20]), # P8(有符号)
            bytes_to_signed_short(calib_data[23], calib_data[22])  # P9(有符号)
        ]
        # 打印气压校准参数
        # print("气压校准参数 dig_P:")
        # for i, val in enumerate(self.dig_P, start=1):
        #     print(f"P{i}: {val} ")  # 同时显示十进制和十六进制


    def _configure_sensor(self):
        """配置传感器工作模式"""
        # 设置过采样:温度x2,气压x16,模式=正常模式
        # 二进制: 0b01010111 (0x57)
        self.bus.write_byte_data(BMP280_ADDR, REG_CTRL_MEAS, 0x57)
        # 配置滤波器系数和待机时间(根据需求调整)
        self.bus.write_byte_data(BMP280_ADDR, REG_CONFIG, 0x14)  # 滤波器系数=4,待机时间=500ms

    def _read_raw_data(self):
        """读取原始温度和气压数据"""
        data = self.bus.read_i2c_block_data(BMP280_ADDR, REG_PRESS_MSB, 6)
        # 解析气压(20位)
        press_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        # 解析温度(20位)
        temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
        # if temp_raw & 0x80000:  # 检查符号位
        #     temp_raw -= 0x100000  # 扩展为32位有符号负数
        return temp_raw, press_raw

    def _compensate_temperature(self, raw_temp):
        """温度补偿计算(返回单位为°C)"""
        T1, T2, T3 = self.dig_T
        var1 = (raw_temp / 16384.0 - T1 / 1024.0) * T2
        var2 = (raw_temp / 131072.0 - T1 / 8192.0) ** 2 * T3
        t_fine = var1 + var2
        temperature = t_fine / 5120.0
        return temperature, t_fine

    def _compensate_pressure(self, raw_press, t_fine):
        """气压补偿计算(返回单位为Pa)"""
        P1, P2, P3, P4, P5, P6, P7, P8, P9 = self.dig_P
        var1 = t_fine / 2.0 - 64000.0
        var2 = var1 * var1 * P6 / 32768.0
        var2 = var2 + var1 * P5 * 2.0
        var2 = var2 / 4.0 + P4 * 65536.0
        # 修正var1计算
        var1 = (P3 * var1 ** 2 / 16384.0 + P2 * var1) / 524288.0  # 关键修正
        var1 = (1.0 + var1 / 32768.0) * P1
        pressure = 1048576.0 - raw_press
        pressure = (pressure - var2 / 4096.0) * 6250.0 / var1
        var1 = P9 * pressure * pressure / 2147483648.0
        var2 = pressure * P8 / 32768.0
        pressure = pressure + (var1 + var2 + P7) / 16.0
        return pressure

    def read_data(self):
        """读取校准后的温度和气压"""
        temp_raw, press_raw = self._read_raw_data()
        temperature, t_fine = self._compensate_temperature(temp_raw)
        pressure = self._compensate_pressure(press_raw, t_fine)
        return temperature, pressure / 100.0  # 转换为hPa

def main():
    try:
        sensor = BMP280(bus_number=3)  # 使用 /dev/i2c-3
        while True:
            temperature, pressure = sensor.read_data()
            print(f"温度: {temperature:.2f}°C, 气压: {pressure:.2f} hPa")
            time.sleep(1)
    except KeyboardInterrupt:
        print("程序终止")
    except Exception as e:
        print(f"错误: {e}")

if __name__ == "__main__":
    main()

2. ESP32-C3 代码示例1:

下面是使用 ESP32-C3 模块,借助于 ESP-IDF 框架开发的代码,来读取 BMP280 的数据:

c
#include <stdio.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define I2C_PORT I2C_NUM_0
#define BMP280_ADDR 0x77
#define SDA_GPIO 9
#define SCL_GPIO 8

// BMP280校准参数结构体
typedef struct
{
    uint16_t dig_T1;
    int16_t dig_T2;
    int16_t dig_T3;
    uint16_t dig_P1;
    int16_t dig_P2;
    int16_t dig_P3;
    int16_t dig_P4; // 添加缺失的成员
    int16_t dig_P5;
    int16_t dig_P6;
    int16_t dig_P7;
    int16_t dig_P8;
    int16_t dig_P9;
} bmp280_calib;

// 读取校准参数(修改I2C读写方式)
static esp_err_t read_calibration_data(bmp280_calib *calib)
{
    uint8_t data[24];
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, 0x88, true); // 校准参数起始地址
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read(cmd, data, sizeof(data), I2C_MASTER_LAST_NACK);
    i2c_master_stop(cmd);

    esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);

    if (ret != ESP_OK)
        return ret;

    calib->dig_T1 = (data[1] << 8) | data[0];
    calib->dig_T2 = (int16_t)((data[3] << 8) | data[2]);
    calib->dig_T3 = (int16_t)((data[5] << 8) | data[4]);
    calib->dig_P1 = (uint16_t)((data[7] << 8) | data[6]);
    calib->dig_P2 = (int16_t)((data[9] << 8) | data[8]);
    calib->dig_P3 = (data[11] << 8) | data[10];
    calib->dig_P4 = (data[13] << 8) | data[12];
    calib->dig_P5 = (data[15] << 8) | data[14];
    calib->dig_P6 = (data[17] << 8) | data[16];
    calib->dig_P7 = (data[19] << 8) | data[18];
    calib->dig_P8 = (data[21] << 8) | data[20];
    calib->dig_P9 = (data[23] << 8) | data[22];
    return ESP_OK;
}

// 温度补偿计算
static float compensate_temp(int32_t raw_temp, const bmp280_calib *calib)
{
    float var1 = (((float)raw_temp) / 16384.0 - ((float)calib->dig_T1) / 1024.0) * ((float)calib->dig_T2);
    float var2 = ((((float)raw_temp) / 131072.0 - ((float)calib->dig_T1) / 8192.0) *
                  (((float)raw_temp) / 131072.0 - ((float)calib->dig_T1) / 8192.0)) *
                 ((float)calib->dig_T3);
    return (var1 + var2) / 5120.0;
}

// 气压补偿计算
static float compensate_press(int32_t raw_press, float t_fine, const bmp280_calib *calib)
{
    float var1 = (t_fine / 2.0) - 64000.0;
    float var2 = var1 * var1 * ((float)calib->dig_P6) / 32768.0;
    var2 = var2 + var1 * ((float)calib->dig_P5) * 2.0;
    var2 = (var2 / 4.0) + (((float)calib->dig_P4) * 65536.0);
    var1 = (((float)calib->dig_P3) * var1 * var1 / 16384.0 + ((float)calib->dig_P2) * var1) / 524288.0;
    var1 = (1.0 + var1 / 32768.0) * ((float)calib->dig_P1);

    if (var1 == 0)
        return 0;

    float pressure = 1048576.0 - (float)raw_press;
    pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1;
    var1 = ((float)calib->dig_P9) * pressure * pressure / 2147483648.0;
    var2 = pressure * ((float)calib->dig_P8) / 32768.0;
    return pressure + (var1 + var2 + ((float)calib->dig_P7)) / 16.0;
}

void app_main(void)
{
    // I2C初始化(保持修改后的配置)
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = SDA_GPIO,
        .scl_io_num = SCL_GPIO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 400000};
    ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &conf));
    ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, conf.mode, 0, 0, 0));

    // 配置BMP280(修改为传统I2C写法)
    uint8_t config_data[2] = {0xF4, 0b01010111};
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write(cmd, config_data, sizeof(config_data), true);
    i2c_master_stop(cmd);
    ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(1000)));
    i2c_cmd_link_delete(cmd);

    bmp280_calib calib;
    ESP_ERROR_CHECK(read_calibration_data(&calib));

    while (1)
    {
        // 读取传感器数据(修改为传统I2C写法)
        uint8_t raw_data[6];
        cmd = i2c_cmd_link_create();
        i2c_master_start(cmd);
        i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_WRITE, true);
        i2c_master_write_byte(cmd, 0xF7, true); // 数据寄存器地址
        i2c_master_start(cmd);
        i2c_master_write_byte(cmd, (BMP280_ADDR << 1) | I2C_MASTER_READ, true);
        i2c_master_read(cmd, raw_data, sizeof(raw_data), I2C_MASTER_LAST_NACK);
        i2c_master_stop(cmd);

        esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(1000));
        i2c_cmd_link_delete(cmd);

        if (ret == ESP_OK)
        {
            // 添加数据解析代码
            int32_t temp_raw = (raw_data[3] << 12) | (raw_data[4] << 4) | (raw_data[5] >> 4);
            int32_t press_raw = (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4);
            // 打印 temp_raw press_raw
            ESP_LOGI("BMP280", "temp_raw: %ld, press_raw: %ld", temp_raw, press_raw);
            // 新增数据有效性检查
            if (temp_raw == 0x80000 || press_raw == 0x80000)
            { // 0x80000表示无效数据
                ESP_LOGE("BMP280", "无效传感器数据");
                continue;
            }
            float temp = compensate_temp(temp_raw, &calib);
            float pressure = compensate_press(press_raw, temp, &calib);
            printf("校准后数据: %.2f °C, %.2f hPa\n", temp, pressure / 100.0);
        }
        else
        {
            ESP_LOGE("BMP280", "读取失败: 0x%x", ret);
        }
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

3. ESP32-C3 代码示例2(推荐):

这部分是(使用ESP-IDF API 来实现I2C)的代码示例,适用于ESP32-C3开发板。

c
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c_master.h"

#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SCL_IO 4
#define I2C_MASTER_SDA_IO 5
#define I2C_MASTER_TIMEOUT_MS 1000

#define BMP280_ADDR_LEN I2C_ADDR_BIT_LEN_7 // BMP280地址长度
#define BMP280_ADDR 0x77                   // BMP280 I2C地址
#define REG_CTRL_MEAS 0xF4                 // 控制测量寄存器地址
#define REG_CONFIG 0xF5                    // 配置寄存器地址
#define REG_PRESS_TEMP_MSB 0xF7            // 温度和压力数据起始地址
#define REG_CALIBRATION 0x88               // 校准参数起始地址

// BMP280校准参数结构体
typedef struct
{
    uint16_t dig_T1;
    int16_t dig_T2;
    int16_t dig_T3;
    uint16_t dig_P1;
    int16_t dig_P2;
    int16_t dig_P3;
    int16_t dig_P4;
    int16_t dig_P5;
    int16_t dig_P6;
    int16_t dig_P7;
    int16_t dig_P8;
    int16_t dig_P9;
} bmp280_calib;

esp_err_t Bmp280_read_calibration_data(i2c_master_dev_handle_t *dev_handle, bmp280_calib *calib);

void BMP280_init(i2c_master_dev_handle_t *dev_handle, bmp280_calib *calib)
{
    Bmp280_read_calibration_data(dev_handle, calib); // 读取校准参数
    // 等待校准完成
    vTaskDelay(pdMS_TO_TICKS(100)); // 等待校准完成
    // TODO: Implement initialization code for the ATH20 sensor
    uint8_t MEAS[2] = {REG_CTRL_MEAS, 0x57}; // 初始化命令
    i2c_master_transmit(*dev_handle, MEAS, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    uint8_t CONF[2] = {REG_CONFIG, 0x14}; // 配置参数
    i2c_master_transmit(*dev_handle, CONF, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    vTaskDelay(pdMS_TO_TICKS(100)); // 等待校准完成
}

void BMP280_read_rawdata(i2c_master_dev_handle_t *dev_handle, uint8_t *data)
{
    uint8_t reg = REG_PRESS_TEMP_MSB; // 读取温度和压力数据
    i2c_master_transmit_receive(*dev_handle, &reg, 1, data, 6, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}

// 读取校准参数(修改I2C读写方式)
esp_err_t Bmp280_read_calibration_data(i2c_master_dev_handle_t *dev_handle, bmp280_calib *calib)
{
    uint8_t data[24];
    uint8_t reg = REG_CALIBRATION; // 校准参数起始地址
    i2c_master_transmit_receive(*dev_handle, &reg, 1, data, 24, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    // calib->dig_T1 = (uint16_t)((data[1] << 8) | data[0]);
    calib->dig_T1 = (((uint16_t)data[1]) << 8) + data[0];
    calib->dig_T2 = (((int16_t)data[3]) << 8) + data[2];
    calib->dig_T3 = (((int16_t)data[5]) << 8) + data[4];
    calib->dig_P1 = (((uint16_t)data[7]) << 8) + data[6];
    calib->dig_P2 = (((int16_t)data[9]) << 8) + data[8];
    calib->dig_P3 = (((int16_t)data[11]) << 8) + data[10];
    calib->dig_P4 = (((int16_t)data[13]) << 8) + data[12];
    calib->dig_P5 = (((int16_t)data[15]) << 8) + data[14];
    calib->dig_P6 = (((int16_t)data[17]) << 8) + data[16];
    calib->dig_P7 = (((int16_t)data[19]) << 8) + data[18];
    calib->dig_P8 = (((int16_t)data[21]) << 8) + data[20];
    calib->dig_P9 = (((int16_t)data[23]) << 8) + data[22];
    // 打印校准参数
    ESP_LOGI("BMP280", "dig_T1: %d", calib->dig_T1);
    ESP_LOGI("BMP280", "dig_T2: %d", calib->dig_T2);
    ESP_LOGI("BMP280", "dig_T3: %d", calib->dig_T3);
    ESP_LOGI("BMP280", "dig_P1: %d", calib->dig_P1);
    ESP_LOGI("BMP280", "dig_P2: %d", calib->dig_P2);
    ESP_LOGI("BMP280", "dig_P3: %d", calib->dig_P3);
    ESP_LOGI("BMP280", "dig_P4: %d", calib->dig_P4);
    ESP_LOGI("BMP280", "dig_P5: %d", calib->dig_P5);
    ESP_LOGI("BMP280", "dig_P6: %d", calib->dig_P6);
    ESP_LOGI("BMP280", "dig_P7: %d", calib->dig_P7);
    ESP_LOGI("BMP280", "dig_P8: %d", calib->dig_P8);
    ESP_LOGI("BMP280", "dig_P9: %d", calib->dig_P9);

    return ESP_OK;
}

// 温度补偿计算
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
static float compensate_temp(int32_t raw_temp, const bmp280_calib *calib, float *t_fine)
{

    // BMP280_S32_t t_fine;
    double dig_T1 = (double)calib->dig_T1;
    double dig_T2 = (double)calib->dig_T2;
    double dig_T3 = (double)calib->dig_T3;
    double var1, var2, tmp, adc_T;
    adc_T = (double)raw_temp;
    var1 = (adc_T / 16384.0 - dig_T1 / 1024.0) * dig_T2;
    tmp = (adc_T / 131072.0 - dig_T1 / 8192.0);
    var2 = tmp * tmp * dig_T3;
    *t_fine = var1 + var2;
    float T = *t_fine / 5120.0;
    return T;
}

// 气压补偿计算
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
static float compensate_press(int32_t raw_press, float t_fine, const bmp280_calib *calib)
{
    // 从校准数据结构中提取校准参数
    double P1 = (double)calib->dig_P1;
    double P2 = (double)calib->dig_P2;
    double P3 = (double)calib->dig_P3;
    double P4 = (double)calib->dig_P4;
    double P5 = (double)calib->dig_P5;
    double P6 = (double)calib->dig_P6;
    double P7 = (double)calib->dig_P7;
    double P8 = (double)calib->dig_P8;
    double P9 = (double)calib->dig_P9;

    double adc_P = (double)raw_press;
    // BMP280_S64_t var1, var2, pressure;
    double var1, var2, pressure;
    var1 = (double)t_fine / 2.0 - 64000.0;
    var2 = var1 * var1 * P6 / 32768.0;
    var2 = var2 + var1 * P5 * 2.0;
    var2 = var2 / 4.0 + (P4 * 65536.0);
    // 修正var1计算
    var1 = (P3 * var1 * var1 / 524288.0 + P2 * var1) / 524288.0; // 关键修正
    var1 = (1.0 + var1 / 32768.0) * P1;
    if( var1 == 0.0)
        return 0.0; // 防止除以零错误
    pressure = 1048576.0 - adc_P;
    pressure = (pressure - var2 / 4096.0) * 6250.0 / var1;
    var1 = P9 * pressure * pressure / 2147483648.0;
    var2 = pressure * P8 / 32768.0;
    pressure = pressure + (var1 + var2 + P7) / 16.0;

    return (float)(pressure);
}

void app_main(void)
{

    i2c_master_bus_config_t i2c_mst_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .i2c_port = I2C_MASTER_NUM,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };

    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = BMP280_ADDR_LEN,
        .device_address = BMP280_ADDR,
        .scl_speed_hz = 400000,
    };

    i2c_master_dev_handle_t dev_handle;
    ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));

    bmp280_calib calib;
    // 配置BMP280(修改为传统I2C写法)
    BMP280_init(&dev_handle, &calib); // 初始化BMP280,并读取校准参数
    // ESP_ERROR_CHECK(Bmp280_read_calibration_data(dev_handle,&calib));

    while (1)
    {
        // 读取传感器数据(修改为传统I2C写法)
        uint8_t raw_data[6];
        BMP280_read_rawdata(&dev_handle, raw_data); // 读取原始数据

        // 添加数据解析代码
        int32_t temp_raw = (raw_data[3] << 12) | (raw_data[4] << 4) | (raw_data[5] >> 4);
        int32_t press_raw = (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4);
        // 打印 temp_raw press_raw
        ESP_LOGI("BMP280", "temp_raw: %ld, press_raw: %ld", temp_raw, press_raw);
        // 新增数据有效性检查
        if (temp_raw == 0x80000 || press_raw == 0x80000)
        { // 0x80000表示无效数据
            ESP_LOGE("BMP280", "无效传感器数据");
            continue;
        }
        // 校准数据
        float t_fine; // 保存温度校准后的中间结果
        float temp = compensate_temp(temp_raw, &calib, &t_fine);
        float pressure = compensate_press(press_raw, t_fine, &calib);
        printf("校准后数据: %.2f °C, %.2f hPa\n", temp, pressure / 100.0);

        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

六.开发说明:

本例中的python程序需要用到 smbus2 模块, 这个模块需要事先进行安装

  1. 更新包列表:sudo apt-get update
  2. 安装 python3-pip:sudo apt-get install python3-pip
  3. 使用 pip3 安装 smbus2:sudo pip3 install smbus2