STM32 HAL库 BMP280气压计读取

BMP280 是一款由博世(Bosch)推出的高精度气压和温度传感器模块,常用于气象监测、高度计、无人机等应用。

  • 工作电压 :模块通常支持 3.3V--5.5V 供电(部分资料指出芯片本身为 1.71V--3.6V,但模块已集成电平转换电路,可兼容 5V)。

  • 通信接口 :支持 I²C(默认地址 0x76)SPI,其中 I²C 接口在多数模块中为 5V 兼容 。

  • 气压测量范围300 hPa 至 1100 hPa(相当于海拔约 -500 米至 +9000 米)。

  • 气压精度

    • 绝对精度:±1 hPa(对应高度误差约 ±8.33 米);

    • 相对精度:±0.12 hPa(可用于检测约 ±1 米的高度变化)。

  • 温度测量范围-40°C 至 +85°C ,精度为 ±0.5°C

  • 工作电流 :典型值约为 0.6 mA(650 µA),具体取决于采样率和工作模式 。

  • 封装与尺寸:采用紧凑型 8 引脚贴片封装,适合空间受限的应用 。

BMP280.h

复制代码
#ifndef BMP280_H
#define BMP280_H


#include "i2c.h"
#include <stdint.h>

#define BMP280_I2C &hi2c1

HAL_StatusTypeDef BMP280_Init();
HAL_StatusTypeDef BMP280_Read(int32_t* temp, int32_t* press);


#endif //BMP280_H

BMP280.c

复制代码
#include "bmp280.h"

#include <stdio.h>

#include "i2c.h"


/**
 * I2C通信地址
 * SDO接地时:I2C地址是:0b1110110,即0x76
 * SDO接VDD时:I2C地址是:0b1110111,即0x77
 * 以接地时为例,完整的地址位为8位:
 * 当主机向从机发送数据的目的是设置(写)从机,最后一位补充0,即0b11101100
 * 当主机向从机发送数据的目的是从从机读取数据,最后一位补充1,即0b11101101
*/
#define BMP280_I2C_ADDRESS 0x76
//id寄存器地址
#define ID_ADDRESS 0xD0
//配置寄存器地址
#define CONFIG_ADDRESS 0xF5
//控制寄存器地址
#define CTRL_ADDRESS 0xF4
//重置寄存器地址
#define RESET_ADDRESS 0xE0
//校准寄存器的起始地址(part_1)
#define CALIBRATION_ADDRESS 0x88
//存储原始读取到温湿度信息的寄存器首地址
#define DATA_ADDRESS 0xF7

//计算补偿使用的变量
static int32_t t_fine;
// 温度和压力补偿值,从补偿寄存器中读取,启动时读取即可
static uint16_t dig_T1;
static int16_t dig_T2;
static int16_t dig_T3;
static uint16_t dig_P1;
static int16_t dig_P2;
static int16_t dig_P3;
static int16_t dig_P4;
static int16_t dig_P5;
static int16_t dig_P6;
static int16_t dig_P7;
static int16_t dig_P8;
static int16_t dig_P9;


//校准温度方法(文档内置)
static int32_t calibration_T(int32_t adc_T);
//校准气压方法(文档内置)
static uint32_t calibration_P(int32_t adc_P);
//获取校准(补偿)值
static HAL_StatusTypeDef readCalibrationData();
//工具方法写入指令
static HAL_StatusTypeDef write_bmp280(uint8_t address, uint8_t cmd);


HAL_StatusTypeDef BMP280_Init()
{
    //先delay一下,确保设备上电了
    HAL_Delay(100);
    //首先检验设备是否正确;
    uint8_t chip_id = 0;
    const HAL_StatusTypeDef r0 = HAL_I2C_Mem_Read(BMP280_I2C, BMP280_I2C_ADDRESS << 1 | 1, ID_ADDRESS,
                                                   I2C_MEMADD_SIZE_8BIT, &chip_id, 1,HAL_MAX_DELAY);
    if (r0 != HAL_OK)
    {
        // 通信失败,返回错误
        return r0;
    }
    if (chip_id != 0x58)
    {
        //设备id错误
        return HAL_ERROR;
    }


    //重置设备设定
    const uint8_t reset_cmd = 0xB6;
    const HAL_StatusTypeDef r1 = write_bmp280(RESET_ADDRESS, reset_cmd);
    if (r1 != HAL_OK)
    {
        return r1;
    }
    // 软复位后需等待 2ms 设备完成初始化,这里等10ms
    HAL_Delay(10);

    // 配置寄存器(0xF5)用于设置设备的速率、滤波器和接口选项, Bits [7:5]:t_sb(休眠时间,BMP只能是110/111)Bits [4:2]:filter(滤波器系数)Bits [0]:spi(兼容SPI模式)
    //休眠200ms,不过滤
    const uint8_t config_cmd = 0xC0;
    const HAL_StatusTypeDef r2 = write_bmp280(CONFIG_ADDRESS, config_cmd);
    if (r2 != HAL_OK)
    {
        return r2;
    }

    //设置ctrl_means寄存器,即控制方法寄存器(0xF4),    Bits [7:5]:osrs_t(温度过采样)Bits [4:2]:osrs_p(气压过采样)Bits [1:0]:mode(工作模式)
    //1倍压力,1倍温度过采样,正常模式;(由于未开启过滤器,此时压力和温度传感器的分辨率是18位)
    const uint8_t ctrl_means_cmd = 0x27;
    const HAL_StatusTypeDef r3 = write_bmp280(CTRL_ADDRESS, ctrl_means_cmd);
    if (r3 != HAL_OK)
    {
        return r3;
    }
    //ctrl_hum 无需设置,因为bmp280不能测定湿度;

    //读取校准值,用于校准读取数据0x88~0xA1
    const HAL_StatusTypeDef r4 = readCalibrationData();
    if (r4 != HAL_OK)
    {
        return r4;
    }

    return HAL_OK;
}


