跳转至

光照与气压传感器应用

概述

光照传感器和气压传感器是环境监测系统中的重要组成部分。光照传感器用于测量环境光强度,广泛应用于自动照明控制、显示屏亮度调节等场景;气压传感器用于测量大气压力,可用于天气预报、高度测量、室内外检测等应用。本教程将介绍BH1750光照传感器和BMP280气压传感器的使用方法。

为什么需要光照和气压传感器

  1. 光照传感器应用
  2. 自动照明控制(路灯、室内灯)
  3. 显示屏亮度自适应
  4. 光照强度监测
  5. 植物生长环境监测
  6. 节能管理系统

  7. 气压传感器应用

  8. 天气预报和气象监测
  9. 高度和楼层检测
  10. 室内外位置判断
  11. 运动追踪(爬楼、登山)
  12. 无人机高度控制

  13. 组合应用优势

  14. 完整的环境监测
  15. 多维度数据分析
  16. 提高系统智能化
  17. 增强用户体验

传感器对比

传感器参数对比:

┌──────────┬─────────────┬─────────────┬─────────────┐
│ 参数     │ BH1750      │ BMP280      │ BME280      │
├──────────┼─────────────┼─────────────┼─────────────┤
│ 测量类型 │ 光照强度    │ 气压+温度   │ 气压+温湿度 │
│ 测量范围 │ 1-65535 lux │ 300-1100hPa │ 300-1100hPa │
│ 精度     │ ±20%        │ ±1 hPa      │ ±1 hPa      │
│ 接口     │ I2C         │ I2C/SPI     │ I2C/SPI     │
│ 电压     │ 2.4-3.6V    │ 1.71-3.6V   │ 1.71-3.6V   │
│ 功耗     │ 0.12mA      │ 2.7μA       │ 3.6μA       │
│ 价格     │ 低          │ 中          │ 中高        │
│ 适用场景 │ 光照检测    │ 气压测量    │ 综合环境    │
└──────────┴─────────────┴─────────────┴─────────────┘

选型建议:
- 仅需光照:BH1750(简单、便宜)
- 气压+温度:BMP280(性价比高)
- 完整环境:BME280(功能全面)
- 高精度气压:BMP388(更高精度)

第一部分:BH1750光照传感器

硬件连接

BH1750引脚定义

BH1750引脚(5针封装):

    ┌─────────┐
    │ BH1750  │
    └─┬─┬─┬─┬─┬─┘
      1 2 3 4 5

引脚说明:
1. VCC  - 电源正极(2.4V-3.6V)
2. GND  - 电源地
3. SCL  - I2C时钟线
4. SDA  - I2C数据线
5. ADDR - I2C地址选择

I2C地址:
- ADDR接GND或悬空: 0x23
- ADDR接VCC: 0x5C

硬件连接电路

连接方案:

MCU                    BH1750传感器
                      ┌─────────┐
3.3V ─────────────────┤1 VCC    │
                      │         │
GND ──────────────────┤2 GND    │
                      │         │
SCL ──────┬───────────┤3 SCL    │
          │           │         │
         ┌┴┐          │4 SDA    │
    4.7k │ │          │         │
         └┬┘          │5 ADDR   │──┐
          │           └─────────┘  │
SDA ──────┼──┬────────────────────┘
          │  │                      
         ┌┴┐ │        接GND选择0x23
    4.7k │ │ │        接VCC选择0x5C
         └┬┘ │        
          │  │        
GND ──────┴──┘        

关键要点:
1. SDA和SCL需要上拉电阻(4.7kΩ)
2. 支持标准模式(100kHz)和快速模式(400kHz)
3. ADDR引脚选择I2C地址
4. 建议使用3.3V供电

I2C通信协议

BH1750命令集

常用命令:

1. 电源控制:
   - Power Down: 0x00
   - Power On: 0x01

2. 测量模式:
   - 连续H分辨率模式: 0x10 (1 lux分辨率)
   - 连续H分辨率模式2: 0x11 (0.5 lux分辨率)
   - 连续L分辨率模式: 0x13 (4 lux分辨率)
   - 单次H分辨率模式: 0x20
   - 单次H分辨率模式2: 0x21
   - 单次L分辨率模式: 0x23

3. 测量时间:
   - H分辨率模式: 120ms
   - H分辨率模式2: 120ms
   - L分辨率模式: 16ms

4. 灵敏度调整:
   - 改变测量时间: 0x40-0x7F (高位)
   - 改变测量时间: 0x60-0x7F (低位)

数据格式:
[高字节][低字节]

光照度计算:
Lux = (高字节 << 8 | 低字节) / 1.2

BH1750驱动代码实现

驱动头文件

/**
 * @file    bh1750.h
 * @brief   BH1750光照传感器驱动
 * @author  嵌入式知识平台
 */

#ifndef __BH1750_H
#define __BH1750_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>

/**
 * @brief  BH1750 I2C地址
 */
#define BH1750_ADDR_LOW   0x23    // ADDR引脚接GND
#define BH1750_ADDR_HIGH  0x5C    // ADDR引脚接VCC

/**
 * @brief  BH1750命令
 */
#define BH1750_POWER_DOWN         0x00    // 关闭
#define BH1750_POWER_ON           0x01    // 开启
#define BH1750_RESET              0x07    // 复位
#define BH1750_CONT_H_RES_MODE    0x10    // 连续H分辨率
#define BH1750_CONT_H_RES_MODE2   0x11    // 连续H分辨率2
#define BH1750_CONT_L_RES_MODE    0x13    // 连续L分辨率
#define BH1750_ONE_H_RES_MODE     0x20    // 单次H分辨率
#define BH1750_ONE_H_RES_MODE2    0x21    // 单次H分辨率2
#define BH1750_ONE_L_RES_MODE     0x23    // 单次L分辨率

