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。
四.编程步骤:
- 确定 BMP280 的 I2C 地址 0x77。
- 初始化 I2C 总线,连接到传感器。
- 读取校准参数。
- 配置传感器的工作模式和采样率。
- 触发测量,等待数据就绪。
- 读取原始温度和压力数据。
- 使用校准参数进行补偿计算。
- 输出结果,处理单位转换。
- 添加循环和异常处理。
五.代码如下:
这是使用 OrangePi zero 3 的 I2C 接口连接的 BMP280 传感器的 Python 代码示例。代码中包括了初始化传感器、读取校准参数、配置传感器、读取温度和压力数据、进行补偿计算以及输出结果等步骤。
1. 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 的数据:
#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开发板。
#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, ®, 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, ®, 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 模块, 这个模块需要事先进行安装
- 更新包列表:
sudo apt-get update
- 安装 python3-pip:
sudo apt-get install python3-pip
- 使用 pip3 安装 smbus2:
sudo pip3 install smbus2