/**
 * 向传感器发送请求,读取温度/气压信息
 * @param temp 温度,除以100为摄氏度
 * @param press 气压,除以100为Pa
 */
HAL_StatusTypeDef BMP280_Read(int32_t* temp, int32_t* press)
{
    uint8_t data[8] = {0};
    const HAL_StatusTypeDef ret = HAL_I2C_Mem_Read(BMP280_I2C, BMP280_I2C_ADDRESS << 1 | 1, DATA_ADDRESS,
                                                   I2C_MEMADD_SIZE_8BIT, data, 8, 1000);

    if (ret != HAL_OK)
    {
        return ret;
    }
    // rx_buffer,读取buffer值看看把,返回数据0-7,共8字节,即0xF7~0xFE;0-2压力,3-5温度,6-7湿度(bmp不存在)
    uint32_t pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
    uint32_t temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
    //校准数据(注意实际要除以100使用)
    *temp = calibration_T(temp_raw);
    *press = calibration_P(pres_raw);
    return HAL_OK;
}


/**
 * 向bmp280写入某个寄存器单字节命令,进行配置或指示行为
 * @param address 寄存器地址
 * @param cmd 单字节数据
 */
HAL_StatusTypeDef write_bmp280(const uint8_t address, uint8_t cmd)
{
    const HAL_StatusTypeDef ret = HAL_I2C_Mem_Write(BMP280_I2C, BMP280_I2C_ADDRESS << 1, address,
                                                    I2C_MEMADD_SIZE_8BIT, &cmd, 1, 1000);
    if (ret != HAL_OK)
    {
        return ret;
    }
    return HAL_OK;
}


HAL_StatusTypeDef readCalibrationData()
{
    //补偿(校准)值一共有33个字节存储,需要读取,对于温度和湿度实际读取到0x88~0x9F即可,即24个寄存器
    //第一部分:0x88~0xA1一共26个8位寄存器
    //第二部分:0xE1~0xE7 一共7个8位寄存器,都是和湿度有关的,不读了
    uint8_t data[24] = {0};
    const HAL_StatusTypeDef ret = HAL_I2C_Mem_Read(BMP280_I2C, BMP280_I2C_ADDRESS << 1 | 1, CALIBRATION_ADDRESS,
                                                   I2C_MEMADD_SIZE_8BIT, data, 24, 1000);
    if (ret != HAL_OK)
    {
        return HAL_ERROR;
    }
    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];
    return HAL_OK;
}

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

    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T;
}

uint32_t calibration_P(int32_t adc_P)
{
    int32_t var1, var2;
    uint32_t P;
    var1 = (t_fine >> 1) - (int32_t)64000;
    var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)dig_P6);
    var2 = var2 + ((var1 * ((int32_t)dig_P5)) << 1);
    var2 = (var2 >> 2) + (((int32_t)dig_P4) << 16);
    var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t)dig_P2) * var1) >> 1)) >> 18;
    var1 = ((((32768 + var1)) * ((int32_t)dig_P1)) >> 15);
    if (var1 == 0)
    {
        return 0;
    }
    P = (((uint32_t)(((int32_t)1048576) - adc_P) - (var2 >> 12))) * 3125;
    if (P < 0x80000000)
    {
        P = (P << 1) / ((uint32_t)var1);
    }
    else
    {
        P = (P / (uint32_t)var1) * 2;
    }
    var1 = (((int32_t)dig_P9) * ((int32_t)(((P >> 3) * (P >> 3)) >> 13))) >> 12;
    var2 = (((int32_t)(P >> 2)) * ((int32_t)dig_P8)) >> 13;
    P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4));
    return P;
}

