欢迎光临
我们一直在努力

STM32F103+BMP180 气压温度传感器 + OLED 完整教程|软件 I2C 驱动、补偿算法、海拔计算全解析

前言

最近做嵌入式实训项目:STM32F103C8T6 搭配 BMP180 数字气压传感器,通过软件 I2C 读取温压原始数据,利用芯片出厂校准系数做补偿运算,再通过标准大气公式估算海拔,最终 12864 OLED 实时显示温度、气压、海拔。 本文完整梳理硬件接线、BMP180 工作原理、软件 I2C 通信、校准补偿逻辑、海拔换算、排错方案,适合单片机入门、课程设计、毕设直接复用。

视频讲解与演示:STM32F103+BMP180 气压温度传感器 + OLED 完整教程|软件 I2C 驱动、补偿算法、海拔计算全解析http:// https://www.bilibili.com/video/BV1HdKE6uEKm/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

一、整体实现流程

外界温压变化 → BMP180 MEMS 压阻元件产生信号 → 内部 ADC 输出原始 UT/UP → STM32 软件 I2C 读取原始数据 + 片内校准参数 → 补偿计算真实温压 → 公式换算海拔 → OLED 屏幕刷新展示

核心逻辑一句话:BMP180 只输出未补偿原始 ADC 值,有效温压、海拔全部由 STM32 软件运算得出。

二、硬件接线方案(双组独立软件 I2C 互不干扰)

1. BMP180 接线

BMP180 引脚STM32 引脚功能
SCL PB6 I2C 时钟
SDA PB7 I2C 数据
VCC 3.3V 供电(芯片额定 1.8~3.6V,禁止直连 5V 裸芯片)
GND GND 共地,通信稳定必备

2. OLED 接线(独立 I2C,不占用 BMP 总线)

OLED 引脚STM32 引脚功能
SCL PB8 I2C 时钟
SDA PB9 I2C 数据
VCC 3.3V 供电
GND GND 共地

3. I2C 上拉电阻关键说明

I2C 为开漏输出,总线必须上拉;手册推荐 2.2k~10kΩ,典型 4.7kΩ。 市面上成品 BMP180 模块自带上拉,裸芯片 / 无板载电阻模块需外接:3.3V 分别串 4.7k 电阻接 SCL、SDA,否则无应答、通信报错。

三、BMP180 内部工作原理

