若该文为原创文章,转载请注明原文出处。

一、简介


BMP180是一款高精度的气压传感器,具有低功耗、低噪声等特点,广泛使用在气压、海拔测量。其内部附带温度传感器,可对气压测量值进行补偿,使用I2C通信方式。此次用到的器件有:STM32最小系统板、BMP180气压传感器模块。

二、BMP180传感器模块简介


根据数据手册,BMP180有以下几个关键特性,我把它整理成表格,一目了然:


注意:BMP180的I2C器件地址是固定的。写入数据时地址是 0xEE,读取数据时地址是 0xEF。这个地址在代码里会用到,千万别写错了。

三、硬件连接(stm32f103c8t6最小系统板)


stm32f103c8t6开发板主频是72MHz,使用8MHz晶振。我们需要把BMP180模块和开发板用杜邦线连接起来。

连接非常简单,只有四根线:

VCC -> 开发板的 3.3V 引脚
GND -> 开发板的 GND 引脚
SDA -> 开发板的 PB1 引脚
SCL -> 开发板的 PB0 引脚


为什么选PB0和PB1?

其实你可以选任意两个GPIO引脚,因为我们的驱动用的是“模拟I2C”,也就是用软件控制GPIO电平变化来模拟I2C的时序,不依赖芯片硬件I2C外设。这样移植起来更灵活,但时序要靠我们自己写代码保证。

四、原理

BMP180如何输出真实数据
这里有个关键点,也是很多新手会困惑的地方:你不能直接从BMP180里读到“25.3°C”或者“101325Pa”这样的最终数值。

BMP180内部有一个EEPROM,存储了11个独特的“校准系数”(比如AC1, AC2, B1, B2等)。每次上电后,单片机必须先读出这11个系数。然后,当你需要测量时,BMP180会给你一个原始的、未经处理的ADC 数值(分别对应温度和气压)。最后,你需要用这个原始值,结合之前读出的校准系数,通过芯片厂商提供的一串公式进行计算,才能得到真实的温度和气压值。

这个过程可以总结为三步:

  1. 读校准系数:从特定地址读出11个校准值,每个传感器都不一样。
  2. 触发测量并读原始值:命令传感器开始转换,等待完成后读取原始的ADC结果(UT代表温度原始值,UP代表气压原始值)。
  3. 用公式计算:将原始值UT、UP和校准系数代入公式,算出最终的温度和气压。
     

五、代码

1、bmp180.h

#ifndef __BMP180_H__
#define __BMP180_H__

#include "stm32f10x.h"

// 软件IIC引脚定义(可根据实际硬件修改)
#define BMP180_SCL_PORT      GPIOB
#define BMP180_SCL_PIN       GPIO_Pin_0

#define BMP180_SDA_PORT      GPIOB
#define BMP180_SDA_PIN       GPIO_Pin_1

// 过采样率设置(0~3)
#define BMP180_OSS           0

typedef struct
{
    int16_t   AC1;
    int16_t   AC2;
    int16_t   AC3;
    uint16_t  AC4;
    uint16_t  AC5;
    uint16_t  AC6;
    int16_t   B1;
    int16_t   B2;
    int16_t   MB;
    int16_t   MC;
    int16_t   MD;
    int32_t   UT;          // 未修正温度
    int32_t   UP;          // 未修正气压
    int32_t   X1;
    int32_t   X2;
    int32_t   X3;
    int32_t   B5;
    int32_t   Temperature; // 温度值(实际值 = Temperature/10,小数部分 = Temperature%10)
    int32_t   B6;
    int32_t   B3;
    uint32_t  B4;
    int32_t   B7;
    int32_t   P;           // 气压值(Pa)
    float     Altitude;    // 海拔高度(m)
} Initialize_BMP180;

extern Initialize_BMP180 Calibration_BMP180;

