1. 概述
1.1 实验目的
本实验基于 STM32CubeMX 与 HAL 库,利用硬件 I²C 接口实现对 BMP280 高精度气压与温度传感器的测量与数据处理。实验内容涵盖 BMP280 的初始化配置、寄存器读写机制、气压与温度数据的获取及物理量计算等关键环节。通过对实验驱动代码与测试结果的完整展示,读者不仅能够深入理解 STM32 硬件 I²C 总线的应用方法,还能掌握数字气压传感器的通信特性与补偿算法,实现由气压推算高度的应用过程。该实验为智能硬件系统中的 环境监测、气象观测、无人机高度控制 与 物联网终端设备 开发提供实践参考与技术支撑。
1.2 气压传感器

此芯片是温湿度传感器和气压传感器二合一的
传感器型号 | 压力范围 (hPa) | 压力精度 / 误差 | 压力分辨率 | 高度分辨率 | 接口方式 | 应用场景 |
---|---|---|---|---|---|---|
BMP180 | 300 ~ 1100 | ±1.0 hPa | 0.01 hPa | ~8 m | I²C | 手机、导航、气象 |
BMP280 | 300 ~ 1100 | ±0.12 hPa | 0.01 hPa | ~1 m | I²C / SPI | 可穿戴设备、环境监测 |
BME280 | 300 ~ 1100 | ±0.12 hPa | 0.01 hPa | ~1 m | I²C / SPI | 气象站、IoT |
LPS22HB | 260 ~ 1260 | ±0.1 hPa | 0.01 hPa | ~0.8 m | I²C / SPI | 手机、IoT |
LPS33HW | 260 ~ 1260 | ±0.1 hPa | 0.01 hPa | ~0.8 m | I²C / SPI | 潜水表、防水应用 |
BMP388 | 300 ~ 1250 | ±0.08 hPa | 0.0023 hPa | ~0.66 m | I²C / SPI | 无人机、运动追踪 |
DPS310 | 300 ~ 1200 | ±0.06 hPa | 0.002 hPa | ~0.5 m | I²C / SPI | 无人机、GPS 辅助 |
BMP390 | 300 ~ 1250 | ±0.03 hPa | 0.002 hPa | ~0.25 m | I²C / SPI | 高精度定位、智能穿戴 |
MS5611 | 10 ~ 1200 | ±0.1 hPa | 0.012 hPa | ~10 cm | I²C / SPI | 航模、气象、无人机 |
1.3 读取原理
BMP280 内部 ADC 会输出未经补偿的原始温度值 adc_T
和原始压力值 adc_P
。为了获得准确的测量结果,需要结合芯片出厂时写入的校准系数进行补偿:首先利用温度校准系数 dig_T1 ~ dig_T3
对原始温度值进行补偿,得到准确温度及中间变量 t_fine
;随后再结合 t_fine
与压力校准系数 dig_P1 ~ dig_P9
对原始压力值进行补偿,计算出精确的气压值。这些校准系数均需通过 I²C 接口从芯片内部寄存器中读取。
在完成温度和压力的补偿计算后,即可得到精确的大气压值。基于此压力,再结合国际标准大气模型所推导的气压---高度公式,便能够换算出对应的海拔高度,从而实现由传感器原始数据到实际环境物理量的完整转换过程。
注:BMP280的IIC固定7位地址是0x77
2. VSCode
2.1 BMP280.c
cpp
/* =========================================================
* BMP280.c
* Driver for BMP280 Pressure & Temperature Sensor
* ========================================================= */
#include "BMP280.h"
#include "stdio.h"
#include <math.h>
/* 静态变量 */
static int32_t bmp280RawPressure = 0;
static int32_t bmp280RawTemperature = 0;
static BMP280_Calib_TypeDef bmp280Cal;
/* === HAL I2C 封装函数 === */
static HAL_StatusTypeDef BMP280_Read(uint8_t reg, uint8_t *buf, uint16_t len)
{
return HAL_I2C_Mem_Read(&hi2c1, BMP280_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100);
}
static HAL_StatusTypeDef BMP280_Write(uint8_t reg, uint8_t value)
{
return HAL_I2C_Mem_Write(&hi2c1, BMP280_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
}
/* === 校准数据读取 === */
static void BMP280_ReadCalibData(void)
{
uint8_t buf[24];
BMP280_Read(BMP280_DIG_T1_LSB_REG, buf, 24);
bmp280Cal.dig_T1 = (uint16_t)(buf[1] << 8 | buf[0]);
bmp280Cal.dig_T2 = (int16_t)(buf[3] << 8 | buf[2]);
bmp280Cal.dig_T3 = (int16_t)(buf[5] << 8 | buf[4]);
bmp280Cal.dig_P1 = (uint16_t)(buf[7] << 8 | buf[6]);
bmp280Cal.dig_P2 = (int16_t)(buf[9] << 8 | buf[8]);
bmp280Cal.dig_P3 = (int16_t)(buf[11] << 8 | buf[10]);
bmp280Cal.dig_P4 = (int16_t)(buf[13] << 8 | buf[12]);
bmp280Cal.dig_P5 = (int16_t)(buf[15] << 8 | buf[14]);
bmp280Cal.dig_P6 = (int16_t)(buf[17] << 8 | buf[16]);
bmp280Cal.dig_P7 = (int16_t)(buf[19] << 8 | buf[18]);
bmp280Cal.dig_P8 = (int16_t)(buf[21] << 8 | buf[20]);
bmp280Cal.dig_P9 = (int16_t)(buf[23] << 8 | buf[22]);
}
/* === 初始化 === */
uint8_t BMP280_Init(void)
{
uint8_t id;
/* 读 ID */
BMP280_Read(BMP280_CHIPID_REG, &id, 1);
if (id != 0x58) return 0; // BMP280 ID 固定为 0x58
/* 读校准参数 */
BMP280_ReadCalibData();
/* 设置采样 & 正常模式 */
uint8_t ctrl = (BMP280_OVERSAMP_16X << 5) | (BMP280_OVERSAMP_8X << 2) | BMP280_NORMAL_MODE;
BMP280_Write(BMP280_CTRLMEAS_REG, ctrl);
/* 配置滤波 (IIR=16, standby=0.5ms) */
uint8_t cfg = (0x04 << 2);
BMP280_Write(BMP280_CONFIG_REG, cfg);
return id;
}
/* === 读取原始数据 === */
static void BMP280_ReadRaw(void)
{
uint8_t data[6];
BMP280_Read(BMP280_PRESSURE_MSB_REG, data, 6);
bmp280RawPressure = (int32_t)((((uint32_t)data[0]) << 12) |
(((uint32_t)data[1]) << 4) |
((uint32_t)data[2] >> 4));
bmp280RawTemperature = (int32_t)((((uint32_t)data[3]) << 12) |
(((uint32_t)data[4]) << 4) |
((uint32_t)data[5] >> 4));
}
/* === 温度补偿 === */
static int32_t BMP280_CompensateT(int32_t adcT)
{
int32_t var1, var2, T;
var1 = ((((adcT >> 3) - ((int32_t)bmp280Cal.dig_T1 << 1))) * ((int32_t)bmp280Cal.dig_T2)) >> 11;
var2 = (((((adcT >> 4) - ((int32_t)bmp280Cal.dig_T1)) *
((adcT >> 4) - ((int32_t)bmp280Cal.dig_T1))) >> 12) *
((int32_t)bmp280Cal.dig_T3)) >> 14;
bmp280Cal.t_fine = var1 + var2;
T = (bmp280Cal.t_fine * 5 + 128) >> 8;
return T; // ℃*100
}
/* === 压力补偿 === */
static uint32_t BMP280_CompensateP(int32_t adcP)
{
int64_t var1, var2, p;
var1 = ((int64_t)bmp280Cal.t_fine) - 128000;
var2 = var1 * var1 * (int64_t)bmp280Cal.dig_P6;
var2 = var2 + ((var1 * (int64_t)bmp280Cal.dig_P5) << 17);
var2 = var2 + (((int64_t)bmp280Cal.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)bmp280Cal.dig_P3) >> 8) +
((var1 * (int64_t)bmp280Cal.dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)bmp280Cal.dig_P1) >> 33;
if (var1 == 0) return 0;
p = 1048576 - adcP;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)bmp280Cal.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)bmp280Cal.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)bmp280Cal.dig_P7) << 4);
return (uint32_t)p; // Pa
}
/* === 压力转海拔 === */
static float BMP280_PressureToAltitude(float pressure)
{
return 44330.0f * (1.0f - powf(pressure / 101325.0f, 0.1903f));
}
/* === 对外接口 === */
void BMP280GetData(float *pressure, float *temperature, float *asl)
{
BMP280_ReadRaw();
*temperature = BMP280_CompensateT(bmp280RawTemperature) / 100.0f;
*pressure = BMP280_CompensateP(bmp280RawPressure) / 256000.0f; // kPa
*asl = BMP280_PressureToAltitude(*pressure * 1000.0f);
}
2.2 BMP280.h
cpp
#ifndef __BMP280_H
#define __BMP280_H
#include "i2c.h"
/* 外部 I2C 句柄 */
extern I2C_HandleTypeDef hi2c1;
/* === I2C 地址 === */
#define BMP280_I2C_ADDR (0x77 << 1) // 7-bit 地址,左移一位给 HAL 用
/* === 校准寄存器起始地址 === */
#define BMP280_DIG_T1_LSB_REG 0x88 // 从这里开始顺序读 24 字节
/* === 芯片 ID === */
#define BMP280_CHIPID_REG 0xD0
/* === 控制与配置寄存器 === */
#define BMP280_CTRLMEAS_REG 0xF4
#define BMP280_CONFIG_REG 0xF5
/* === 数据寄存器 === */
#define BMP280_PRESSURE_MSB_REG 0xF7 // 连续读 6 字节: P(3) + T(3)
/* === 电源模式 === */
#define BMP280_SLEEP_MODE (0x00)
#define BMP280_FORCED_MODE (0x01)
#define BMP280_NORMAL_MODE (0x03)
/* === 过采样设置 === */
#define BMP280_OVERSAMP_1X (0x01)
#define BMP280_OVERSAMP_2X (0x02)
#define BMP280_OVERSAMP_4X (0x03)
#define BMP280_OVERSAMP_8X (0x04)
#define BMP280_OVERSAMP_16X (0x05)
/* === 校准参数结构体 === */
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;
int32_t t_fine;
} BMP280_Calib_TypeDef;
/* === 外部接口 === */
uint8_t BMP280_Init(void);
void BMP280GetData(float* pressure, float* temperature, float* asl);
#endif /* __BMP280_H */
2.3 main
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "BMP280.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
float pressure, temperature, altitude;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
if (BMP280_Init() == 0) {
printf("BMP280 Init Failed!\r\n");
while (1);
}
printf("BMP280 Init OK!\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
BMP280GetData(&pressure, &temperature, &altitude);
printf("Temp: %.2f C, Pressure: %.2f KPa, Altitude: %.2f m\r\n",
temperature, pressure, altitude);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
3 实验结果

通过串口打印出通过BMP280测量到的温度、压强、海拔高度如上所述,其温度偏高,下图位使用ATH20温湿度传感器测量的值与之对比,结果如下