/**
 * @brief  BH1750测量模式
 */
typedef enum {
    BH1750_MODE_CONT_H_RES = 0,     // 连续H分辨率(1 lux)
    BH1750_MODE_CONT_H_RES2,        // 连续H分辨率2(0.5 lux)
    BH1750_MODE_CONT_L_RES,         // 连续L分辨率(4 lux)
    BH1750_MODE_ONE_H_RES,          // 单次H分辨率
    BH1750_MODE_ONE_H_RES2,         // 单次H分辨率2
    BH1750_MODE_ONE_L_RES           // 单次L分辨率
} BH1750_Mode;

/**
 * @brief  BH1750配置
 */
typedef struct {
    I2C_HandleTypeDef* hi2c;    // I2C句柄
    uint8_t address;            // I2C地址
    BH1750_Mode mode;           // 测量模式
} BH1750_Config;

// 函数声明
bool BH1750_Init(BH1750_Config* config);
bool BH1750_ReadLight(BH1750_Config* config, float* lux);
bool BH1750_SetMode(BH1750_Config* config, BH1750_Mode mode);
bool BH1750_PowerDown(BH1750_Config* config);
bool BH1750_PowerOn(BH1750_Config* config);

#endif /* __BH1750_H */

驱动实现文件

/**
 * @file    bh1750.c
 * @brief   BH1750光照传感器驱动实现
 */

#include "bh1750.h"

// 私有函数声明
static bool BH1750_WriteCommand(BH1750_Config* config, uint8_t cmd);
static bool BH1750_ReadData(BH1750_Config* config, uint16_t* data);

/**
 * @brief  写入命令
 * @param  config: 传感器配置
 * @param  cmd: 命令
 * @retval true: 成功, false: 失败
 */
static bool BH1750_WriteCommand(BH1750_Config* config, uint8_t cmd) {
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        config->hi2c,
        config->address << 1,
        &cmd,
        1,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取数据
 * @param  config: 传感器配置
 * @param  data: 数据指针
 * @retval true: 成功, false: 失败
 */
static bool BH1750_ReadData(BH1750_Config* config, uint16_t* data) {
    uint8_t buffer[2];

    HAL_StatusTypeDef status = HAL_I2C_Master_Receive(
        config->hi2c,
        config->address << 1,
        buffer,
        2,
        HAL_MAX_DELAY
    );

    if (status != HAL_OK) {
        return false;
    }

    *data = (buffer[0] << 8) | buffer[1];
    return true;
}

/**
 * @brief  初始化BH1750传感器
 * @param  config: 传感器配置
 * @retval true: 成功, false: 失败
 */
bool BH1750_Init(BH1750_Config* config) {
    // 上电
    if (!BH1750_PowerOn(config)) {
        return false;
    }

    HAL_Delay(10);

    // 设置测量模式
    if (!BH1750_SetMode(config, config->mode)) {
        return false;
    }

    return true;
}

/**
 * @brief  上电
 * @param  config: 传感器配置
 * @retval true: 成功, false: 失败
 */
bool BH1750_PowerOn(BH1750_Config* config) {
    return BH1750_WriteCommand(config, BH1750_POWER_ON);
}

/**
 * @brief  关闭
 * @param  config: 传感器配置
 * @retval true: 成功, false: 失败
 */
bool BH1750_PowerDown(BH1750_Config* config) {
    return BH1750_WriteCommand(config, BH1750_POWER_DOWN);
}

/**
 * @brief  设置测量模式
 * @param  config: 传感器配置
 * @param  mode: 测量模式
 * @retval true: 成功, false: 失败
 */
bool BH1750_SetMode(BH1750_Config* config, BH1750_Mode mode) {
    uint8_t cmd;

    switch (mode) {
        case BH1750_MODE_CONT_H_RES:
            cmd = BH1750_CONT_H_RES_MODE;
            break;
        case BH1750_MODE_CONT_H_RES2:
            cmd = BH1750_CONT_H_RES_MODE2;
            break;
        case BH1750_MODE_CONT_L_RES:
            cmd = BH1750_CONT_L_RES_MODE;
            break;
        case BH1750_MODE_ONE_H_RES:
            cmd = BH1750_ONE_H_RES_MODE;
            break;
        case BH1750_MODE_ONE_H_RES2:
            cmd = BH1750_ONE_H_RES_MODE2;
            break;
        case BH1750_MODE_ONE_L_RES:
            cmd = BH1750_ONE_L_RES_MODE;
            break;
        default:
            return false;
    }

    config->mode = mode;
    return BH1750_WriteCommand(config, cmd);
}

/**
 * @brief  读取光照强度
 * @param  config: 传感器配置
 * @param  lux: 光照强度指针(lux)
 * @retval true: 成功, false: 失败
 */
bool BH1750_ReadLight(BH1750_Config* config, float* lux) {
    uint16_t raw_data;

    // 如果是单次模式,需要先发送测量命令
    if (config->mode >= BH1750_MODE_ONE_H_RES) {
        if (!BH1750_SetMode(config, config->mode)) {
            return false;
        }

        // 等待测量完成
        if (config->mode == BH1750_MODE_ONE_L_RES) {
            HAL_Delay(24);  // L分辨率模式:16ms + 余量
        } else {
            HAL_Delay(180);  // H分辨率模式:120ms + 余量
        }
    }

    // 读取数据
    if (!BH1750_ReadData(config, &raw_data)) {
        return false;
    }

    // 计算光照强度
    // 公式:Lux = raw_data / 1.2
    *lux = (float)raw_data / 1.2f;

    return true;
}

BH1750使用示例

/**
 * @file    main.c
 * @brief   BH1750传感器使用示例
 */

#include "stm32f1xx_hal.h"
#include "bh1750.h"
#include <stdio.h>

// I2C句柄(需要在CubeMX中配置)
extern I2C_HandleTypeDef hi2c1;

// BH1750传感器配置
BH1750_Config bh1750_config = {
    .hi2c = &hi2c1,
    .address = BH1750_ADDR_LOW,
    .mode = BH1750_MODE_CONT_H_RES    // 连续高分辨率模式
};

/**
 * @brief  主函数
 */
int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化I2C
    MX_I2C1_Init();

    // 初始化UART(用于打印)
    MX_USART1_UART_Init();

    printf("BH1750光照传感器测试\n");
    printf("═══════════════════════════════════════\n\n");

    // 初始化BH1750传感器
    if (!BH1750_Init(&bh1750_config)) {
        printf("❌ BH1750初始化失败!\n");
        printf("请检查:\n");
        printf("  1. I2C连接是否正确\n");
        printf("  2. 上拉电阻是否存在\n");
        printf("  3. 传感器地址是否正确\n");
        while (1);
    }

    printf("✓ BH1750初始化成功\n\n");

    float lux;
    uint32_t read_count = 0;

    while (1) {
        // 读取光照强度
        if (BH1750_ReadLight(&bh1750_config, &lux)) {
            read_count++;

            // 打印数据
            printf("读取 #%lu:\n", read_count);
            printf("  光照强度: %.1f lux\n", lux);

            // 光照等级评估
            if (lux < 10) {
                printf("  等级: 黑暗 🌑\n");
            } else if (lux < 50) {
                printf("  等级: 昏暗 🌘\n");
            } else if (lux < 200) {
                printf("  等级: 室内照明 💡\n");
            } else if (lux < 1000) {
                printf("  等级: 明亮 ☀️\n");
            } else {
                printf("  等级: 强光 ☀️☀️\n");
            }

            printf("\n");
        } else {
            printf("读取失败\n\n");
        }

        // 每秒读取一次
        HAL_Delay(1000);
    }
}

