BMP280 esp32 Arduino 气压温度海拔高度测量

1.介绍

BMP280是一款基于博世公司APSM工艺的小封装低功耗数字复合传感器,它可以测量环境温度和大气压强。气压敏感元件是一个低噪高精度高分辨率绝对大气压力压电式感应元件;温度感测元件具有低噪高分辨率特性,温度值可以对气压进行温度补偿自校正。可校准精确计算海拔高度。I2C或SPI接口,宽量程(300-1100hpa),精度高。

BMP280

本项目由 esp32 + Arduino 平台设计的传感器驱动模块,实现了气压数据、温度数据的获取,以及基于这两个数据计算出高度海拔数据

传感器参数

传感器电源电压 范围为1.71V到3.6V
温度测量范围 0℃ ~ +65℃ 误差±2%
气压测量范围 300 hPa ~ 1100 hPa 误差±1hPa
海拔高度范围 -500米 ~ 9000米
数字接口 支持I2C(最高3.4MHz)和SPI(3线和4线,最高10MHz)
I2C地址 当SDO连接到GND时为0x76;连接到VDD时为0x77

引脚连接

VCC 电源正极
GND
SCL I2C / SPI串行时钟线
SDA I2C串行数据线
3线SPI串行数据输入/输出端口
4线SPI串行输入端口
CSB 片选引脚,接高电平(默认)位I2C通信接口,低电平位SPI通信接口
SDO I2C地址选择位
4线SPI串行输出端口

模块在I2C模式下通过SDO引脚来确定器件地址,SDO接GND低电平从机地址为1110110(0×76),本模块默认0×76,;接高电平从机地址1110111(0×77)

原理图

BMP280原理图



2.程序设计

在进行程序设计之前,我们先了解一下这个模块的流程图吧!

流程图

流程图

由于BMP280输出由 ADC 输出值组成。然而,每个传感元件的行为不同,因此实际压力和温度必须使用一组校准参数来计算

BMP280.ino

#include "./src/BMP280/BMP280_DEV.h"
#include "./src/OLED/OLED.h"

double temperature, pressure, altitude;

void setup() {// put your setup code here, to run once:
  Serial.begin(115200);  // 初始化串口通信,波特率为115200

  OLED_Init();
  OLED_ColorTurn(0);    //0正常显示 1反色显示
  OLED_DisplayTurn(0);  //0正常显示 1翻转180度显示

  BMP_Init();
}

void loop() {// put your main code here, to run repeatedly:
    //读取传感器数据
    BMP_ReadData(&temperature, &pressure, &altitude);

    Serial.print(F(" *C:"));
    Serial.print(temperature);
    Serial.print(F(" hPa:"));
    Serial.print(pressure);
    Serial.print(F(" m:"));
    Serial.println(altitude);

    //显示屏输出
    OLED_ShowString(0, 0, "*C:", 16);
    OLED_ShowString(0, 2, "hPa:", 16);
    OLED_ShowString(0, 4, "m:", 16);

    // 转换为字符串
    char alt_str[10];
    char temp_str[10];
    char press_str[10];
    dtostrf(altitude, 6, 2, alt_str);  // 总宽度6,小数点后2位
    dtostrf(temperature, 6, 2, temp_str);
    dtostrf(pressure, 6, 2, press_str);
    OLED_ShowString(20, 0, temp_str, 16);
    OLED_ShowString(30, 2, press_str, 16);
    OLED_ShowString(20, 4, alt_str, 16);
}

BMP280_DEV.cpp

#include "BMP280_DEV.h"

#define BME280_ADDRESS 0x76
unsigned long int hum_raw, temp_raw, pres_raw; // 湿度、温度、压强数据
signed long int t_fine;						   // 存储中间精细温度值

// 补偿参数
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;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;

