历史上的今天
返回首页

历史上的今天

今天是:2024年09月07日(星期六)

2020年09月07日 | BMP180测量海拔高度传感器单片机程序

2020-09-07 来源:51hei

在测量海拔高度时,传统的做法是通过测量某一高度的大气压力,再经过换算才能得到高度数据。为了测量大气压力,就得用上气压传感器,下面就来讨论一下气压传感器的应用。


气压传感器是压力传感器中的一种,它专用于测量气体的绝对压强。目前市场上能见到的气压传感器有很多种,下面就以市场上常见的Bosch公司推出的BMP180来进行讨论。BMP180不仅可以实时的测量大气压力,还能测量实时温度。同时它还具有IIC总线的接口,便于单片机进行访问。另外它的使用也很方便,不需要太多的操作就可读取到气压及测量数据。


BMP180采用强大的8脚陶瓷无引线芯片承载(LCC)超薄封装,它性能卓越,内置有校准补偿,绝对精度最低可以达到0.03hPa(0.25米),并且耗电极低,只有3μA。气压测量范围从300hPa到1100hPa,换算成高度为海拔9000米到500米。下图是其封装外形和引脚排列。

      

引脚各功能如下:1脚(GND)接电源地,2脚(EOC)为完成转换输出,3脚(VDDA)为正电源,4脚(VDDD)为数字正电源,5脚为空,6脚(SCL)为IIC的时钟端,7脚(SDA)为IIC的数据端,8脚(XCLR)为主清除信号输入端,低电平有效,用来复位BMP180和初始化寄存器和控制器,在不用的情况下可以空置。


BMP180的工作电压为1.8V~3.6V,典型工作电压为2.5V,其与单片机相连的典型电路如下图所示。

从上图中可以看到,BMP180内包含有电阻式压力传感器、AD转换器和控制单元,其中控制单元包括了EEPROM和IIC接口。读取BMP180时会直接传送没有经过补偿的温度值和压力值。而在EEPROM中则储存了176位单独的校准数据,这些数据将对读取的温度压力值进行补偿。176位的EEPROM被划分为11个字,每个字16位,这样就包含有11个校准系数。每个器件模块都有自己单独的校准系数,在第一次计算温度压力数据之前,单片机就应该先读出读出EEPROM中的这些校准数据,然后再开始采集数据温度和压力数据。


和所有的IIC总线器件一样,BMP180也有一个器件的固定地址,根据其数据手册,出厂时默认BMP180的从机地址为0xEE(写入方向),或0xEF(读出方向)。温度数据UT和压力数据UP都存储在寄存器的第0到15位之中,压力数据UP的精度还可扩展至16~19位。


  

上图中左边是Bosch公司技术手册上提供的读取顺序的流程图,右边是EEPROM中的校准数据。


从流程图中可以看出,单片机发送开始信号启动温度和压力测量,经过一定的转换时间(4.5ms)后,从IIC接口读出结果。为了将温度的单位换算成℃和将压力的单位换算成hPa,需要用到EEPROM中的校准数据来进行补偿计算,这些数据也可以从IIC接口读出。事实上,EEPROM中的这些校准数据应该在程序初始化的时候就读出,以方便后面的计算。 在同一个采样周期中BMP180可以采128次压力值和1次温度值,并且这些值在读取后会被及时更新掉。若不想等待到最大转化时间之后才读取数据,可以有效利用BMP180的输出管脚EOC来检查转化是否完毕。若为1表示转换完成,为0表示转换正在进行中。


要得到温度或气压的值,必须要访问地址为0xF4的控制寄存器。它根据写入数据的不同,回应的值也不一样,具体如下表所示。

从图中可以看出,要获得温度数据,必须先向控制寄存器(地址0xF4)写0x2E,然后等待至少4.5ms,才可以从地址0xF6和0xF7读取十六位的温度数据。同样,要获得气压数据,必须先向控制寄存器(地址0xF4)写0x34,然后等待至少4.5ms,才可以从地址0xF6和0xF7读取16位的气压数据,若要扩展分辨率,还可继续读取0xF8(XLSB)扩展16位数据到19位。获取到的数据还要根据EEPROM中的校准数据来进行补偿后才能用,EEPROM的数据读取可根据上图中的地址来进行,地址从0xAA~0xBF,具体的补偿算法可参看官方的数据手册,这里就不赘述了。


下面以一个例子来看一下BMP180的具体应用。