BMP180 是博世 MEMS 压阻式数字气压传感器,内部结构:微型压力膜片 + 压阻电桥 + 温度检测单元 + ADC+E2PROM 校准存储 + I2C 接口。

  • 大气压力挤压膜片形变,压阻阻值改变;
  • 温度同时会干扰压阻输出,所以必须同步采集温度做补偿;
  • ADC 输出原始温度 UT、原始气压 UP,无工程意义,不能直接使用;
  • 每颗芯片出厂写入 11 组唯一 16 位校准参数,存储在 E2PROM 寄存器 0xAA~0xBF,用于抵消个体偏移、温度漂移误差。
  • 四、软件 I2C 通信细节(无硬件 I2C 外设,纯 GPIO 模拟)

    1. I2C 设备地址

    7 位基础地址:0x77

    • 写操作 8 位地址:0xEE(0x77<<1,最低位 0)
    • 读操作 8 位地址:0xEF(0x77<<1 | 1,最低位 1)

    2. 标准读写时序

    1)写寄存器:起始信号→发送写地址等待 ACK→寄存器地址→写入数据→停止信号 2)读寄存器:起始→写地址 + 寄存器地址→重复起始→读地址→读取字节→最后 NACK→停止信号 初始化阶段会发送 9 个恢复时钟,释放卡死的 I2C 总线,解决异常通信死机问题。

    五、初始化流程与芯片校验

    BMP180_Init()执行步骤:

  • PB6、PB7 配置为开漏输出,释放总线电平;
  • 读取 ID 寄存器 0xD0,标准 ID 固定 0x55;读不到则判定传感器离线,OLED 提示 Sensor error;
  • 读取 0xAA~0xBF 全部校准系数,校验有效性;
  • 校验通过,初始化完成。
  • 重点:不同 BMP180 校准参数不能通用,必须上电实时读取自身 E2PROM。

    六、温压测量完整流程(OSS=0 超低功耗模式)

    规则:必须先测温度,再测气压

  • 向 0xF4 写入 0x2E,启动温度转换,延时 5ms(手册最大转换时间 4.5ms,留余量);
  • 读取 0xF6、0xF7 得到原始温度 UT;
  • 用温度校准系数计算中间变量 B5(气压补偿必备参数);
  • 向 0xF4 写入 0x34(OSS=0)启动气压转换,延时 5ms;
  • 读取 0xF6/F7/F8 获取 3 字节原始气压 UP;
  • 结合 B5 与全部校准参数,补偿计算真实气压;
  • 标准大气公式计算估算海拔,刷新 OLED。
  • 寄存器速查表: 0xD0:芯片 ID;0xF4:测量控制寄存器;0xF6/F7/F8:转换结果寄存器

    七、补偿计算与单位转换

  • 温度:输出单位 0.1℃,例 235=23.5℃;
  • 气压:输出单位 Pa,1hPa=100Pa;屏幕显示拆分整数 / 小数位;
  • 海拔计算公式(国际标准大气模型): Altitude = 44330 × [1 – (P / P0)^(1 / 5.255)] P = 当前实测气压,P0 固定 101325Pa(标准海平面气压)
  • 海拔误差说明

    海拔不是传感器直接测量值,纯气压推算结果:

  • 当地实时海平面气压不等于 101325Pa,天气、季节、地域都会造成偏差;
  • 想要精准海拔,需要根据当地气象数据修改 P0,或在已知高度点反向校准。
  • 八、OLED 显示格式

    BMP180
    Temp: 23.5 C
    Press:1013.25 hPa
    Alt: 0042 m

    异常提示:

    • Sensor error:芯片失联、ID 错误、校准参数失效;
    • Check PB6 / PB7:I2C 引脚接反、供电 / 接地故障; 主循环 500ms 刷新一次,通信失败自动重新初始化,恢复后自动正常采集。

    九、工程文件函数分工

  • BMP180.c:底层 I2C 时序、初始化、读取校准、温压采集计算;
  • #include "BMP180.h"
    #include "delay.h"
    #include <math.h>

    /*
    * 文件名称:BMP180.c
    * 功能说明:
    * 1. 使用 PB6、PB7 模拟 I2C 时序,与 BMP180 进行通信;
    * 2. 读取芯片内部 E2PROM 中的出厂校准系数;
    * 3. 读取原始温度和原始气压,并按照数据手册完成补偿计算;
    * 4. 使用标准大气公式估算海拔。
    *
    * 硬件连接:BMP180 SCL -> PB6,BMP180 SDA -> PB7。
    */

    /* BMP180 软件 I2C 使用的 GPIO 引脚。 */
    #define BMP180_SCL_PIN GPIO_Pin_6
    #define BMP180_SDA_PIN GPIO_Pin_7

    /* BMP180 的 7 位地址为 0x77,加入读写位后分别为 0xEE 和 0xEF。 */
    #define BMP180_ADDRESS_WRITE 0xEE
    #define BMP180_ADDRESS_READ 0xEF

    /* BMP180 主要寄存器地址。 */
    #define BMP180_REG_CHIP_ID 0xD0
    #define BMP180_REG_CONTROL 0xF4
    #define BMP180_REG_RESULT 0xF6

    /* 写入测量控制寄存器的转换命令。当前气压测量采用 OSS=0 模式。 */
    #define BMP180_CMD_TEMPERATURE 0x2E
    #define BMP180_CMD_PRESSURE_OSS0 0x34

    /*
    * BMP180 出厂校准参数。
    * 每颗芯片的参数都不相同,必须从 0xAA~0xBF 寄存器读取,
    * 不能直接使用其他芯片的固定参数。
    */
    typedef struct
    {
    s16 ac1;
    s16 ac2;
    s16 ac3;
    u16 ac4;
    u16 ac5;
    u16 ac6;
    s16 b1;
    s16 b2;
    s16 mb;
    s16 mc;
    s16 md;
    } BMP180_CalibrationTypeDef;

    /* 保存当前芯片的校准参数,以及驱动是否已成功初始化。 */
    static BMP180_CalibrationTypeDef bmp180_cal;
    static u8 bmp180_ready = 0;

    /* 软件 I2C 时序延时,用于控制 SCL 和 SDA 的变化速度。 */
    static void BMP180_I2C_Delay(void)
    {
    delay_us(3);
    }

    /* 释放 SCL。PB6 为开漏输出,写 1 后由上拉电阻产生高电平。 */
    static void BMP180_SCL_High(void)
    {
    GPIO_SetBits(GPIOB, BMP180_SCL_PIN);
    }

    /* 将 SCL 主动拉低。 */
    static void BMP180_SCL_Low(void)
    {
    GPIO_ResetBits(GPIOB, BMP180_SCL_PIN);
    }

    /* 释放 SDA,使 BMP180 可以在数据线或应答周期驱动该引脚。 */
    static void BMP180_SDA_High(void)
    {
    GPIO_SetBits(GPIOB, BMP180_SDA_PIN);
    }

    /* 将 SDA 主动拉低。 */
    static void BMP180_SDA_Low(void)
    {
    GPIO_ResetBits(GPIOB, BMP180_SDA_PIN);
    }

    /* 读取 SDA 的实际电平,用于接收数据和检测从机 ACK。 */
    static u8 BMP180_SDA_Read(void)
    {
    return (u8)GPIO_ReadInputDataBit(GPIOB, BMP180_SDA_PIN);
    }

    /*
    * 产生 I2C 停止信号:
    * SCL 为高电平期间,SDA 从低电平跳变为高电平。
    */
    static void BMP180_I2C_Stop(void)
    {
    BMP180_SCL_Low();
    BMP180_SDA_Low();
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    BMP180_SDA_High();
    BMP180_I2C_Delay();
    }

    /*
    * 产生 I2C 起始或重复起始信号:
    * SCL 为高电平期间,SDA 从高电平跳变为低电平。
    */
    static void BMP180_I2C_Start(void)
    {
    BMP180_SDA_High();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    BMP180_SDA_Low();
    BMP180_I2C_Delay();
    BMP180_SCL_Low();
    }

    /*
    * 通过软件 I2C 发送一个字节,数据从最高位开始发送。
    * 发送完成后释放 SDA,并在第 9 个时钟周期读取 BMP180 的应答。
    * 返回值:1 表示收到低电平 ACK;0 表示无应答。
    */
    static u8 BMP180_I2C_WriteByte(u8 value)
    {
    u8 i;
    u8 ack;

    for(i = 0; i < 8; i++)
    {
    BMP180_SCL_Low();
    if((value & 0x80U) != 0U)
    {
    BMP180_SDA_High();
    }
    else
    {
    BMP180_SDA_Low();
    }
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    value <<= 1;
    }

    /* 第 9 个时钟周期用于读取从机应答。 */
    BMP180_SCL_Low();
    BMP180_SDA_High();
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    ack = (BMP180_SDA_Read() == 0U) ? 1U : 0U;
    BMP180_SCL_Low();
    return ack;
    }

    /*
    * 通过软件 I2C 接收一个字节,数据从最高位开始接收。
    * send_ack 非 0 时发送 ACK,表示主机还要继续读取;
    * send_ack 为 0 时发送 NACK,表示当前字节是最后一个字节。
    */
    static u8 BMP180_I2C_ReadByte(u8 send_ack)
    {
    u8 i;
    u8 value = 0;

    BMP180_SDA_High();
    for(i = 0; i < 8; i++)
    {
    BMP180_SCL_Low();
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    value <<= 1;
    if(BMP180_SDA_Read() != 0U)
    {
    value |= 1U;
    }
    BMP180_I2C_Delay();
    }

    /* 第 9 个时钟周期由主机向 BMP180 返回 ACK 或 NACK。 */
    BMP180_SCL_Low();
    if(send_ack != 0U)
    {
    BMP180_SDA_Low();
    }
    else
    {
    BMP180_SDA_High();
    }
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    BMP180_SCL_Low();
    BMP180_SDA_High();
    return value;
    }

    /*
    * I2C 总线恢复:释放 SDA 后输出 9 个 SCL 时钟,再发送停止信号。
    * 当上一次通信异常中断、从机仍占用 SDA 时,该操作有助于释放总线。
    */
    static void BMP180_I2C_BusRecovery(void)
    {
    u8 i;

    BMP180_SDA_High();
    for(i = 0; i < 9; i++)
    {
    BMP180_SCL_Low();
    BMP180_I2C_Delay();
    BMP180_SCL_High();
    BMP180_I2C_Delay();
    }
    BMP180_I2C_Stop();
    }

    /* 向 BMP180 的指定寄存器写入一个字节,任一阶段无 ACK 都返回失败。 */
    static u8 BMP180_WriteRegister(u8 reg, u8 value)
    {
    BMP180_I2C_Start();
    if(BMP180_I2C_WriteByte(BMP180_ADDRESS_WRITE) == 0U ||
    BMP180_I2C_WriteByte(reg) == 0U ||
    BMP180_I2C_WriteByte(value) == 0U)
    {
    BMP180_I2C_Stop();
    return 0;
    }
    BMP180_I2C_Stop();
    return 1;
    }

    /*
    * 从指定寄存器开始连续读取 length 个字节。
    * 先以写方向发送寄存器地址,再发送重复起始信号并切换到读方向。
    */
    static u8 BMP180_ReadRegisters(u8 reg, u8 *buffer, u8 length)
    {
    u8 i;

    if(buffer == 0 || length == 0U)
    {
    return 0;
    }

    /* 写入待读取的起始寄存器地址。 */
    BMP180_I2C_Start();
    if(BMP180_I2C_WriteByte(BMP180_ADDRESS_WRITE) == 0U ||
    BMP180_I2C_WriteByte(reg) == 0U)
    {
    BMP180_I2C_Stop();
    return 0;
    }

    /* 重复起始后切换到读方向,最后一个字节返回 NACK。 */
    BMP180_I2C_Start();
    if(BMP180_I2C_WriteByte(BMP180_ADDRESS_READ) == 0U)
    {
    BMP180_I2C_Stop();
    return 0;
    }

    for(i = 0; i < length; i++)
    {
    buffer[i] = BMP180_I2C_ReadByte((i + 1U < length) ? 1U : 0U);
    }
    BMP180_I2C_Stop();
    return 1;
    }

    /* 连续读取大端格式的两个字节,并组合成一个 16 位无符号数。 */
    static u8 BMP180_ReadU16(u8 reg, u16 *value)
    {
    u8 bytes[2];

    if(value == 0 || BMP180_ReadRegisters(reg, bytes, 2) == 0U)
    {
    return 0;
    }
    *value = (u16)(((u16)bytes[0] << 8) | bytes[1]);
    return 1;
    }

    /*
    * 从 E2PROM 读取 11 个出厂校准系数。
    * AC1、AC2、AC3、B1、B2、MB、MC、MD 为有符号数;
    * AC4、AC5、AC6 为无符号数。
    */
    static u8 BMP180_ReadCalibration(void)
    {
    u16 value;

    if(BMP180_ReadU16(0xAA, &value) == 0U) return 0;
    bmp180_cal.ac1 = (s16)value;
    if(BMP180_ReadU16(0xAC, &value) == 0U) return 0;
    bmp180_cal.ac2 = (s16)value;
    if(BMP180_ReadU16(0xAE, &value) == 0U) return 0;
    bmp180_cal.ac3 = (s16)value;
    if(BMP180_ReadU16(0xB0, &value) == 0U) return 0;
    bmp180_cal.ac4 = value;
    if(BMP180_ReadU16(0xB2, &value) == 0U) return 0;
    bmp180_cal.ac5 = value;
    if(BMP180_ReadU16(0xB4, &value) == 0U) return 0;
    bmp180_cal.ac6 = value;
    if(BMP180_ReadU16(0xB6, &value) == 0U) return 0;
    bmp180_cal.b1 = (s16)value;
    if(BMP180_ReadU16(0xB8, &value) == 0U) return 0;
    bmp180_cal.b2 = (s16)value;
    if(BMP180_ReadU16(0xBA, &value) == 0U) return 0;
    bmp180_cal.mb = (s16)value;
    if(BMP180_ReadU16(0xBC, &value) == 0U) return 0;
    bmp180_cal.mc = (s16)value;
    if(BMP180_ReadU16(0xBE, &value) == 0U) return 0;
    bmp180_cal.md = (s16)value;

    /* 关键无符号校准参数为 0x0000 或 0xFFFF 时,认为校准数据无效。 */
    if(bmp180_cal.ac4 == 0U || bmp180_cal.ac4 == 0xFFFFU ||
    bmp180_cal.ac5 == 0U || bmp180_cal.ac5 == 0xFFFFU ||
    bmp180_cal.ac6 == 0U || bmp180_cal.ac6 == 0xFFFFU)
    {
    return 0;
    }
    return 1;
    }

    /*
    * 初始化 BMP180:
    * 1. 将 PB6、PB7 配置为 50MHz 开漏输出;
    * 2. 恢复并释放软件 I2C 总线;
    * 3. 读取 0xD0,确认芯片 ID 为 0x55;
    * 4. 读取并检查出厂校准参数。
    */
    u8 BMP180_Init(void)
    {
    GPIO_InitTypeDef gpio;
    u8 chip_id;

    bmp180_ready = 0;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    gpio.GPIO_Pin = BMP180_SCL_PIN | BMP180_SDA_PIN;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_Init(GPIOB, &gpio);
    GPIO_SetBits(GPIOB, BMP180_SCL_PIN | BMP180_SDA_PIN);
    BMP180_I2C_BusRecovery();

    /* 芯片 ID 不为 0x55 时,通常表示接线、供电或 I2C 通信异常。 */
    if(BMP180_ReadRegisters(BMP180_REG_CHIP_ID, &chip_id, 1) == 0U ||
    chip_id != 0x55U)
    {
    return 0;
    }
    if(BMP180_ReadCalibration() == 0U)
    {
    return 0;
    }

    bmp180_ready = 1;
    return 1;
    }

    /*
    * 读取并计算 BMP180 数据。
    * BMP180 的气压结果依赖温度补偿中间量 B5,
    * 因此必须先完成温度测量和补偿,再进行气压测量和补偿。
    */
    u8 BMP180_ReadData(BMP180_DataTypeDef *data)
    {
    u8 raw_bytes[3];
    u16 raw_temperature;
    u32 raw_pressure;
    s32 x1;
    s32 x2;
    s32 x3;
    s32 b3;
    s32 b5;
    s32 b6;
    u32 b4;
    u32 b7;
    s32 pressure;
    s32 denominator;
    double altitude;

    /* 防止使用空指针,或在传感器尚未成功初始化时读取数据。 */
    if(data == 0 || bmp180_ready == 0U)
    {
    return 0;
    }

    /* 第一步:启动温度转换。0x2E 对应一次温度测量。 */
    if(BMP180_WriteRegister(BMP180_REG_CONTROL, BMP180_CMD_TEMPERATURE) == 0U)
    {
    return 0;
    }
    /* 数据手册规定温度转换最长约 4.5ms,这里等待 5ms。 */
    delay_ms(5);
    if(BMP180_ReadRegisters(BMP180_REG_RESULT, raw_bytes, 2) == 0U)
    {
    return 0;
    }
    raw_temperature = (u16)(((u16)raw_bytes[0] << 8) | raw_bytes[1]);

    /*
    * 使用 UT、AC5、AC6、MC、MD 计算真实温度及中间量 B5。
    * temperature 的单位为 0.1℃。
    */
    x1 = (((s32)raw_temperature – (s32)bmp180_cal.ac6) *
    (s32)bmp180_cal.ac5) >> 15;
    denominator = x1 + (s32)bmp180_cal.md;
    if(denominator == 0)
    {
    return 0;
    }
    x2 = ((s32)bmp180_cal.mc << 11) / denominator;
    b5 = x1 + x2;
    data->temperature = (b5 + 8) >> 4;

    /* 第二步:启动 OSS=0(超低功耗模式)气压转换。 */
    if(BMP180_WriteRegister(BMP180_REG_CONTROL, BMP180_CMD_PRESSURE_OSS0) == 0U)
    {
    return 0;
    }
    /* OSS=0 气压转换最长约 4.5ms,这里等待 5ms。 */
    delay_ms(5);
    if(BMP180_ReadRegisters(BMP180_REG_RESULT, raw_bytes, 3) == 0U)
    {
    return 0;
    }
    raw_pressure = (((u32)raw_bytes[0] << 16) |
    ((u32)raw_bytes[1] << 8) |
    raw_bytes[2]) >> 8;

    /*
    * 按照 BMP180 数据手册的整数补偿算法计算真实气压。
    * B5 来自前面的温度补偿,因此不能跳过温度测量直接计算气压。
    */
    b6 = b5 – 4000;
    x1 = ((s32)bmp180_cal.b2 * ((b6 * b6) >> 12)) >> 11;
    x2 = ((s32)bmp180_cal.ac2 * b6) >> 11;
    x3 = x1 + x2;
    b3 = ((((s32)bmp180_cal.ac1 * 4 + x3) + 2) >> 2);
    x1 = ((s32)bmp180_cal.ac3 * b6) >> 13;
    x2 = ((s32)bmp180_cal.b1 * ((b6 * b6) >> 12)) >> 16;
    x3 = (x1 + x2 + 2) >> 2;
    b4 = ((u32)bmp180_cal.ac4 * (u32)(x3 + 32768)) >> 15;
    if(b4 == 0U || (s32)raw_pressure < b3)
    {
    return 0;
    }

    /* 根据 B7 的数值范围选择不同计算顺序,避免 32 位整数溢出。 */
    b7 = (raw_pressure – (u32)b3) * 50000U;
    if(b7 < 0x80000000UL)
    {
    pressure = (s32)((b7 << 1) / b4);
    }
    else
    {
    pressure = (s32)((b7 / b4) << 1);
    }
    x1 = (pressure >> 8) * (pressure >> 8);
    x1 = (x1 * 3038) >> 16;
    x2 = (-7357 * pressure) >> 16;
    pressure += (x1 + x2 + 3791) >> 4;
    if(pressure <= 0)
    {
    return 0;
    }

    /* pressure 的单位为 Pa,补偿算法的输出分辨率为 1Pa。 */
    data->pressure = (u32)pressure;

    /*
    * 使用标准大气公式估算海拔:
    * H = 44330 × [1 – (P / P0)^0.190294957]
    * 其中 P0 固定为标准海平面气压 101325Pa,所以该结果是估算值。
    */
    altitude = 44330.0 * (1.0 – pow((double)pressure / 101325.0,
    0.190294957));
    data->altitude = (s32)((altitude >= 0.0) ? (altitude + 0.5) : (altitude – 0.5));
    return 1;
    }

  • BMP180.h:数据结构体定义;
  • #ifndef __BMP180_H
    #define __BMP180_H

    #include "sys.h"

    /*
    * 文件名称:BMP180.h
    * 功能说明:声明 BMP180 驱动对外提供的数据类型和操作接口。
    *
    * BMP180 使用独立的软件 I2C 总线:
    * SCL -> PB6
    * SDA -> PB7
    * OLED 仍使用 PB8/PB9,两个模块的通信引脚互不影响。
    */

    /* BMP180 完成补偿计算后的测量结果。 */
    typedef struct
    {
    s32 temperature; /* 温度,单位为 0.1℃,例如 235 表示 23.5℃。 */
    u32 pressure; /* 绝对气压,单位为 Pa,例如 101325 表示 1013.25hPa。 */
    s32 altitude; /* 估算海拔,单位为 m,以 101325Pa 为海平面参考气压。 */
    } BMP180_DataTypeDef;

    /*
    * 初始化 BMP180 软件 I2C、检查芯片 ID 并读取芯片校准参数。
    * 返回值:1 表示初始化成功;0 表示芯片无应答、ID 错误或校准数据无效。
    */
    u8 BMP180_Init(void);

    /*
    * 启动温度和气压转换,读取原始数据并完成补偿及海拔计算。
    * 参数 data:用于保存温度、气压和估算海拔的结构体指针。
    * 返回值:1 表示读取成功;0 表示参数无效、未初始化或通信失败。
    */
    u8 BMP180_ReadData(BMP180_DataTypeDef *data);

    #endif

  • main.c:OLED 显示封装、屏幕数字打印函数;
  • /*
    * BMP180 environmental monitor
    * BMP180 SCL -> PB6, SDA -> PB7
    * OLED SCL -> PB8, SDA -> PB9 (unchanged)
    */
    #include "delay.h"
    #include "sys.h"
    #include "oled.h"
    #include "BMP180.h"

    #define BMP180_REFRESH_MS 500

    static void BMP180_ShowData(const BMP180_DataTypeDef *data);
    static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 len, u8 size);

    int main(void)
    {
    BMP180_DataTypeDef sensor_data;
    u8 sensor_ok;

    delay_init();
    delay_ms(1000);
    NVIC_Configuration();
    OLED_Init();
    OLED_ColorTurn(0);
    OLED_DisplayTurn(0);

    OLED_Clear();
    OLED_ShowString(32, 0, (u8 *)"BMP180", 16);
    OLED_ShowString(8, 24, (u8 *)"SCL PB6 SDA PB7", 12);
    OLED_ShowString(20, 44, (u8 *)"Initializing", 12);
    OLED_Refresh();

    sensor_ok = BMP180_Init();
    delay_ms(200);

    while(1)
    {
    if(sensor_ok != 0U && BMP180_ReadData(&sensor_data) != 0U)
    {
    BMP180_ShowData(&sensor_data);
    }
    else
    {
    OLED_Clear();
    OLED_ShowString(32, 0, (u8 *)"BMP180", 16);
    OLED_ShowString(18, 24, (u8 *)"Sensor error", 12);
    OLED_ShowString(3, 42, (u8 *)"Check PB6 / PB7", 12);
    OLED_Refresh();
    delay_ms(500);
    sensor_ok = BMP180_Init();
    }
    delay_ms(BMP180_REFRESH_MS);
    }
    }

    static void BMP180_ShowData(const BMP180_DataTypeDef *data)
    {
    u32 temperature_abs;
    u32 pressure_hpa;

    temperature_abs = (u32)((data->temperature < 0) ?
    -data->temperature : data->temperature);
    pressure_hpa = data->pressure / 100U;

    OLED_Clear();
    OLED_ShowString(32, 0, (u8 *)"BMP180", 16);

    OLED_ShowString(0, 18, (u8 *)"Temp:", 12);
    if(data->temperature < 0)
    OLED_ShowString(30, 18, (u8 *)"-", 12);
    OLED_ShowNum(36, 18, temperature_abs / 10U, 2, 12);
    OLED_ShowString(48, 18, (u8 *)".", 12);
    OLED_ShowNum(54, 18, temperature_abs % 10U, 1, 12);
    OLED_ShowString(60, 18, (u8 *)" C", 12);

    OLED_ShowString(0, 32, (u8 *)"Press:", 12);
    OLED_ShowNum(36, 32, pressure_hpa, 4, 12);
    OLED_ShowString(60, 32, (u8 *)".", 12);
    OLED_ShowNum(66, 32, data->pressure % 100U, 2, 12);
    OLED_ShowString(78, 32, (u8 *)" hPa", 12);

    OLED_ShowString(0, 46, (u8 *)"Alt:", 12);
    OLED_ShowSignedNum(30, 46, data->altitude, 5, 12);
    OLED_ShowString(60, 46, (u8 *)" m", 12);

    OLED_Refresh();
    }

    static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 len, u8 size)
    {
    u32 magnitude;

    if(value < 0)
    {
    OLED_ShowString(x, y, (u8 *)"-", size);
    magnitude = (u32)(-value);
    }
    else
    {
    OLED_ShowString(x, y, (u8 *)" ", size);
    magnitude = (u32)value;
    }
    OLED_ShowNum((u8)(x + size / 2U), y, magnitude, (u8)(len – 1U), size);
    }

    十、常见故障排查

  • 屏幕提示 Sensor error 排查顺序:3.3V 供电 + 共地→SCL/SDA 引脚是否接反 PB6/PB7→模块上拉电阻→杜邦线接触→读取 ID 寄存器 0x55;
  • 气压正常,海拔偏差极大 原因:基准海平面气压固定 101325Pa,更换本地真实气压校准 P0 即可;
  • 温压数据跳动明显 OSS=0 高速模式无滤波,可提升过采样等级 OSS 或多次采样均值滤波;
  • 数值漂移、温度不准 传感器不要紧贴发热元件;进气孔不要封堵,避免气流直吹。
  • 赞(0)
    未经允许不得转载:171主机测评 » STM32F103+BMP180 气压温度传感器 + OLED 完整教程|软件 I2C 驱动、补偿算法、海拔计算全解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址