// BMP280初始化
void BMP_Init()
{
	uint8_t osrs_t = 1;	  // Temperature oversampling x 1  温度
	uint8_t osrs_p = 1;	  // Pressure oversampling x 1     压强
	uint8_t osrs_h = 1;	  // Humidity oversampling x 1     湿度
	uint8_t mode = 3;	  // Normal mode                     普通模式
	uint8_t t_sb = 5;	  // Tstandby 1000ms                 测量速率
	uint8_t filter = 0;	  // Filter off                    IIR 过滤器
	uint8_t spi3w_en = 0; // 3-wire SPI Disable            使用I²C读取

	uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
	uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
	uint8_t ctrl_hum_reg = osrs_h;

	Wire.begin();

	writeReg(BMP280_CTRL_HUMIDITY_REG, ctrl_hum_reg);  // 湿度寄存器,BMP280没有这个寄存器
	writeReg(BMP280_CTRLMEAS_REG, ctrl_meas_reg); // 控制寄存器,关于温度和压强的控制寄存器
	writeReg(BMP280_CONFIG_REG, config_reg);	   // 控制寄存器,配置测量速率
	readTrim();					   //读取初始化补偿参数
}

/*
   返回气温、压强、高度
*/
void BMP_ReadData(double *temp_act, double *press_act, double *hum_act)
{

	signed long int temp_cal;
	unsigned long int press_cal, hum_cal;

	readData();

	// 有湿度功能打开这个注释,注释下面的;
	//  temp_cal = calibration_T(temp_raw);
	//  press_cal = calibration_P(pres_raw);
	//  hum_cal = calibration_H(hum_raw);
	//  *temp_act = (double)temp_cal / 100.0;
	//  *press_act = (double)press_cal / 100.0;
	//  *hum_act = (double)hum_cal / 1024.0;

	temp_cal = calibration_T(temp_raw);
	press_cal = calibration_P(pres_raw);
	*temp_act = (double)temp_cal / 100.0;
	*press_act = (double)press_cal / 100.0;
	*hum_act = BMP_GetAltitude(*temp_act, *press_act); // 这里其实返回的是高度的数据

	delay(1000);
}

// 获取高度数据
double BMP_GetAltitude(double temperature, double pressure)
{
	const float SEA_LEVEL_PRESSURE = 1013.23f;
	return ((double)powf(SEA_LEVEL_PRESSURE / pressure, 0.190223f) - 1.0f) * (temperature + 273.15f) / 0.0065f; // Calculate the altitude in metres
}

// 初始化补偿参数读取,calibration_T、calibration_P等函数依赖于目前函数读取的dig_T1、dig_P1等参数
void readTrim()
{
	uint8_t data[32], i = 0;
	// 指定地址读,读取温度和压强的原始数据
	Wire.beginTransmission(BME280_ADDRESS);
	Wire.write(BMP280_TRIMMING_PARAM_START);
	Wire.endTransmission();
	Wire.requestFrom(BME280_ADDRESS, 24); // 24个字节
	while (Wire.available())
	{
		data[i] = Wire.read();
		i++;
	}

	// 指定地址读,这一位是保留位
	Wire.beginTransmission(BME280_ADDRESS);
	Wire.write(BMP280_TRIMMING_PARAM_END);
	Wire.endTransmission();
	Wire.requestFrom(BME280_ADDRESS, 1); // 1个字节
	data[i] = Wire.read();
	i++;

	// 指定地址读,这里是湿度原始数据的读取,BMP280没有
	Wire.beginTransmission(BME280_ADDRESS);
	Wire.write(BMP280_TRIMMING_PARAM_HUM_START);
	Wire.endTransmission();
	Wire.requestFrom(BME280_ADDRESS, 7); // 7个字节
	while (Wire.available())
	{
		data[i] = Wire.read();
		i++;
	}

	// 移位操作
	dig_T1 = (data[1] << 8) | data[0];
	dig_T2 = (data[3] << 8) | data[2];
	dig_T3 = (data[5] << 8) | data[4];
	dig_P1 = (data[7] << 8) | data[6];
	dig_P2 = (data[9] << 8) | data[8];
	dig_P3 = (data[11] << 8) | data[10];
	dig_P4 = (data[13] << 8) | data[12];
	dig_P5 = (data[15] << 8) | data[14];
	dig_P6 = (data[17] << 8) | data[16];
	dig_P7 = (data[19] << 8) | data[18];
	dig_P8 = (data[21] << 8) | data[20];
	dig_P9 = (data[23] << 8) | data[22];
	dig_H1 = data[24];
	dig_H2 = (data[26] << 8) | data[25];
	dig_H3 = data[27];
	dig_H4 = (data[28] << 4) | (0x0F & data[29]);
	dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F);
	dig_H6 = data[31];
}

