STM32CubeMX + HAL 库:基于 I²C 通信的 BMP280气压海拔测量
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
/* =========================================================* 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
#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
/* 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温湿度传感器测量的值与之对比,结果如下