/**
 * @brief  自动照明控制示例
 */
void auto_lighting_control(void) {
    const float LIGHT_THRESHOLD_LOW = 50.0f;   // 开灯阈值
    const float LIGHT_THRESHOLD_HIGH = 100.0f; // 关灯阈值

    float lux;
    bool light_on = false;

    printf("自动照明控制系统\n");
    printf("═══════════════════════════════════════\n");
    printf("开灯阈值: %.1f lux\n", LIGHT_THRESHOLD_LOW);
    printf("关灯阈值: %.1f lux\n\n", LIGHT_THRESHOLD_HIGH);

    while (1) {
        if (BH1750_ReadLight(&bh1750_config, &lux)) {
            printf("当前光照: %.1f lux\n", lux);

            // 滞回控制,避免频繁开关
            if (!light_on && lux < LIGHT_THRESHOLD_LOW) {
                // 光照过低,开灯
                HAL_GPIO_WritePin(LIGHT_GPIO_Port, LIGHT_Pin, GPIO_PIN_SET);
                light_on = true;
                printf("💡 开灯\n");
            } else if (light_on && lux > LIGHT_THRESHOLD_HIGH) {
                // 光照充足,关灯
                HAL_GPIO_WritePin(LIGHT_GPIO_Port, LIGHT_Pin, GPIO_PIN_RESET);
                light_on = false;
                printf("🌙 关灯\n");
            } else {
                printf("状态: %s\n", light_on ? "开启" : "关闭");
            }

            printf("\n");
        }

        HAL_Delay(2000);
    }
}

/**
 * @brief  显示屏亮度自适应示例
 */
void display_brightness_control(void) {
    float lux;
    uint8_t brightness;

    printf("显示屏亮度自适应\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (BH1750_ReadLight(&bh1750_config, &lux)) {
            // 根据光照强度计算亮度(0-100%)
            if (lux < 10) {
                brightness = 10;  // 最低亮度
            } else if (lux > 1000) {
                brightness = 100;  // 最高亮度
            } else {
                // 对数映射
                brightness = (uint8_t)(10 + 90 * logf(lux / 10.0f) / logf(100.0f));
            }

            printf("光照: %.1f lux -> 亮度: %d%%\n", lux, brightness);

            // 设置PWM占空比控制背光
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, brightness);
        }

        HAL_Delay(500);
    }
}

第二部分:BMP280气压传感器

硬件连接

BMP280引脚定义

BMP280引脚(6针封装):

    ┌─────────┐
    │ BMP280  │
    └─┬─┬─┬─┬─┬─┬─┘
      1 2 3 4 5 6

引脚说明:
1. VCC  - 电源正极(1.71V-3.6V)
2. GND  - 电源地
3. SCL  - I2C时钟线 / SPI时钟线
4. SDA  - I2C数据线 / SPI MOSI
5. CSB  - 片选(I2C模式接VCC)
6. SDO  - I2C地址选择 / SPI MISO

I2C地址:
- SDO接GND: 0x76
- SDO接VCC: 0x77