// 用户接口函数
void BMP180_Init(void);                // 初始化引脚并读取校准参数
void BMP180_Result(void);               // 更新温度、气压和海拔数据

#endif

2、bmp180.c

#include "bmp180.h"
#include "delay.h"
#include "usart.h"    // 若需打印调试信息,可保留;否则可删除
#include "math.h"

Initialize_BMP180 Calibration_BMP180;

/*------------------ 软件IIC底层函数(静态,BMP180专用)------------------*/

/**
 * @brief 初始化软件IIC引脚
 */
static void BMP180_I2C_InitPins(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // SCL 推挽输出
    GPIO_InitStructure.GPIO_Pin = BMP180_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BMP180_SCL_PORT, &GPIO_InitStructure);
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);

    // SDA 推挽输出(初始高)
    GPIO_InitStructure.GPIO_Pin = BMP180_SDA_PIN;
    GPIO_Init(BMP180_SDA_PORT, &GPIO_InitStructure);
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
}

/**
 * @brief SDA设置为输出模式
 */
static void BMP180_I2C_SDA_Out(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = BMP180_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BMP180_SDA_PORT, &GPIO_InitStructure);
}

/**
 * @brief SDA设置为输入模式(浮空输入)
 */
static void BMP180_I2C_SDA_In(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = BMP180_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(BMP180_SDA_PORT, &GPIO_InitStructure);
}

/**
 * @brief IIC起始信号
 */
static void BMP180_I2C_Start(void)
{
    BMP180_I2C_SDA_Out();
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
    GPIO_ResetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
}

/**
 * @brief IIC停止信号
 */
static void BMP180_I2C_Stop(void)
{
    BMP180_I2C_SDA_Out();
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    GPIO_ResetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
}

/**
 * @brief 等待从机应答
 * @retval 0:收到应答;1:超时无应答
 */
static uint8_t BMP180_I2C_WaitAck(void)
{
    uint8_t errorTime = 0;

    BMP180_I2C_SDA_In();
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);

    while (GPIO_ReadInputDataBit(BMP180_SDA_PORT, BMP180_SDA_PIN)) {
        errorTime++;
        if (errorTime > 250) {
            BMP180_I2C_Stop();
            return 1;
        }
    }

    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
    return 0;
}

/**
 * @brief 主机发送应答信号(ACK)
 */
static void BMP180_I2C_Ack(void)
{
    BMP180_I2C_SDA_Out();
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    GPIO_ResetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
}

/**
 * @brief 主机发送非应答信号(NACK)
 */
static void BMP180_I2C_NAck(void)
{
    BMP180_I2C_SDA_Out();
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
    delay_us(3);
    GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);
}

/**
 * @brief 发送一个字节(高位在前)
 * @param data 要发送的字节
 */
static void BMP180_I2C_SendByte(uint8_t data)
{
    uint8_t t;

    BMP180_I2C_SDA_Out();
    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);

    for (t = 0; t < 8; t++) {
        if (data & 0x80)
            GPIO_SetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);
        else
            GPIO_ResetBits(BMP180_SDA_PORT, BMP180_SDA_PIN);

        data <<= 1;
        delay_us(3);
        GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
        delay_us(3);
        GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
        delay_us(3);
    }
}

/**
 * @brief 读取一个字节
 * @param ack 1:读取后发送ACK(继续读下一字节);0:读取后发送NACK(结束读)
 * @return 读到的字节
 */
static uint8_t BMP180_I2C_ReadByte(uint8_t ack)
{
    uint8_t i, receive = 0;

    BMP180_I2C_SDA_In();

    for (i = 0; i < 8; i++) {
        receive <<= 1;
        GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
        delay_us(3);
        GPIO_SetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
        if (GPIO_ReadInputDataBit(BMP180_SDA_PORT, BMP180_SDA_PIN))
            receive++;
        delay_us(3);
    }

    if (ack)
        BMP180_I2C_Ack();      // 发送ACK,准备读下一字节
    else
        BMP180_I2C_NAck();     // 发送NACK,结束本次读

    GPIO_ResetBits(BMP180_SCL_PORT, BMP180_SCL_PIN);
    delay_us(3);

    return receive;
}