使用BMP280Lib

初始化调用BMP280_Init();

随后调用BMP280_Read(&bmp280_temp,&bmp280_press);读取温度和气压即可。

在UP的OLED程序中这样使用:

复制代码
/* 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 "adc.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <math.h>
#include <stdio.h>
#include <stdbool.h>
#include "config.h"

#include "oled.h"

#include "app.h"

#include "ds1302.h"

#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 */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* 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_DMA_Init();
  MX_SPI2_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
#if OLED == 1
  // 初始化OLED
  OLED_Init();
  OLED_DisPlay_On();
  OLED_Clear();
  OLED_Refresh();
  // 初始化3D立方体
  InitCube();
  
#endif


#if DS1302 == 1
  //DS1302
  ds1302_gpio_init();
  //ds1302_init();
  uint32_t time_display_tick = 0;
  // DS1302时间显示缓存
  char time_str1[32],time_str2[32],time_str3[32];
#endif

#if BMP280 == 1

bool bmp280flag = false;
BMP280_Init();
// BMP280数据存储变量
int32_t bmp280_temp = 0;    // 温度(原始值,除以100为℃)
int32_t bmp280_press = 0;   // 气压(原始值,除以100为Pa)
// BMP280显示缓存
char bmp280_temp_str[32] = "Temp: --.- ";
char bmp280_press_str[32] = "Press: ----.- hPa";
#endif


  uint32_t last_refresh_tick = HAL_GetTick();
  uint32_t i=0;
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if(HAL_GetTick() - last_refresh_tick >= 1) {
        if(++i >= 60000) i = 0;
        last_refresh_tick = HAL_GetTick();
        if(i % 100 == 0) bmp280flag = true;
    }
    OLED_Clear_();

#if OLED == 1
    // 核心3D立方体渲染逻辑
    RotateCube();   // 旋转立方体
    ProjectCube();  // 3D转2D投影
    DrawCube();     // 绘制立方体
#endif
    
    // LED同步闪烁
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);

#if DS1302 == 1
    if(HAL_GetTick() - time_display_tick > 500)
    {
        time_display_tick = HAL_GetTick();
        // 读取DS1302实时时间
        ds1302_read_realTime();
        sprintf(time_str1, "%04d-%02d-%02d", TimeData.year, TimeData.month, TimeData.day);
        sprintf(time_str2, "%02d:%02d:%02d", TimeData.hour, TimeData.minute, TimeData.second);
        sprintf(time_str3, "Week:%d", TimeData.week);
    }
    // 格式化时间字符串(OLED显示)
    OLED_ShowString(0, 0, (u8*)time_str1, 16, 1);
    OLED_ShowString(0, 16,(u8*)time_str2,16, 1); // 第二行显示时间
    OLED_ShowString(0, 32,(u8*)time_str3,16, 1); // 第三行显示星期
#endif
    
#if BMP280 == 1
if(bmp280flag == true){
  bmp280flag = false;
  BMP280_Read(&bmp280_temp,&bmp280_press);

  float temp = (float)bmp280_temp / 100.0f;
  sprintf(bmp280_temp_str,"Temp:%.1f",temp);

  float press = (float)bmp280_press / 1000.0f;
  sprintf(bmp280_press_str, "Press:%.3f",press);
}
OLED_ShowString(0,0,(u8*)bmp280_temp_str,16,1);
OLED_ShowString(0,16,(u8*)bmp280_press_str,16,1);

  

#endif

    OLED_Refresh();
    
    //HAL_Delay(10);  // 控制动画帧率
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {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();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != 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 */
相关推荐
峥嵘life2 小时前
Android16 EDLA【CTS】CtsNetTestCases存在fail项
android·java·linux·学习·elasticsearch
OopspoO2 小时前
lubancat-A1
嵌入式硬件
航Hang*2 小时前
计算机等级考试(三级Linux技术)--- 考纲与知识点
linux·运维·服务器·计算机三级·计算机等级考试
txinyu的博客2 小时前
虚拟内存
linux·运维·服务器
Y1rong2 小时前
STM32之ADC
stm32·单片机·嵌入式硬件
蓬荜生灰2 小时前
STM32(8)-- 自己创建库函数
stm32·单片机·嵌入式硬件
楼田莉子2 小时前
Linux进程间通信——管道
linux·运维·服务器·c++·学习
qq_401700412 小时前
基于CN3762 PWM 降压模式双节18650锂电池充电管理芯片
单片机·嵌入式硬件
仰泳之鹅2 小时前
【杂谈】针对Cortex M4内核使用Systick进行延时
单片机·嵌入式硬件