硬件连接电路(I2C模式)

连接方案:

MCU                    BMP280传感器
                      ┌─────────┐
3.3V ─────────────────┤1 VCC    │
                      │         │
GND ──────────────────┤2 GND    │
                      │         │
SCL ──────┬───────────┤3 SCL    │
          │           │         │
         ┌┴┐          │4 SDA    │
    4.7k │ │          │         │
         └┬┘          │5 CSB    │──── VCC (I2C模式)
          │           │         │
SDA ──────┼──┬────────┤6 SDO    │──┐
          │  │        └─────────┘  │
         ┌┴┐ │                      │
    4.7k │ │ │        接GND选择0x76
         └┬┘ │        接VCC选择0x77
          │  │        
GND ──────┴──┘        

关键要点:
1. SDA和SCL需要上拉电阻(4.7kΩ)
2. CSB接VCC选择I2C模式
3. SDO引脚选择I2C地址
4. 支持3.3V供电

寄存器和数据格式

BMP280寄存器地址

重要寄存器:

0xD0: chip_id (固定值0x58)
0xE0: reset (写入0xB6复位)
0xF3: status (测量状态)
0xF4: ctrl_meas (测量控制)
0xF5: config (配置)
0xF7-0xF9: press_msb, press_lsb, press_xlsb (气压数据)
0xFA-0xFC: temp_msb, temp_lsb, temp_xlsb (温度数据)
0x88-0xA1: 校准参数

测量模式:
- Sleep模式: 00
- Forced模式: 01或10 (单次测量)
- Normal模式: 11 (连续测量)

过采样设置:
- 跳过: 000
- ×1: 001
- ×2: 010
- ×4: 011
- ×8: 100
- ×16: 101

BMP280驱动代码实现

驱动头文件

/**
 * @file    bmp280.h
 * @brief   BMP280气压传感器驱动
 * @author  嵌入式知识平台
 */

#ifndef __BMP280_H
#define __BMP280_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>

/**
 * @brief  BMP280 I2C地址
 */
#define BMP280_ADDR_LOW   0x76    // SDO接GND
#define BMP280_ADDR_HIGH  0x77    // SDO接VCC

/**
 * @brief  BMP280寄存器地址
 */
#define BMP280_REG_CHIP_ID      0xD0
#define BMP280_REG_RESET        0xE0
#define BMP280_REG_STATUS       0xF3
#define BMP280_REG_CTRL_MEAS    0xF4
#define BMP280_REG_CONFIG       0xF5
#define BMP280_REG_PRESS_MSB    0xF7
#define BMP280_REG_TEMP_MSB     0xFA
#define BMP280_REG_CALIB_START  0x88

/**
 * @brief  BMP280芯片ID
 */
#define BMP280_CHIP_ID          0x58

/**
 * @brief  BMP280工作模式
 */
typedef enum {
    BMP280_MODE_SLEEP = 0,
    BMP280_MODE_FORCED = 1,
    BMP280_MODE_NORMAL = 3
} BMP280_Mode;

/**
 * @brief  BMP280过采样
 */
typedef enum {
    BMP280_OVERSAMPLING_SKIP = 0,
    BMP280_OVERSAMPLING_1X = 1,
    BMP280_OVERSAMPLING_2X = 2,
    BMP280_OVERSAMPLING_4X = 3,
    BMP280_OVERSAMPLING_8X = 4,
    BMP280_OVERSAMPLING_16X = 5
} BMP280_Oversampling;

/**
 * @brief  BMP280校准参数
 */
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_CalibData;

/**
 * @brief  BMP280配置
 */
typedef struct {
    I2C_HandleTypeDef* hi2c;
    uint8_t address;
    BMP280_Mode mode;
    BMP280_Oversampling temp_oversampling;
    BMP280_Oversampling press_oversampling;
    BMP280_CalibData calib;
} BMP280_Config;

/**
 * @brief  BMP280数据
 */
typedef struct {
    float temperature;    // 温度(°C)
    float pressure;       // 气压(hPa)
    float altitude;       // 高度(米)
    bool valid;
} BMP280_Data;

// 函数声明
bool BMP280_Init(BMP280_Config* config);
bool BMP280_Read(BMP280_Config* config, BMP280_Data* data);
bool BMP280_ReadPressure(BMP280_Config* config, float* pressure);
bool BMP280_ReadTemperature(BMP280_Config* config, float* temperature);
float BMP280_CalculateAltitude(float pressure, float sea_level_pressure);

#endif /* __BMP280_H */

驱动实现文件

/**
 * @file    bmp280.c
 * @brief   BMP280气压传感器驱动实现
 */

#include "bmp280.h"
#include <math.h>

// 私有函数声明
static bool BMP280_WriteReg(BMP280_Config* config, uint8_t reg, uint8_t value);
static bool BMP280_ReadReg(BMP280_Config* config, uint8_t reg, uint8_t* value);
static bool BMP280_ReadRegs(BMP280_Config* config, uint8_t reg, uint8_t* buffer, uint8_t len);
static bool BMP280_ReadCalibration(BMP280_Config* config);
static int32_t BMP280_CompensateTemperature(BMP280_Config* config, int32_t adc_T);
static uint32_t BMP280_CompensatePressure(BMP280_Config* config, int32_t adc_P);

/**
 * @brief  写入寄存器
 */