// 指定地址写,写入指定地址的寄存器
void writeReg(uint8_t reg_address, uint8_t data)
{
	Wire.beginTransmission(BME280_ADDRESS);
	Wire.write(reg_address);
	Wire.write(data);
	Wire.endTransmission();
}

// 读取数据
void readData()
{
	int i = 0;
	uint32_t data[8];
	// 指定地址读,读取8个字节数据,MSB、LSB、XLSB寄存器
	// 气压数据:3个字节组合成20位数值
	// 温度数据:3个字节组合成20位数值
	// 湿度数据:2个字节组合成16位数值
	Wire.beginTransmission(BME280_ADDRESS);
	Wire.write(BMP280_PRESSURE_MSB_REG);
	Wire.endTransmission();
	Wire.requestFrom(BME280_ADDRESS, 8);
	while (Wire.available())
	{
		data[i] = Wire.read();
		i++;
	}
	pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
	temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
	hum_raw = (data[6] << 8) | data[7];
}

// BMP280传感器的温度校准函数官方提供的直接使用就行,就是根据补偿参数和温度数据的计算的来
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
signed long int calibration_T(signed long int adc_T)
{

	signed long int var1, var2, T;
	var1 = ((((adc_T >> 3) - ((signed long int)dig_T1 << 1))) * ((signed long int)dig_T2)) >> 11;
	var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T >> 4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;

	t_fine = var1 + var2;
	T = (t_fine * 5 + 128) >> 8;
	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
unsigned long int calibration_P(signed long int adc_P)
{
	signed long int var1, var2;
	unsigned long int P;
	var1 = (((signed long int)t_fine) >> 1) - (signed long int)64000;
	var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((signed long int)dig_P6);
	var2 = var2 + ((var1 * ((signed long int)dig_P5)) << 1);
	var2 = (var2 >> 2) + (((signed long int)dig_P4) << 16);
	var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((signed long int)dig_P2) * var1) >> 1)) >> 18;
	var1 = ((((32768 + var1)) * ((signed long int)dig_P1)) >> 15);
	if (var1 == 0)
	{
		return 0;
	}
	P = (((unsigned long int)(((signed long int)1048576) - adc_P) - (var2 >> 12))) * 3125;
	if (P < 0x80000000)
	{
		P = (P << 1) / ((unsigned long int)var1);
	}
	else
	{
		P = (P / (unsigned long int)var1) * 2;
	}
	var1 = (((signed long int)dig_P9) * ((signed long int)(((P >> 3) * (P >> 3)) >> 13))) >> 12;
	var2 = (((signed long int)(P >> 2)) * ((signed long int)dig_P8)) >> 13;
	P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
	return P;
}

// 同上,这里是湿度校准函数,BMP280没有所以我就没调用这个函数
unsigned long int calibration_H(signed long int adc_H)
{
	signed long int v_x1;

	v_x1 = (t_fine - ((signed long int)76800));
	v_x1 = (((((adc_H << 14) - (((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int)32768))) >> 10) + ((signed long int)2097152)) * ((signed long int)dig_H2) + 8192) >> 14));
	v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
	v_x1 = (v_x1 < 0 ? 0 : v_x1);
	v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
	return (unsigned long int)(v_x1 >> 12);
}

BMP280_DEV.h

#include <Wire.h>
#include <Arduino.h>


#define BMP280_ADDRESS						0x76		//从设备地址
#define BMP280_RESET_VALUE					0xB6		//复位寄存器写入值