/*------------------ BMP180专用读写函数 ------------------*/

/**
 * @brief 从BMP180指定寄存器读取一个字节(用于调试,一般用不到)
 * @param Add 寄存器地址
 * @return 读到的数据
 */
uint8_t BMP180_Read(uint8_t Add)
{
    uint8_t temp;

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEE);   // 写地址
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(Add);
    BMP180_I2C_WaitAck();
    BMP180_I2C_Stop();

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEF);   // 读地址
    BMP180_I2C_WaitAck();
    temp = BMP180_I2C_ReadByte(0);
    BMP180_I2C_Stop();

    return temp;
}

/**
 * @brief 从BMP180读取16位无符号整数(大端)
 * @param Add 起始寄存器地址
 * @return 16位数据
 */
static uint16_t BMP180_u16_Read(uint8_t Add)
{
    uint16_t temp;

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEE);
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(Add);
    BMP180_I2C_WaitAck();
    BMP180_I2C_Stop();

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEF);
    BMP180_I2C_WaitAck();
    temp = BMP180_I2C_ReadByte(1);   // 第一个字节后发ACK
    temp <<= 8;
    temp |= BMP180_I2C_ReadByte(0);  // 最后一个字节后发NACK
    BMP180_I2C_Stop();

    return temp;
}

/**
 * @brief 从BMP180读取16位有符号整数
 * @param Add 起始寄存器地址
 * @return 16位数据
 */
static int16_t BMP180_int16_t_Read(uint8_t Add)
{
    return (int16_t)BMP180_u16_Read(Add);
}

/**
 * @brief 读取未修正的温度值
 * @return UT
 */
static int32_t BMP180_Read_UT(void)
{
    int32_t temp_T;

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEE);
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(0xF4);
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(0x2E);   // 启动温度测量
    BMP180_I2C_WaitAck();
    BMP180_I2C_Stop();

    delay_ms(5);                  // 等待转换完成

    temp_T = (int32_t)BMP180_int16_t_Read(0xF6);
    return temp_T;
}

/**
 * @brief 读取未修正的气压值
 * @return UP
 */
static int32_t BMP180_Read_UP(void)
{
    int32_t temp_P;

    BMP180_I2C_Start();
    BMP180_I2C_SendByte(0xEE);
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(0xF4);
    BMP180_I2C_WaitAck();
    BMP180_I2C_SendByte(0x34);   // 启动气压测量(oss=0)
    BMP180_I2C_WaitAck();
    BMP180_I2C_Stop();

    delay_ms(5);                  // 等待转换完成

    temp_P = (int32_t)BMP180_int16_t_Read(0xF6);
    temp_P &= 0x0000FFFF;
    return temp_P;
}

/*------------------ 用户接口函数 ------------------*/

/**
 * @brief 初始化BMP180:初始化I2C引脚并读取校准参数
 */
void BMP180_Init(void)
{
    BMP180_I2C_InitPins();

    Calibration_BMP180.AC1 = BMP180_int16_t_Read(0xAA);
    Calibration_BMP180.AC2 = BMP180_int16_t_Read(0xAC);
    Calibration_BMP180.AC3 = BMP180_int16_t_Read(0xAE);
    Calibration_BMP180.AC4 = BMP180_u16_Read(0xB0);
    Calibration_BMP180.AC5 = BMP180_u16_Read(0xB2);
    Calibration_BMP180.AC6 = BMP180_u16_Read(0xB4);
    Calibration_BMP180.B1  = BMP180_int16_t_Read(0xB6);
    Calibration_BMP180.B2  = BMP180_int16_t_Read(0xB8);
    Calibration_BMP180.MB  = BMP180_int16_t_Read(0xBA);
    Calibration_BMP180.MC  = BMP180_int16_t_Read(0xBC);
    Calibration_BMP180.MD  = BMP180_int16_t_Read(0xBE);

    // 可选调试打印
    // printf("AC1 = %d\r\n", Calibration_BMP180.AC1);
    // ...
}