static bool BMP280_WriteReg(BMP280_Config* config, uint8_t reg, uint8_t value) {
    uint8_t buffer[2] = {reg, value};

    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        config->hi2c,
        config->address << 1,
        buffer,
        2,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取寄存器
 */
static bool BMP280_ReadReg(BMP280_Config* config, uint8_t reg, uint8_t* value) {
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        config->hi2c,
        config->address << 1,
        &reg,
        1,
        HAL_MAX_DELAY
    );

    if (status != HAL_OK) return false;

    status = HAL_I2C_Master_Receive(
        config->hi2c,
        config->address << 1,
        value,
        1,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取多个寄存器
 */
static bool BMP280_ReadRegs(BMP280_Config* config, uint8_t reg, uint8_t* buffer, uint8_t len) {
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        config->hi2c,
        config->address << 1,
        &reg,
        1,
        HAL_MAX_DELAY
    );

    if (status != HAL_OK) return false;

    status = HAL_I2C_Master_Receive(
        config->hi2c,
        config->address << 1,
        buffer,
        len,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取校准参数
 */
static bool BMP280_ReadCalibration(BMP280_Config* config) {
    uint8_t calib[24];

    if (!BMP280_ReadRegs(config, BMP280_REG_CALIB_START, calib, 24)) {
        return false;
    }

    config->calib.dig_T1 = (calib[1] << 8) | calib[0];
    config->calib.dig_T2 = (calib[3] << 8) | calib[2];
    config->calib.dig_T3 = (calib[5] << 8) | calib[4];
    config->calib.dig_P1 = (calib[7] << 8) | calib[6];
    config->calib.dig_P2 = (calib[9] << 8) | calib[8];
    config->calib.dig_P3 = (calib[11] << 8) | calib[10];
    config->calib.dig_P4 = (calib[13] << 8) | calib[12];
    config->calib.dig_P5 = (calib[15] << 8) | calib[14];
    config->calib.dig_P6 = (calib[17] << 8) | calib[16];
    config->calib.dig_P7 = (calib[19] << 8) | calib[18];
    config->calib.dig_P8 = (calib[21] << 8) | calib[20];
    config->calib.dig_P9 = (calib[23] << 8) | calib[22];

    return true;
}

/**
 * @brief  温度补偿计算
 */
static int32_t BMP280_CompensateTemperature(BMP280_Config* config, int32_t adc_T) {
    int32_t var1, var2, T;

    var1 = ((((adc_T >> 3) - ((int32_t)config->calib.dig_T1 << 1))) * 
            ((int32_t)config->calib.dig_T2)) >> 11;

    var2 = (((((adc_T >> 4) - ((int32_t)config->calib.dig_T1)) * 
              ((adc_T >> 4) - ((int32_t)config->calib.dig_T1))) >> 12) * 
            ((int32_t)config->calib.dig_T3)) >> 14;

    config->calib.t_fine = var1 + var2;
    T = (config->calib.t_fine * 5 + 128) >> 8;

    return T;
}

/**
 * @brief  气压补偿计算
 */
static uint32_t BMP280_CompensatePressure(BMP280_Config* config, int32_t adc_P) {
    int64_t var1, var2, p;

    var1 = ((int64_t)config->calib.t_fine) - 128000;
    var2 = var1 * var1 * (int64_t)config->calib.dig_P6;
    var2 = var2 + ((var1 * (int64_t)config->calib.dig_P5) << 17);
    var2 = var2 + (((int64_t)config->calib.dig_P4) << 35);
    var1 = ((var1 * var1 * (int64_t)config->calib.dig_P3) >> 8) + 
           ((var1 * (int64_t)config->calib.dig_P2) << 12);
    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)config->calib.dig_P1) >> 33;

    if (var1 == 0) {
        return 0;
    }

    p = 1048576 - adc_P;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((int64_t)config->calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((int64_t)config->calib.dig_P8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((int64_t)config->calib.dig_P7) << 4);

    return (uint32_t)p;
}

/**
 * @brief  初始化BMP280传感器
 */
bool BMP280_Init(BMP280_Config* config) {
    uint8_t chip_id;

    // 读取芯片ID
    if (!BMP280_ReadReg(config, BMP280_REG_CHIP_ID, &chip_id)) {
        return false;
    }

    if (chip_id != BMP280_CHIP_ID) {
        return false;
    }

    // 软复位
    BMP280_WriteReg(config, BMP280_REG_RESET, 0xB6);
    HAL_Delay(10);

    // 读取校准参数
    if (!BMP280_ReadCalibration(config)) {
        return false;
    }

    // 配置测量参数
    uint8_t ctrl_meas = (config->temp_oversampling << 5) | 
                        (config->press_oversampling << 2) | 
                        config->mode;

    if (!BMP280_WriteReg(config, BMP280_REG_CTRL_MEAS, ctrl_meas)) {
        return false;
    }

    // 配置滤波器和待机时间
    uint8_t config_reg = (0 << 5) | (4 << 2) | 0;  // 滤波器关闭,待机时间500ms
    if (!BMP280_WriteReg(config, BMP280_REG_CONFIG, config_reg)) {
        return false;
    }

    return true;
}

/**
 * @brief  读取传感器数据
 */
bool BMP280_Read(BMP280_Config* config, BMP280_Data* data) {
    uint8_t buffer[6];

    // 如果是Forced模式,需要触发测量
    if (config->mode == BMP280_MODE_FORCED) {
        uint8_t ctrl_meas = (config->temp_oversampling << 5) | 
                            (config->press_oversampling << 2) | 
                            BMP280_MODE_FORCED;
        BMP280_WriteReg(config, BMP280_REG_CTRL_MEAS, ctrl_meas);
        HAL_Delay(50);  // 等待测量完成
    }

    // 读取原始数据
    if (!BMP280_ReadRegs(config, BMP280_REG_PRESS_MSB, buffer, 6)) {
        data->valid = false;
        return false;
    }

    // 解析原始数据
    int32_t adc_P = ((int32_t)buffer[0] << 12) | ((int32_t)buffer[1] << 4) | ((int32_t)buffer[2] >> 4);
    int32_t adc_T = ((int32_t)buffer[3] << 12) | ((int32_t)buffer[4] << 4) | ((int32_t)buffer[5] >> 4);

    // 温度补偿
    int32_t temp = BMP280_CompensateTemperature(config, adc_T);
    data->temperature = (float)temp / 100.0f;

    // 气压补偿
    uint32_t press = BMP280_CompensatePressure(config, adc_P);
    data->pressure = (float)press / 25600.0f;

    // 计算高度
    data->altitude = BMP280_CalculateAltitude(data->pressure, 1013.25f);

    data->valid = true;
    return true;
}

/**
 * @brief  计算高度
 * @param  pressure: 当前气压(hPa)
 * @param  sea_level_pressure: 海平面气压(hPa)
 * @retval 高度(米)
 */
float BMP280_CalculateAltitude(float pressure, float sea_level_pressure) {
    return 44330.0f * (1.0f - powf(pressure / sea_level_pressure, 0.1903f));
}

BMP280使用示例

/**
 * @file    main.c
 * @brief   BMP280传感器使用示例
 */

#include "stm32f1xx_hal.h"
#include "bmp280.h"
#include <stdio.h>

// I2C句柄
extern I2C_HandleTypeDef hi2c1;

// BMP280传感器配置
BMP280_Config bmp280_config = {
    .hi2c = &hi2c1,
    .address = BMP280_ADDR_LOW,
    .mode = BMP280_MODE_NORMAL,
    .temp_oversampling = BMP280_OVERSAMPLING_2X,
    .press_oversampling = BMP280_OVERSAMPLING_16X
};

/**
 * @brief  主函数
 */
int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化I2C
    MX_I2C1_Init();

    // 初始化UART
    MX_USART1_UART_Init();

    printf("BMP280气压传感器测试\n");
    printf("═══════════════════════════════════════\n\n");

    // 初始化BMP280传感器
    if (!BMP280_Init(&bmp280_config)) {
        printf("❌ BMP280初始化失败!\n");
        while (1);
    }

    printf("✓ BMP280初始化成功\n\n");

    BMP280_Data data;
    uint32_t read_count = 0;

    while (1) {
        // 读取传感器数据
        if (BMP280_Read(&bmp280_config, &data)) {
            read_count++;

            printf("读取 #%lu:\n", read_count);
            printf("  温度: %.2f °C\n", data.temperature);
            printf("  气压: %.2f hPa\n", data.pressure);
            printf("  高度: %.1f 米\n", data.altitude);
            printf("\n");
        }

        HAL_Delay(1000);
    }
}

/**
 * @brief  天气预报示例
 */
void weather_forecast(void) {
    #define HISTORY_SIZE 12  // 保存12小时数据

    float pressure_history[HISTORY_SIZE];
    uint8_t history_index = 0;
    bool history_full = false;

    BMP280_Data data;

    printf("简易天气预报系统\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (BMP280_Read(&bmp280_config, &data)) {
            // 保存气压历史
            pressure_history[history_index] = data.pressure;
            history_index++;

            if (history_index >= HISTORY_SIZE) {
                history_index = 0;
                history_full = true;
            }

            printf("当前气压: %.2f hPa\n", data.pressure);

            // 分析气压趋势
            if (history_full) {
                float pressure_change = data.pressure - pressure_history[history_index];

                printf("12小时气压变化: %.2f hPa\n", pressure_change);

                if (pressure_change > 3.0f) {
                    printf("天气预报: ☀️ 天气转好\n");
                } else if (pressure_change < -3.0f) {
                    printf("天气预报: 🌧️ 可能降雨\n");
                } else {
                    printf("天气预报: ⛅ 天气稳定\n");
                }
            } else {
                printf("正在收集数据... (%d/%d)\n", 
                       history_index, HISTORY_SIZE);
            }

            printf("\n");
        }

        // 每小时更新一次
        HAL_Delay(3600000);
    }
}

