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

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温湿度传感器测量的值与之对比,结果如下

相关推荐
小莞尔5 小时前
【51单片机】【protues仿真】基于51单片机PM2.5空气质量检测系统
stm32·单片机·嵌入式硬件·51单片机·proteus
lingran__6 小时前
C语言制作扫雷游戏(拓展版赋源码)
c语言·算法·游戏
zgc12453676 小时前
51单片机基础day3
单片机·嵌入式硬件·51单片机
竹照煜_ysn7 小时前
STM32——WDG看门狗
stm32·单片机·嵌入式硬件
DS小龙哥7 小时前
基于华为云的STM32F103C8T6智能停车场管理系统
大数据·stm32·华为云
77qqqiqi7 小时前
学习结构体
c语言·学习
学习噢学个屁7 小时前
基于STM32智能阳台监控系统
c语言·stm32·单片机·嵌入式硬件
糖糖单片机设计8 小时前
硬件开发_基于物联网的老人跌倒监测报警系统
stm32·单片机·嵌入式硬件·物联网·51单片机
房开民16 小时前
使用海康机器人相机SDK实现基本参数配置(C语言示例)
c语言·数码相机·机器人