#define BMP280_CHIPID_REG                    0xD0  /*Chip ID Register */
#define BMP280_RESET_REG                     0xE0  /*Softreset Register */
#define BMP280_CTRL_HUMIDITY_REG             0xF2  /*Ctrl Humidity Register */
#define BMP280_STATUS_REG                    0xF3  /*Status Register */
#define BMP280_CTRLMEAS_REG                  0xF4  /*Ctrl Measure Register */
#define BMP280_CONFIG_REG                    0xF5  /*Configuration Register */
#define BMP280_PRESSURE_MSB_REG              0xF7  /*Pressure MSB Register */
#define BMP280_PRESSURE_LSB_REG              0xF8  /*Pressure LSB Register */
#define BMP280_PRESSURE_XLSB_REG             0xF9  /*Pressure XLSB Register */
#define BMP280_TEMPERATURE_MSB_REG           0xFA  /*Temperature MSB Reg */
#define BMP280_TEMPERATURE_LSB_REG           0xFB  /*Temperature LSB Reg */
#define BMP280_TEMPERATURE_XLSB_REG          0xFC  /*Temperature XLSB Reg */
//状态寄存器转换标志
#define	BMP280_MEASURING					0x01
#define	BMP280_IM_UPDATE					0x08

//补偿参数
#define BMP280_TRIMMING_PARAM_START		      0x88 //压强、温度校准参数起始地址
#define BMP280_TRIMMING_PARAM_END			   0xA1 //压强、温度校准参数结束地址
#define BMP280_TRIMMING_PARAM_HUM_START		0xE1 //湿度校准参数起始地址


void BMP_Init();
void BMP_ReadData(double *temp_act, double *press_act, double *hum_act);
double BMP_GetAltitude(double temperature, double pressure);

void readTrim();
void writeReg(uint8_t reg_address, uint8_t data);
void readData();
signed long int calibration_T(signed long int adc_T);
unsigned long int calibration_P(signed long int adc_P);
unsigned long int calibration_H(signed long int adc_H);



3.官方文档重点

这一边是对官方文档的介绍,我挑了几个重点出来


三种模式

BMP280工作的三种模式

支持三种主要的工作模式:睡眠模式(Sleep Mode)、强制模式(Forced Mode)和正常模式(Normal Mode)。

  1. 睡眠模式(Sleep Mode)

    • 定义:睡眠模式是BMP280的默认模式,设备在该模式下不进行任何测量,功耗最低。
    • 功耗:在睡眠模式下,传感器的功耗非常低,典型值为0.1 μA(25°C时)。
    • 用途:适用于需要极低功耗的应用场景,例如在设备长时间不使用时,可以将传感器置于睡眠模式以节省电量。
    • 操作:在睡眠模式下,所有寄存器都可以访问,但不会进行任何测量。如果需要进行测量,必须切换到其他模式。
  2. 强制模式(Forced Mode)

    • 定义:在强制模式下,传感器会进行一次测量,测量完成后自动返回到睡眠模式。
    • 功耗:强制模式下的功耗取决于测量的类型(压力或温度)和采样率。例如,在1Hz采样率下,功耗为2.7 μA。
    • 用途:适用于需要低采样率或由主机控制测量的应用场景。例如,某些应用可能只需要每隔几分钟进行一次测量,强制模式可以有效节省电量。
    • 操作:在强制模式下,每次测量完成后,设备会自动返回到睡眠模式。如果需要进行下一次测量,需要再次将设备切换到强制模式。
  3. 正常模式(Normal Mode)

    • 定义:正常模式下,传感器会自动在测量周期和待机周期之间循环。测量周期进行压力和温度测量,待机周期则处于低功耗状态。
    • 功耗:正常模式下的功耗取决于采样率和测量设置。例如,在1Hz采样率下,功耗为2.8 μA。
    • 用途:适用于需要连续测量的应用场景,例如在需要实时监测压力和温度变化的设备中。正常模式可以有效减少主机处理器的负担,因为测量是自动进行的。
    • 操作:在正常模式下,设备会根据设置的采样率自动进行测量。测量结果存储在数据寄存器中,主机可以随时读取这些数据。
模式切换
  • 切换方式:通过写入控制寄存器(0xF4)的mode[1:0]位来切换模式。
    • 00:睡眠模式
    • 0110:强制模式
    • 11:正常模式
  • 注意事项:如果设备当前正在进行测量,模式切换命令将延迟到当前测量周期结束时执行。