/**
 * @brief  楼层检测示例
 */
void floor_detection(void) {
    const float FLOOR_HEIGHT = 3.0f;  // 每层楼高度(米)
    float reference_pressure = 0;
    int16_t reference_floor = 0;

    BMP280_Data data;

    printf("楼层检测系统\n");
    printf("═══════════════════════════════════════\n\n");

    // 设置参考楼层
    printf("请在参考楼层按下按钮...\n");
    // 等待按钮按下
    while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_SET);

    if (BMP280_Read(&bmp280_config, &data)) {
        reference_pressure = data.pressure;
        printf("参考楼层气压: %.2f hPa\n\n", reference_pressure);
    }

    while (1) {
        if (BMP280_Read(&bmp280_config, &data)) {
            // 计算相对高度
            float altitude_diff = BMP280_CalculateAltitude(data.pressure, reference_pressure);

            // 计算楼层
            int16_t floor = reference_floor + (int16_t)(altitude_diff / FLOOR_HEIGHT);

            printf("当前气压: %.2f hPa\n", data.pressure);
            printf("相对高度: %.1f 米\n", altitude_diff);
            printf("当前楼层: %d\n", floor);

            if (floor > reference_floor) {
                printf("方向: ⬆️ 上升 %d 层\n", floor - reference_floor);
            } else if (floor < reference_floor) {
                printf("方向: ⬇️ 下降 %d 层\n", reference_floor - floor);
            } else {
                printf("方向: ➡️ 同一楼层\n");
            }

            printf("\n");
        }

        HAL_Delay(1000);
    }
}