/**
 * @brief 更新温度、气压和海拔数据,结果存入Calibration_BMP180结构体
 */
void BMP180_Result(void)
{
    Calibration_BMP180.UT = BMP180_Read_UT();
    Calibration_BMP180.UP = BMP180_Read_UP();

    // 计算温度
    Calibration_BMP180.X1 = ((Calibration_BMP180.UT - Calibration_BMP180.AC6) * Calibration_BMP180.AC5) >> 15;
    Calibration_BMP180.X2 = (((int32_t)Calibration_BMP180.MC) << 11) / (Calibration_BMP180.X1 + Calibration_BMP180.MD);
    Calibration_BMP180.B5 = Calibration_BMP180.X1 + Calibration_BMP180.X2;
    Calibration_BMP180.Temperature = (Calibration_BMP180.B5 + 8) >> 4;

    // 计算气压
    Calibration_BMP180.B6 = Calibration_BMP180.B5 - 4000;
    Calibration_BMP180.X1 = (((int32_t)Calibration_BMP180.B2) * (Calibration_BMP180.B6 * Calibration_BMP180.B6 >> 12)) >> 11;
    Calibration_BMP180.X2 = (((int32_t)Calibration_BMP180.AC2) * Calibration_BMP180.B6) >> 11;
    Calibration_BMP180.X3 = Calibration_BMP180.X1 + Calibration_BMP180.X2;
    Calibration_BMP180.B3 = (((((int32_t)Calibration_BMP180.AC1) * 4 + Calibration_BMP180.X3) << BMP180_OSS) + 2) / 4;
    Calibration_BMP180.X1 = ((int32_t)Calibration_BMP180.AC3) * Calibration_BMP180.B6 >> 13;
    Calibration_BMP180.X2 = (((int32_t)Calibration_BMP180.B1) * (Calibration_BMP180.B6 * Calibration_BMP180.B6 >> 12)) >> 16;
    Calibration_BMP180.X3 = ((Calibration_BMP180.X1 + Calibration_BMP180.X2) + 2) >> 2;
    Calibration_BMP180.B4 = ((int32_t)Calibration_BMP180.AC4) * (uint32_t)(Calibration_BMP180.X3 + 32768) >> 15;
    Calibration_BMP180.B7 = ((uint32_t)Calibration_BMP180.UP - Calibration_BMP180.B3) * (50000 >> BMP180_OSS);

    if (Calibration_BMP180.B7 < 0x80000000)
        Calibration_BMP180.P = (Calibration_BMP180.B7 * 2) / Calibration_BMP180.B4;
    else
        Calibration_BMP180.P = (Calibration_BMP180.B7 / Calibration_BMP180.B4) * 2;

    Calibration_BMP180.X1 = (Calibration_BMP180.P >> 8) * (Calibration_BMP180.P >> 8);
    Calibration_BMP180.X1 = (((int32_t)Calibration_BMP180.X1) * 3038) >> 16;
    Calibration_BMP180.X2 = (-7375 * Calibration_BMP180.P) >> 16;
    Calibration_BMP180.P = Calibration_BMP180.P + ((Calibration_BMP180.X1 + Calibration_BMP180.X2 + 3791) >> 4) - 390; // -390 经验修正

    // 计算海拔
    Calibration_BMP180.Altitude = 44330.0f * (1.0f - powf(Calibration_BMP180.P / 101325.0f, 1.0f / 5.255f));
}

使用的是GPIO模拟IIC,如有修改引脚需要修改初始化。

经测试,现在读取值。

如有侵权,或需要完整代码,请及时联系博主。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