BMP180芯片介绍与I2C驱动示例

目录

1、芯片介绍

2、传感器特点

3、测量流程

4、校准系数

5、高度计算

6、设备i2c通讯地址

7、启动温度和压力测量

8、读取A/D转换结果或E2PROM数据

9、i2c驱动代码


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
 

  • 17
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值