第三部分:组合应用

完整环境监测系统

/**
 * @brief  完整环境监测系统
 */

typedef struct {
    float light;          // 光照强度(lux)
    float temperature;    // 温度(°C)
    float pressure;       // 气压(hPa)
    float altitude;       // 高度(米)
    uint32_t timestamp;   // 时间戳
} EnvironmentData;

/**
 * @brief  读取所有环境数据
 */
bool read_environment(EnvironmentData* env) {
    BH1750_Config* light_sensor = &bh1750_config;
    BMP280_Config* pressure_sensor = &bmp280_config;

    // 读取光照
    if (!BH1750_ReadLight(light_sensor, &env->light)) {
        return false;
    }

    // 读取气压和温度
    BMP280_Data bmp_data;
    if (!BMP280_Read(pressure_sensor, &bmp_data)) {
        return false;
    }

    env->temperature = bmp_data.temperature;
    env->pressure = bmp_data.pressure;
    env->altitude = bmp_data.altitude;
    env->timestamp = HAL_GetTick();

    return true;
}

/**
 * @brief  环境监测主程序
 */
void environment_monitoring(void) {
    EnvironmentData env;

    printf("完整环境监测系统\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (read_environment(&env)) {
            printf("环境数据 [%lu ms]:\n", env.timestamp);
            printf("┌─────────────────────────────────┐\n");
            printf("│ 光照强度: %8.1f lux      │\n", env.light);
            printf("│ 温度:     %8.2f °C       │\n", env.temperature);
            printf("│ 气压:     %8.2f hPa      │\n", env.pressure);
            printf("│ 高度:     %8.1f 米       │\n", env.altitude);
            printf("└─────────────────────────────────┘\n");

            // 环境评估
            printf("\n环境评估:\n");

            // 光照评估
            if (env.light < 50) {
                printf("  💡 光照不足,建议开灯\n");
            } else if (env.light > 1000) {
                printf("  ☀️ 光照充足\n");
            } else {
                printf("  ✓ 光照适中\n");
            }

            // 温度评估
            if (env.temperature < 18) {
                printf("  ❄️ 温度偏低\n");
            } else if (env.temperature > 26) {
                printf("  🔥 温度偏高\n");
            } else {
                printf("  ✓ 温度舒适\n");
            }

            // 气压评估
            if (env.pressure < 1000) {
                printf("  📉 气压偏低,可能降雨\n");
            } else if (env.pressure > 1020) {
                printf("  📈 气压偏高,天气晴好\n");
            } else {
                printf("  ✓ 气压正常\n");
            }

            printf("\n");
        }

        HAL_Delay(5000);
    }
}

/**
 * @brief  室内外检测
 */
void indoor_outdoor_detection(void) {
    EnvironmentData env;

    printf("室内外检测系统\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (read_environment(&env)) {
            bool is_indoor = false;
            uint8_t confidence = 0;

            // 判断依据1:光照强度
            if (env.light < 500) {
                confidence += 30;  // 室内可能性+30%
            } else if (env.light > 5000) {
                confidence -= 30;  // 室外可能性+30%
            }

            // 判断依据2:气压变化稳定性
            static float last_pressure = 0;
            if (last_pressure != 0) {
                float pressure_change = fabsf(env.pressure - last_pressure);
                if (pressure_change < 0.5f) {
                    confidence += 20;  // 室内气压更稳定
                }
            }
            last_pressure = env.pressure;

            // 判断依据3:温度稳定性
            static float last_temp = 0;
            if (last_temp != 0) {
                float temp_change = fabsf(env.temperature - last_temp);
                if (temp_change < 1.0f) {
                    confidence += 20;  // 室内温度更稳定
                }
            }
            last_temp = env.temperature;

            // 综合判断
            is_indoor = (confidence > 30);

            printf("位置判断:\n");
            printf("  光照: %.1f lux\n", env.light);
            printf("  气压: %.2f hPa\n", env.pressure);
            printf("  温度: %.2f °C\n", env.temperature);
            printf("  置信度: %d%%\n", 50 + confidence);
            printf("  判断结果: %s\n\n", is_indoor ? "🏠 室内" : "🌳 室外");
        }

        HAL_Delay(10000);
    }
}

常见问题与解决方案

BH1750常见问题

问题1:读数始终为0或65535

可能原因:
1. I2C通信失败
   - 检查I2C地址是否正确
   - 检查上拉电阻
   - 检查连线

2. 传感器未上电
   - 检查VCC连接
   - 测量电源电压

3. 传感器损坏
   - 更换传感器测试

解决方案:
/**
 * @brief  BH1750诊断
 */