BMP_Init()初始化的 mode 变量中我设定的是 正常模式


内存映射

这个表就相当重要了!

内存映射

  1. 初始化配置:config寄存器、ctrl_meas寄存器,用于初始化配置
  2. 原始数据:msb、xlsb、lsb temp高中低数据寄存器、press高中低数据寄存器
  3. 补偿参数:calib25...calib00寄存器, 从 0xA1~ 0x88 只读,里面放着就是气温、校准的初始化补偿参数

reset寄存器:重启功能,如果设置为 0xB6,那么BMP280会重启

id寄存器:芯片标识号,其值为0x58。


status状态寄存器

status寄存器

地址为0xF3

  • 位3:measuring(测量状态位)
    • 1:正在进行转换(温度或气压)。
    • 0:测量完成。在读取数据之前,必须检查此位是否为0,以确保数据是新的且稳定的。
  • 位0:im_update(NVM重写状态位)
    • 1:非易失性内存(NVM)数据(通常是校准参数)正在被复制到寄存器中。
    • 0:复制完成。在读取校准系数之前,应确保此位为0。

ctrl_meas、config配置寄存器
  1. ctrl_meas寄存器(地址0xF4)

    ctrl_meas寄存器

    • 位[7:5]:osrs_t[2:0](温度过采样设置)

      • 000:跳过温度测量。
      • 001:1x过采样率。
      • 010:2x过采样率。
      • 011:4x过采样率。
      • 100:8x过采样率。
      • 101110111:16x过采样。
    • 位[4:2]:osrs_p[2:0](气压过采样设置)

      • 设置方式与osrs_t相同(000=跳过,001=1x,…,111=16x)。
    • 位[1:0]:mode[1:0](电源/工作模式)

      • 00:睡眠模式(低功耗,不进行测量)。
      • 0110:强制模式(单次测量模式。写入该模式后,传感器进行一次温压测量,完成后自动回到睡眠模式)。
      • 11:正常模式(连续测量模式。按config寄存器中的t_sb设定的时间间隔连续进行测量)。
  2. config寄存器(地址0xF5)

    config寄存器

    • 位[7:5]:t_sb[2:0](待机时间 - 仅用于正常模式)
      • 000:0.5ms。
      • 001:62.5ms。
      • 010:125ms。
      • 011:250ms。
      • 100:500ms。
      • 101:1000ms。
      • 110:2000ms。
      • 111:4000ms。
      • 设置在正常模式下,连续测量之间的待机时间间隔。
    • 位[4:2]:filter[2:0](IIR滤波器设置)
      • 000:滤波器关闭(无滤波)。
      • 001:系数=2。
      • 010:系数=4。
      • 011:系数=8。
      • 100101110111:系数=16。
      • 设置内置IIR滤波器的系数,用于平滑气压输出,尤其对无人机高度测量等应用有用。
    • 位0:spi3w_en(SPI 3线使能)
      • 0:I²C接口或4线SPI接口(标准)。
      • 1:3线SPI接口使能(仅当使用SPI接口时相关)。

press寄存器(地址0xF7~0xF9)

这个寄存器就是readData中读取的原始数据了与补偿参数一起计算最后得出当前大气压强

press寄存器

  • MSB寄存器的值与LSB寄存器的值和XLSB寄存器的值一起组成20位(未补偿的)原始气压数据的对应电压。其中气压数据MSB寄存器的值是最高8位。

temp寄存器(地址0xFA~0xFC)

由于temp内存地址是和press内存地址是连续的所以读取的时候直接从press寄存器地址开始,连续读取5个字节就是这两个的数据了!

在这里插入图片描述

  • 存储了20位的温度测量转换后的数据。

基于使用场景的推荐过滤器设置

在这里插入图片描述

官方有6种推荐的使用场景过滤器配置:手持设备低功耗模式、手持设备动态模式、天气监测(最低功耗)、电梯/楼层变化检测、下降检测、室内导航。我使用的是第三个,你可以根据对应的场景配置相应的过滤器配置


osrs_p、osrs_t、filter寄存器配置

在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