目录
1、芯片介绍
BMP180是Bosch Sensortec的一种高精度数字气压和温度传感器,使用BMP180可以测量环境温度、压力和高度。BMP180是超低功耗,低电压的电子元件,经过优化,具有高精度和高稳定性,适用于移动电话,PDA,GPS导航设备和户外设备。
它由压阻传感器、模数转换器,带E2PROM和串行I2C接口的控制单元组成。
详细内容参考芯片手册链接:https://pan.baidu.com/s/1LAcNy3YuQ4Y52nonb88ZVw
提取码:qmc8
2、传感器特点
BOSCH BMP180是测量压力和海拔高度最常用的传感器之一。该模块的特点如下:
压力测量范围为300至1100hPa(海拔9000米 ~ -500米)
芯片电压1.8V~3.6V(VDDA),1.62V~3.6V(VDD),模块的VIN输入5V(3.3V亦可)
高精度:低功耗模式下,分辨率为0.06hPa(0.5米),高线性模式下,分辨率为0.03hPa(0.25米)
低功耗(标准模式下为5μA)
内部温度传感器,精度为0.5°C
支持I2C协议进行通信
温度补偿,完全校准
3、测量流程
微控制器发送一个启动序列来启动压力或温度测量。转换时间后,可以通过I2C接口读取结果值(分别为UP或UT)。为了计算温度(°C)和压力(hPa),必须使用校准数据。这些校准数据可以在软件初始化时通过I2C接口从BMP180 E2PROM读出。
4、校准系数
176位的E2PROM被分成11个字,每个字16位。这些包含11个校准系数。每个传感器模块都有单独的系数。在第一次计算温度和压力之前,主机读出E2PROM数据。
可以通过检查没有一个字的值为0或0xffff。来检查数据通信是否正常。正常情况下校准系数在E2PROM的地址如下所示:
官方提供了使用校准系数来计算压力的算法如下所示:
5、高度计算
p是测量气压,海平面气压p0标准为1013.25hPa,以米为单位,高度计算使用国际气压公式计算海拔高度。
6、设备i2c通讯地址
BMP180模块地址如下图所示。设备地址的LSB区分读(1)和写(0)操作,对应地址0xEF(读)和0xEE(写)
7、启动温度和压力测量
开始测量温度值UT和压力值UP的时序图如下所示。启动条件后,主机发送设备地址写入、寄存器地址和控制寄存器数据。BMP180在接收到数据时,每8个数据位发送一次ack (acknowledgement)。在最后一次ack之后,主机发送一个停止条件。
不同的控制寄存器数据代表这不同的压力测量精度和黄钻换时间,详细参考下表
8、读取A/D转换结果或E2PROM数据
读出温度数据字UT(16位)、压力数据字UP(16 ~ 19位)和E2PROM数据的步骤如下:
启动条件完成后,主控发送模块地址写命令和地址。寄存器地址可选择E2PROM校准系数数据寄存器0xAA ~ 0xBF,或者温度或压力值数据寄存器 0xF6 (MSB), 0xF7 (LSB)。
然后主机发送一个重启条件,随后是模块地址读取,BMP180将确认(ACKS)。BMP180首先发送8msb,由master (ACKM)确认,然后发送8lsb。主机发送一个(NACKM),最后发送一个停止条件。
9、i2c驱动代码
i2c.c
#include "user_includes.h"
/*
*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线延迟,最快400KHz
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
for(int i = 0;i < 100 ;i++)
{
__NOP();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参:_ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
if (i == 7)
{
I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参:无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(uint8_t ack)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
if(ack==0)
i2c_NAck();
else
i2c_Ack();
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参:无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t try_time = 100;//连接超时次数
I2C_SDA_1(); //CPU释放SDA总线
i2c_Delay();
I2C_SCL_1(); //CPU驱动SCL = 1, 此时器件会返回ACK应答
i2c_Delay();
while(I2C_SDA_READ())//等待SHT30应答
{
try_time--;
i2c_Delay();
if(try_time==0)//超时,无响应
{
I2C_SCL_0();
i2c_Delay();
return 1;
}
}
I2C_SCL_0();
i2c_Delay();
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_GPIO_Config
* 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_GPIO_Config(void)
{
//个人觉得程序初始化就是要通俗易懂,所以习惯用库函数进行
//但是像改变IIC引脚电平状态,由于需要频繁操作,所以最好还是使用寄存器操作
LL_GPIO_InitTypeDef GPIO_InitStructure;
LL_APB2_GRP1_EnableClock(IIC_CLOCK);
GPIO_InitStructure.Pin = IIC_SDA_PIN|IIC_SCL_PIN;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStructure.Mode = LL_GPIO_MODE_OUTPUT;//开漏输出
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
GPIO_InitStructure.Pull = LL_GPIO_PULL_UP;
LL_GPIO_Init(IIC_GPIO,&GPIO_InitStructure);
//pin_mode(WIFI_IO,
//pin_mode(WIFI_RES, PIN_MODE_OUTPUT_OPENDRAIN);PIN_MODE_OUTPUT_OPENDRAIN);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参:_Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
i2c_GPIO_Config(); /* 配置GPIO */
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendBye(_Address|I2C_WR);
ucAck = i2tc_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}
i2c.h
#ifndef __I2C_H
#define __I2C_H
#include "stm32f1xx_hal.h"
#include "stm32f1xx_ll_gpio.h"
#define IIC_CLOCK LL_APB2_GRP1_PERIPH_GPIOA
#define IIC_GPIO GPIOA
#define IIC_SCL_PIN LL_GPIO_PIN_8
#define IIC_SDA_PIN LL_GPIO_PIN_3
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define I2C_SCL_1() LL_GPIO_SetOutputPin(IIC_GPIO, IIC_SCL_PIN)
#define I2C_SCL_0() LL_GPIO_ResetOutputPin(IIC_GPIO, IIC_SCL_PIN)
#define I2C_SDA_1() LL_GPIO_SetOutputPin(IIC_GPIO, IIC_SDA_PIN)
#define I2C_SDA_0() LL_GPIO_ResetOutputPin(IIC_GPIO, IIC_SDA_PIN)
#define I2C_SDA_READ() LL_GPIO_IsInputPinSet(IIC_GPIO,IIC_SDA_PIN)
#define I2C_SCL_READ() LL_GPIO_IsInputPinSet(IIC_GPIO,IIC_SCL_PIN)
#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */
void i2c_Start(void);
void i2c_Stop(void);
void i2c_SendByte(uint8_t _ucByte);
uint8_t i2c_ReadByte(uint8_t ack);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
uint8_t i2c_CheckDevice(uint8_t _Address);
void i2c_GPIO_Config(void);
#endif
bmp180.c
#include "user_includes.h"
// 定义存取EEPROM校准值的变量
short ac1;
short ac2;
short ac3;
unsigned short ac4;
unsigned short ac5;
unsigned short ac6;
short b1;
short b2;
short mb;
short mc;
short md;
// 从气压传感器读取值
bool BMP180_Multiple_Read(uint16_t REG_Address, uint16_t *ReadData)
{
uint8_t REG_Data[2];
uint8_t status;
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_WR);//写7位I2C设备地址加0作为写取位,1为读取位
i2c_WaitAck();
i2c_SendByte(REG_Address);
i2c_WaitAck();
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_RD);//写7位I2C设备地址加0作为写取位,1为读取位
if(i2c_WaitAck()==0) {
REG_Data[0] = i2c_ReadByte(1);
REG_Data[1] = i2c_ReadByte(0);
i2c_Stop();
}
//status = I2C2_Read_NByte(BMP180_SlaveAddress, REG_Address, REG_Data, 2);
*ReadData = (uint16_t)REG_Data[0]<<8 | (uint16_t)REG_Data[1];
return status ;
}
// 读取温度值
bool BMP180_ReadTemp(uint16_t * temp)
{
uint8_t value = BMP_COVERT_TEMP;
uint8_t REG_Data[3];
uint8_t status;
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_WR);//写7位I2C设备地址加0作为写取位,1为读取位
i2c_WaitAck();
i2c_SendByte(0xF4);
i2c_WaitAck();
i2c_SendByte(BMP_COVERT_TEMP);
i2c_WaitAck();
i2c_Stop();
vTaskDelay(5);
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_WR);//写7位I2C设备地址加0作为写取位,1为读取位
i2c_WaitAck();
i2c_SendByte(BMP_OUT_MSB);
i2c_WaitAck();
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_RD);//写7位I2C设备地址加0作为写取位,1为读取位
if(i2c_WaitAck()==0) {
REG_Data[0] = i2c_ReadByte(1);
REG_Data[1] = i2c_ReadByte(0);
i2c_Stop();
*temp = ((uint32_t)REG_Data[0] <<8 | (uint32_t)REG_Data[1]);
return 0;
}
return 1;
}
// 读取气压值
bool BMP180_ReadPressure(uint32_t * pressure)
{
uint8_t status;
uint8_t REG_Data[3];
uint8_t value = BMP_COVERT_PRES;
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_WR);//写7位I2C设备地址加0作为写取位,1为读取位
i2c_WaitAck();
i2c_SendByte(0xF4);
i2c_WaitAck();
i2c_SendByte(BMP_COVERT_PRES);
i2c_WaitAck();
i2c_Stop();
vTaskDelay(OSS_TIME_MS);
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_WR);//写7位I2C设备地址加0作为写取位,1为读取位
i2c_WaitAck();
i2c_SendByte(BMP_OUT_MSB);
i2c_WaitAck();
i2c_Start();
i2c_SendByte(BMP180_SlaveAddress | I2C_RD);//写7位I2C设备地址加0作为写取位,1为读取位
if(i2c_WaitAck()==0) {
REG_Data[0] = i2c_ReadByte(1);
REG_Data[1] = i2c_ReadByte(1);
REG_Data[2] = i2c_ReadByte(0);
i2c_Stop();
*pressure = ((uint32_t)REG_Data[0] << 16 | (uint32_t)REG_Data[1] <<8 | (uint32_t)REG_Data[2]) >> (8 - OSS);
return 0;
}
return 1;
}
// 传感器初始化并读取EEPROM值
void BMP180_Init(void)
{
i2c_GPIO_Config();
BMP180_Multiple_Read( BMP_AC1_ADDR, (uint16_t *)&ac1 );
BMP180_Multiple_Read( BMP_AC2_ADDR, (uint16_t *)&ac2 );
BMP180_Multiple_Read( BMP_AC3_ADDR, (uint16_t *)&ac3 );
BMP180_Multiple_Read( BMP_AC4_ADDR, (uint16_t *)&ac4 );
BMP180_Multiple_Read( BMP_AC5_ADDR, (uint16_t *)&ac5 );
BMP180_Multiple_Read( BMP_AC6_ADDR, (uint16_t *)&ac6 );
BMP180_Multiple_Read( BMP_B1_ADDR, (uint16_t *)&b1 );
BMP180_Multiple_Read( BMP_B2_ADDR, (uint16_t *)&b2 );
BMP180_Multiple_Read( BMP_MB_ADDR, (uint16_t *)&mb );
BMP180_Multiple_Read( BMP_MC_ADDR, (uint16_t *)&mc );
BMP180_Multiple_Read( BMP_MD_ADDR, (uint16_t *)&md );
}
// 校准温度和气压
void BMP180Convert(float *temperature, long *pressure)
{
uint16_t ut;
uint32_t up;
bool temp_state , pressure_state;
long x1, x2, b5, b6, x3, b3, p;
unsigned long b4, b7;
temp_state = BMP180_ReadTemp(&ut);
pressure_state = BMP180_ReadPressure(&up);
if(temp_state == 0 && pressure_state == 0)
{
// 计算温度
x1 = ((long)ut - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
*temperature = ((b5 + 8) >> 4) * 0.1f;
// 计算气压
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = (ac2 * b6) >> 11;
x3 = x1 + x2;
b3 = ((((long)ac1 * 4 + x3) << OSS) + 2) >> 2;
x1 = (ac3 * b6) >> 13;
x2 = (b1 * (b6 * b6>> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15;
b7 = ((unsigned long) up - b3) * (50000 >> OSS);
if( b7 < 0x80000000)
p = (b7 * 2) / b4 ;
else
p = (b7 / b4) * 2;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
* pressure = p + ((x1 + x2 + 3791) >> 4);
}
}
// 计算海拔
void BMP180_Altitude(float *temperature, long *pressure, float *altitide)
{
BMP180Convert(temperature, pressure); // 计算出压强
*altitide = 44330*(1 - pow((*pressure)/ PRESSURE_OF_SEA, 1.0f / 5.255f));
}
bmp180.h
#ifndef _DRV_BMP180_H_
#define _DRV_BMP180_H_
#include "stm32f1xx.h"
#include <stdbool.h>
#include <math.h>
#define BMP180_SlaveAddress 0xEE // 定义器件在IIC总线中的从地址
#define BMP_AC1_ADDR 0xAA // 定义校准寄存器的地址
#define BMP_AC2_ADDR 0xAC
#define BMP_AC3_ADDR 0xAE
#define BMP_AC4_ADDR 0xB0
#define BMP_AC5_ADDR 0xB2
#define BMP_AC6_ADDR 0xB4
#define BMP_B1_ADDR 0xB6
#define BMP_B2_ADDR 0xB8
#define BMP_MB_ADDR 0xBA
#define BMP_MC_ADDR 0xBC
#define BMP_MD_ADDR 0xBE
#define CONTROL_REG_ADDR 0xF4 // 控制寄存器,在这个寄存器中设置不同的值可以设置不同转换时间,同时不同的值可以确认转换大气压或者温度
#define BMP_COVERT_TEMP 0x2E // 转换温度 4.5MS
#define BMP_COVERT_PRES_0 0x34 // 转换大气压 4.5ms
#define BMP_COVERT_PRES_1 0x74 // 转换大气压 7.5ms
#define BMP_COVERT_PRES_2 0xB4 // 转换大气压 13.5ms
#define BMP_COVERT_PRES_3 0xF4 // 转换大气压 25.5ms
#define BMP_OUT_MSB 0xF6 // ADC输出高8位
#define BMP_OUT_LSB 0xF7 // ADC输出低8位
#define BMP_OUT_XLSB 0xF8 // 19位测量时,ADC输出最低3位
#define OSS_TIME_MS 26 // 时间间隔
#define BMP_COVERT_PRES BMP_COVERT_PRES_3 // 大气压转换用时
#define OSS 3 // 大气压转换时间
#define PRESSURE_OF_SEA 101325.0f // 参考海平面压强
bool BMP180_Multiple_Read(uint16_t REG_Address , uint16_t *ReadData);
bool BMP180_ReadTemp(uint16_t * temp);
bool BMP180_ReadPressure(uint32_t * pressure);
void BMP180_Init(void);
void BMP180Convert(float *temperature, long *pressure);
void BMP180_Altitude(float *temperature, long *pressure, float *altitide);
#endif