void diagnose_bh1750(void) {
    printf("BH1750诊断程序\n");
    printf("═══════════════════════════════════════\n\n");

    // 测试I2C通信
    printf("1. 测试I2C通信...\n");
    uint8_t test_data;
    HAL_StatusTypeDef status = HAL_I2C_Master_Receive(
        bh1750_config.hi2c,
        bh1750_config.address << 1,
        &test_data,
        1,
        100
    );

    if (status == HAL_OK) {
        printf("   ✓ I2C通信正常\n");
    } else {
        printf("   ❌ I2C通信失败 (错误码: %d)\n", status);
        printf("   请检查:\n");
        printf("     - I2C地址是否正确\n");
        printf("     - 上拉电阻是否存在\n");
        printf("     - 连线是否正确\n");
        return;
    }

    // 测试多次读取
    printf("\n2. 测试连续读取...\n");
    int success = 0;
    float lux;

    for (int i = 0; i < 10; i++) {
        if (BH1750_ReadLight(&bh1750_config, &lux)) {
            success++;
            printf("   读取 #%d: %.1f lux\n", i+1, lux);
        }
        HAL_Delay(200);
    }

    printf("   成功率: %d/10\n", success);

    if (success < 8) {
        printf("   ⚠ 成功率偏低,可能存在问题\n");
    } else {
        printf("   ✓ 传感器工作正常\n");
    }
}

问题2:光照读数不准确

校准方法:
1. 使用标准光照计对比
2. 在不同光照条件下测试
3. 考虑传感器安装位置
4. 避免遮挡和反光

注意事项:
- BH1750精度约±20%
- 适合相对测量,不适合精密测量
- 定期校准提高精度

BMP280常见问题

问题1:气压读数异常

/**
 * @brief  BMP280数据验证
 */
bool validate_bmp280_data(BMP280_Data* data) {
    // 气压范围检查(300-1100 hPa)
    if (data->pressure < 300 || data->pressure > 1100) {
        printf("⚠ 气压超出范围: %.2f hPa\n", data->pressure);
        return false;
    }

    // 温度范围检查(-40 to 85°C)
    if (data->temperature < -40 || data->temperature > 85) {
        printf("⚠ 温度超出范围: %.2f°C\n", data->temperature);
        return false;
    }

    // 变化率检查
    static float last_pressure = 1013.25f;
    float pressure_change = fabsf(data->pressure - last_pressure);

    if (pressure_change > 10.0f) {
        printf("⚠ 气压变化过大: %.2f hPa\n", pressure_change);
        return false;
    }

    last_pressure = data->pressure;
    return true;
}

问题2:高度计算不准确

影响因素:
1. 海平面气压设置
   - 需要根据当地实际气压校准
   - 可从气象站获取

2. 温度影响
   - 气压随温度变化
   - 使用温度补偿

3. 天气变化
   - 气压随天气变化
   - 定期更新参考气压

解决方案:
/**
 * @brief  高度校准
 */
void calibrate_altitude(void) {
    BMP280_Data data;
    float known_altitude;  // 已知高度
    float sea_level_pressure;

    printf("高度校准程序\n");
    printf("═══════════════════════════════════════\n\n");

    printf("请输入当前已知高度(米): ");
    scanf("%f", &known_altitude);

    // 读取当前气压
    if (BMP280_Read(&bmp280_config, &data)) {
        // 反推海平面气压
        // P0 = P / (1 - h/44330)^5.255
        sea_level_pressure = data.pressure / 
            powf(1.0f - known_altitude / 44330.0f, 5.255f);

        printf("\n校准结果:\n");
        printf("  当前气压: %.2f hPa\n", data.pressure);
        printf("  已知高度: %.1f 米\n", known_altitude);
        printf("  海平面气压: %.2f hPa\n", sea_level_pressure);
        printf("\n请在代码中使用此海平面气压值\n");
    }
}

性能优化与最佳实践

低功耗设计

/**
 * @brief  低功耗环境监测
 */
void low_power_monitoring(void) {
    EnvironmentData env;

    while (1) {
        // 唤醒传感器
        BH1750_PowerOn(&bh1750_config);
        HAL_Delay(10);

        // 读取数据
        read_environment(&env);

        // 处理数据
        process_environment_data(&env);

        // 关闭传感器
        BH1750_PowerDown(&bh1750_config);

        // 进入低功耗模式
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

        // 唤醒后恢复时钟
        SystemClock_Config();

        // 下次测量间隔由RTC唤醒控制
    }
}

数据滤波

/**
 * @brief  移动平均滤波
 */
typedef struct {
    float buffer[10];
    uint8_t index;
    bool full;
} MovingAverage;

float moving_average_filter(MovingAverage* ma, float new_value) {
    ma->buffer[ma->index] = new_value;
    ma->index++;

    if (ma->index >= 10) {
        ma->index = 0;
        ma->full = true;
    }

    float sum = 0;
    int count = ma->full ? 10 : ma->index;

    for (int i = 0; i < count; i++) {
        sum += ma->buffer[i];
    }

    return sum / count;
}

总结

关键要点

  1. 传感器选型
  2. BH1750:简单易用的光照传感器
  3. BMP280:高精度气压温度传感器
  4. 组合使用实现完整环境监测

  5. 硬件设计

  6. I2C上拉电阻必不可少
  7. 注意地址选择引脚配置
  8. 电源去耦电容
  9. 合理布局减少干扰

  10. 软件实现

  11. 正确实现I2C通信
  12. BMP280需要校准参数补偿
  13. 添加数据有效性检查
  14. 实现滤波提高稳定性

  15. 实际应用

  16. 自动照明控制
  17. 天气预报
  18. 高度/楼层检测
  19. 室内外判断
  20. 完整环境监测

下一步学习

  • 超声波测距传感器
  • 生物传感器(心率、血氧)
  • 传感器数据融合技术
  • 多传感器系统设计

作者: 嵌入式知识平台内容团队
最后更新: 2026-03-07
版本: 1.0.0