例子:利用单片机读取来自BMP180的温度和气压数据,并把它们通过LCD1602显示出来。


BMP180的SDA、SCL端分别接到ATMega16的TWI端(PC1、PC0),EOC和XCLR端悬空,LCD1602的接法与前面的一致。参考代码如下。


#include

//=========================定义从器件地址和读写方式=============================

#define rd_device_add 0xef //即11101111,1110111是BMP180器件的固定地址,最后的1表示对从器件进行读操作

#define wr_device_add 0xee //即11101110,1110111是BMP180器件的固定地址,最后的0表示对从器件时行写操作

//===============================TWI状态定义==================================

#define START 0x08

#define RE_START  0x10

#define MT_SLA_ACK  0x18

#define MT_SLA_NOACK  0x20

#define MT_DATA_ACK 0x28

#define MT_DATA_NOACK 0x30

#define MR_SLA_ACK  0x40

#define MR_SLA_NOACK  0x48

#define MR_DATA_ACK 0x50

#define MR_DATA_NOACK 0x58

//=============================常用TWI操作定义================================

#define Start() (TWCR=(1<#define Stop()  (TWCR=(1<#define Wait()  {while(!(TWCR&(1<#define TestAck() (TWSR&0xf8)

#define SetAck()  (TWCR|=(1<#define SetNoAck()  (TWCR&=~(1<#define Twi() (TWCR=(1<#define Write8Bit(x)  {TWDR=(x);TWCR=(1<//============引脚电平的宏定义===============

#define LCM_RS_1 PORTB_Bit0=1     //RS脚输出高电平

#define LCM_RS_0 PORTB_Bit0=0       //RS脚输出低电平

#define LCM_RW_1 PORTB_Bit1=1       //RW脚输出高电平

#define LCM_RW_0 PORTB_Bit1=0       //RW脚输出低电平

#define LCM_EN_1 PORTB_Bit2=1      //EN脚输出高电平  

#define LCM_EN_0 PORTB_Bit2=0       //EN脚输出低电平

#define DataPort PORTA          //PORTA为数据端口

#define Busy 0x80         //忙信号

//==============定义全局变量================

unsigned char ge,shi,bai,qian,wan,shiwan;    //显示变量

unsigned char ReadTemp[2];                   //接收到的温度数据缓冲区

unsigned char ReadPressure[2];              //接收到的气压数据缓冲区

int ac1;

int ac2;

int ac3;

unsigned int ac4;

unsigned int ac5;

unsigned int ac6;

int b1;

int b2;

int mb;

int mc;

int md;

//==============定义显示字符串================

const unsigned char str0[]={"  T:   .   C    "};    //显示温度

const unsigned char str1[]={"  P:   .   Kpa  "};    //显示气压

//===============1mS延时===================

void delay_1ms(void)  

{   

  unsigned int i;

  for(i=1;i<(unsigned int)(8*143-2);i++)

    ;

}

//=============n*1mS延时===============

void delay_nms(unsigned int n)  

