基于STM32的BMP180气压传感器详解
BMP180是一款高精度的气压传感器,具有低功耗、低噪声等特点,广泛使用在气压、海拔测量。其内部附带温度传感器,可对气压测量值进行补偿,使用I2C通信方式。此次用到的器件有:STM32最小系统板、BMP180气压传感器模块。根据数据手册,BMP180有以下几个关键特性,我把它整理成表格,一目了然:注意:BMP180的I2C器件地址是固定的。写入数据时地址是 0xEE,读取数据时地址是 0xEF。这
若该文为原创文章,转载请注明原文出处。
一、简介
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 数值(分别对应温度和气压)。最后,你需要用这个原始值,结合之前读出的校准系数,通过芯片厂商提供的一串公式进行计算,才能得到真实的温度和气压值。
这个过程可以总结为三步:
- 读校准系数:从特定地址读出11个校准值,每个传感器都不一样。
- 触发测量并读原始值:命令传感器开始转换,等待完成后读取原始的ADC结果(UT代表温度原始值,UP代表气压原始值)。
- 用公式计算:将原始值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,如有修改引脚需要修改初始化。
经测试,现在读取值。

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



所有评论(0)