{

  unsigned int i=0;

  while(i   {delay_1ms();

    i++;

   }

}

//===============IIC总线写n个字节(成功返回0,失败返回1)=====================

unsigned char I2C_Write(unsigned char RomAddress,unsigned char *buf,unsigned char len)

{

  unsigned char i;

  Start();                                                  //启动I2C总线

  Wait();                                                   //等待回应

  if(TestAck()!=START)                        

    return 1;                                              //若回应的不是启动信号,则失败返回1

  Write8Bit(wr_device_add);                    //写I2C从器件地址、写方向

  Wait();                                                   //等待回应

  if(TestAck()!=MT_SLA_ACK)                 

    return 1;                                               //若回应的不是ACK信号,则失败返回值1                                

  Write8Bit(RomAddress);                         //写BMP180的ROM地址

  Wait();                                                     //等待回应

  if(TestAck()!=MT_DATA_ACK)

    return 1;                                                 //若回应的不是ACK信号则失败返回值1

  for(i=0;i    {

      Write8Bit(buf[i]);                          //写数据到BMP180的ROM中

      Wait();                                                      //等待回应

      if(TestAck()!=MT_DATA_ACK)

        {return 1;}                                     //若回应的不是ACK信号则失败返回值1

      delay_nms(10);

    }

  Stop();                                                      //停止I2C总线

  delay_nms(10);                                          //延时等待BMP180写完

  return 0;                                                   //写入成功,返回值0

}

//====================IIC总线读n个字节(成功返回0,失败返回1)=========================

unsigned char I2C_Read(unsigned char RomAddress,unsigned char *buf,unsigned char len)

{

  unsigned char i;

  Start();                                                     //启动I2C总线

  Wait();                                                      //等待回应

  if(TestAck()!=START)

    return 1;                                                  //若回应的不是启动信号,则失败返回1

  Write8Bit(wr_device_add);                        //写I2C从器件地址、写方向

  Wait();                                                       //等待回应

  if(TestAck()!=MT_SLA_ACK)

    return 1;                                                   //若回应的不是ACK信号,则失败返回值1

  Write8Bit(RomAddress);                             //写BMP180的ROM地址

  Wait();                                                         //等待回应

  if(TestAck()!=MT_DATA_ACK)

    return 1;                                                     //若回应的不是ACK信号,则失败返回值1

推荐阅读

史海拾趣

Hexawave公司的发展小趣事

当涉及到采用算术逻辑单元(ALU, Arithmetic Logic Unit)组成的二进制加减电路时,网友们可能会提出多种问题,这里列举一些常见问题及其解答:

1. ALU 如何实现二进制加法?

回答
在二进制加法中,ALU 使用全加器(Full Adder)或一系列的全加器(对于多位二进制数)来实现。每个全加器接受三个输入:两个加数位(A 和 B)以及一个来自低位的进位(Cin)。它产生两个输出:一个和位(Sum)和一个向高位的进位(Cout)。通过级联多个全加器,可以完成多位二进制数的加法。

2. ALU 如何实现二进制减法?

回答
二进制减法可以通过加法来实现,利用“补码”的概念。具体地,将减数取反加一(即求其二进制补码),然后将该补码与被减数相加。结果的正负由最高位(符号位)决定,其余位表示数值大小。ALU 内部可以包含专门的电路来处理这种补码加法,从而间接实现减法。

3. ALU 如何处理进位和借位?

回答
在加法中,进位(Carry)是从低位向高位传递的,每个全加器都会输出一个进位信号给下一个高位的全加器。在减法(通过补码加法实现)中,由于使用了加法器,进位的概念仍然适用,但在某些情况下,它可能被视为“借位”的相反操作,尤其是在直观理解减法过程时。不过,从电路设计的角度来看,ALU 内部处理的是加法操作,包括进位。

4. ALU 如何支持更复杂的算术运算,如乘法和除法?

回答
ALU 通常支持基本的算术运算(加、减)和逻辑运算。对于乘法和除法,ALU 可能不支持直接计算,或者仅支持部分乘法和除法的简化版本(如移位操作,可以视为乘以2的幂或除以2的幂的简化形式)。复杂的乘法和除法运算通常需要额外的硬件单元(如乘法器和除法器)来执行,这些单元可能作为ALU的补充或与之并行工作。

5. ALU 的设计如何影响计算机的性能?

回答
ALU 的设计对计算机的性能有直接影响。更快的ALU 能够更快地完成算术和逻辑运算,从而提高整个计算机的处理速度。此外,ALU 的指令集和设计的灵活性也会影响其能够执行的操作种类和效率。现代CPU中的ALU通常非常高效且灵活,能够执行多种复杂的算术和逻辑操作,以满足现代应用程序的需求。

Datapro International Inc公司的发展小趣事

Datapro International Inc公司成立于20世纪90年代初,当时正值电子信息技术飞速发展的时期。公司的创始人是一群充满激情和创新精神的年轻工程师,他们看到了在数据存储和处理方面的巨大潜力。于是,他们决定成立Datapro International Inc公司,致力于开发高效、稳定的数据存储解决方案。

在创立初期,Datapro International Inc公司面临着重重困难,包括资金短缺、技术挑战和市场竞争等。然而,他们凭借坚定的信念和不懈的努力,成功开发出了第一款具有创新性的数据存储产品,该产品凭借其卓越的性能和稳定性迅速赢得了市场的认可。这一成功为Datapro International Inc公司的发展奠定了坚实的基础。

Ambersil公司的发展小趣事

随着电子行业的竞争日益激烈,Ambersil公司意识到,要想在市场中立足,必须注重产品质量。公司加大了对生产线的投入,引进了先进的生产设备和技术,严格把控产品质量。同时,公司还建立了完善的售后服务体系,及时解决客户在使用过程中遇到的问题。这些举措使得Ambersil公司的产品在市场上赢得了良好的口碑。

Custom Electronics Inc公司的发展小趣事

在电子行业的早期阶段,CEI凭借其卓越的研发团队和创新能力,成功开发出一款具有革命性的新型电容器。这款电容器以其高效能、长寿命和稳定性强的特点,迅速在市场中获得认可。CEI借此机会,进一步扩大了生产规模,提高了产品质量,逐渐确立了其在电子行业中的领先地位。

General Magnetics Inc公司的发展小趣事
可能由于输入电压波动、负载变化或内部元件老化等原因导致。
Fairchild (ON Semiconductor)公司的发展小趣事

2016年,Fairchild Semiconductor被ON Semiconductor收购。这一收购标志着Fairchild Semiconductor的发展进入了一个新的阶段。ON Semiconductor是一家全球领先的半导体公司,拥有广泛的产品线和强大的技术实力。通过整合Fairchild Semiconductor的技术和资源,ON Semiconductor进一步巩固了其在半导体行业中的领导地位,并继续为全球客户提供优质的半导体产品和服务。

问答坊 | AI 解惑

单片机最小系统

本帖最后由 paulhyde 于 2014-9-15 09:20 编辑 一般竞赛都用什么单片机最小系统?51单片机还是msp430?51是at89s51还是89c51?两个有什么区别吗?  …

查看全部问答>

关于串口驱动

个人理解 微软显示串口驱动的MDD层 自己要实现PDD层 今天在PB5.0下看到这个 D:\\WINCE500\\PUBLIC\\COMMON\\OAK\\DRIVERS\\SERIAL中有一个SERPDDCM文件夹 看名字好像是串口驱动的PDD 想问下这是个什么东西 如果是PDD 那写串口驱动还需要做些什么 ...…

查看全部问答>

请问NK调入内存并开始执行后,为何还要读FLASH?

如下所示,EBOOT已经把NK调度到内存,并且已经跳到NK去执行。为什么NK还要初始化FLASH,并且要花十几秒的时间去读FLASH? Windows CE Kernel for ARM (Thumb Enabled) Built on Feb  8 2007 at 23:36:51 ProcessorType=0920 &nbs ...…

查看全部问答>

LIST BOX显示数据的问题

{          // TODO: 在此添加控件通知处理程序代码          int nEngineType;          int OpenMode;           ...…

查看全部问答>

关于驱动安装成功后出现的未知设备问题

目前开发完一个针对philips saa7130的视频采集驱动,安装完后,系统提示出现“未知设备”,但卡的驱动已经成功装上。分析后发现“未知设备”是saa7130芯片支持的TV tuner,但目前我们的用户不需要该功能,所以想在装上驱动后,不再出现提示用户安装 ...…

查看全部问答>

单片机学习方面的问题!

我想学单片机,现在准备买设备。大家帮忙推荐几个好点的东西。 编程器我想买多功能编程器,不知道有哪些?还有其他的配件大家也帮帮忙推荐一下,我现在对这方面还不懂。 我学过c,8086汇编,模拟电路,数字电路 微机原理 verilog等课程。欢迎大 ...…

查看全部问答>

ucos-ii任务划分设计

比较详尽的介绍如何对基于ucos-ii的系统中进行任务的划分…

查看全部问答>

俺的C习题(7)——这其实也是个没啥技术含量的

如题,没啥技术含量,不过,我记得我最初对这个题目很是蛋疼。为何?全在于理解上出了问题。我以为,这个题的奥义是要实现在一组元素上任意顺序的四则运算符。不过这也不难,当时我考虑的方法是。做一个和数据元素一样长或者小一个单位的数组,用来 ...…

查看全部问答>

LPC9221 io口设置与使用

我有个nxp p89lpc9221芯片,用来控制7段数码管显示,用ICP口编程。 我现在设置p0.0,p0.1,p0.2为开漏输出;P0M1 |= 0x07;   P0M2 |= 0x07; 三个脚都上拉到3.3v; 然后设置三个脚输出为0: sbit p00 = P0 ^ 0; sbit p01 = P0 ^ 1; sbit ...…

查看全部问答>

帮看看串口通信用不了

//用的是STM32F107VCT  25MHz的外部晶振 //固件库是V3.5的 //想实现串口通信(USART2),却怎么也发不出去数据,求看看是哪里出错了,我新手。。。谢谢啦! #include \"stm32f10x.h\" void RCC_Configuration(void); void GPIO_ ...…

查看全部问答>