BMP085气压传感器驱动

   BMP085是新一代的小封装气压传感器,主要用于气压温度检测,在四轴飞行器上可以用作定高检测,该传感器属于IIC总线接口,依然沿用标准IIC驱动程序

   使用该传感器需要注意的是我们不能直接读出转换好的二进制温度数据或者气压数据,必须先读出一整套约176位的矫正数据,然后启动转换,将转换的数据与矫正数据一起进行矫正运算才能正常获得温度,温度精度为0.1,大气压有16bit或者19bit的精度,一般选择16位精度,

  所以对于该传感器设备,驱动模式如下

  1.获取矫正数据-->启动温度转换--->获取原始温度数据-->启动大气压转换-->获取原始大气压数据-->计算实际温度数据大气压数据

   另,转换时的转换时间也是计算时的重要参数,计算大气压时的过程中需要使用冤死温度


矫正数据如下


矫正数据都是16位有符号的,所以读出的时候需要连续读取两个字节

通过计算得到的大气压强可以计算得到当前传感器相对于海平面的绝对高度



具体驱动代码如下所示

#ifndef __BMP085_H_
#define __BMP085_H_
#include "ioremap.h"
#include "common.h"
#include "stm32f10x.h"
#include "uart.h"
#include "delay.h"

//BMP SDA PC0
//BMP SCL PC1

#define BMP_ACK_WAIT_TIME	200		//iic通讯时的ack等待时间

#define BMP085_DEBUG	1

#define OSS 0	// 大气压的转换时间,有0-3可选值

//地址均为读地址
#define BMP085_ADDR		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_TEMP_PRES_DATA_REG	0XF6	//两个字节温度数据

//0xf6 0xf7 0xf8 压强地址
//0xf6 0xf7 温度地址

typedef struct BMP085PARAM //校准参数表
{
  s16 ac1;
  s16 ac2;
  s16 ac3;
  u16 ac4;
  u16 ac5;
  u16 ac6;
  s16 b1;
  s16 b2;
  s16 mb;
  s16 mc;
  s16 md;
}BMP085PARAM;



void BmpInit(void);			//接口与参数初始化

//读取温度数据并校正转换
long BmpConvertTemp(void);

//读取压强数据并校正转换
long BmpConvertPressure(void);



#endif






#include "bmp085.h"

static struct BMP085PARAM bmp085ParamStruct = {0,0,0,0,0,0,0,0,0,0,0};

//IO方向设置
#define BMP_SDA_IN()  {GPIOC->CRL&=0XFFFFFFF0;GPIOC->CRL|=8;}
#define BMP_SDA_OUT() {GPIOC->CRL&=0XFFFFFFF0;GPIOC->CRL|=3;}

//IO操作函数	 
#define BMP_SCL    PCout(1) //SCL
#define BMP_SDA    PCout(0) //SDA	 
#define BMP_READ_SDA   PCin(0)  //输入SDA 

/**************************BMP5883 IIC驱动函数*********************************/

static void BMP085IOInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOC, ENABLE );	
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
	
    BMP_SCL = 1;
    BMP_SDA = 1;
}



//发送IIC起始信号
static void ComStart(void)
{
	BMP_SDA_OUT();     //sda线输出
    BMP_SDA=1;	  	  
    BMP_SCL=1;
    DelayUs(5);
    BMP_SDA=0;//START:when CLK is high,DATA change form high to low 
    DelayUs(5);
    BMP_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//发送IIC停止信号
static void ComStop(void)
{
	BMP_SDA_OUT();//sda线输出
    BMP_SDA=0;//STOP:when CLK is high DATA change form low to high
    BMP_SCL=1;
    DelayUs(5);
    BMP_SDA=1;//发送I2C总线结束信号
    DelayUs(5);		
}
//等待ACK,为1代表无ACK 为0代表等到了ACK
static u8 ComWaitAck(void)
{
	u8 waitTime = 0;
	BMP_SDA_OUT();//sda线输出
	BMP_SDA = 1;
	DelayUs(5);
    BMP_SDA_IN();      //SDA设置为输入
	BMP_SCL=1;
	DelayUs(5);
	while(BMP_READ_SDA)
	{
		waitTime++;
		DelayUs(1);
		if(waitTime > BMP_ACK_WAIT_TIME)
		{
			ComStop();
			return 1;
		}
	}
	BMP_SCL = 0;
	return 0;
	
}

static void ComSendAck(void)
{
	BMP_SCL = 0;
	BMP_SDA_OUT();
    BMP_SDA = 0;
	DelayUs(2);
    BMP_SCL = 1;
    DelayUs(5);
    BMP_SCL = 0;
    DelayUs(5);
}

static void ComSendNoAck(void)
{
	BMP_SCL = 0;
	BMP_SDA_OUT();
    BMP_SDA = 1;
	DelayUs(2);
    BMP_SCL = 1;
    DelayUs(5);
    BMP_SCL = 0;
    DelayUs(5);
}
//返回0 写入收到ACK 返回1写入未收到ACK
static u8 ComSendByte(u8 byte)
{
	u8 t;   
    BMP_SDA_OUT(); 	
    for(t=0;t<8;t++)
    {              
        BMP_SDA=(byte&0x80)>>7;
        byte<<=1; 	   
        BMP_SCL=1;
        DelayUs(5); 
        BMP_SCL=0;	
        DelayUs(5);
    }	 
    return ComWaitAck();
}

static void ComReadByte(u8* byte)
{
	u8 i,receive=0;
    BMP_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        receive <<= 1;
        BMP_SCL=1; 
        DelayUs(5);
        if(BMP_READ_SDA)receive++;
        BMP_SCL=0; 
        DelayUs(5); 
    }					  
    *byte = receive;
}

/**************************BMP5883 IIC驱动函数*********************************/

//**************************************
//向I2C设备写入一个字节数据
//**************************************
u8 BmpWriteByte(u8 addr,u8 dataValue)
{
	u8 res = 0;
	ComStart();                  //起始信号
	res = ComSendByte(BMP085_ADDR);   //发送设备地址+写信号
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
	res = ComSendByte(addr);    //内部寄存器地址,
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
	res = ComSendByte(dataValue);       //内部寄存器数据,
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
	ComStop();                   //发送停止信号
	return 0;
}

//**************************************
//从I2C设备读取一个字节数据
//**************************************
u8	BmpReadByte(u8 addr,u8* data)	//读取一个字节
{
	u8 REG_data,res = 0;
	ComStart();                   //起始信号
	res = ComSendByte(BMP085_ADDR);    //发送设备地址+写信号
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
	res = ComSendByte(addr);     //发送存储单元地址,从0开始
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}	
	ComStart();                   //起始信号
	res = ComSendByte(BMP085_ADDR+1);  //发送设备地址+读信号
	if(res)
	{
		#ifdef BMP085_DEBUG
		printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
	ComReadByte(®_data);       //读出寄存器数据
	ComSendNoAck();                //接收应答信号
	ComStop();                    //停止信号
	*data = REG_data;
	return 0;
}

s16 BmpReadTwoByte(u8 addr)		//读出BMP085内部数据,连续两个
{
  u8 msb, lsb;
  short dataValue;
  BmpReadByte(addr,&msb);
  BmpReadByte(addr + 1,&lsb);
  dataValue = ((short)msb)<<8;
  dataValue |= lsb;
  return dataValue;
}


//获取校正参数,读出十一个字的校准系数**************
void BmpGetParam(BMP085PARAM* bmpParam)
{
   bmpParam->ac1 = BmpReadTwoByte(BMP_AC1_ADDR);
   bmpParam->ac2 = BmpReadTwoByte(BMP_AC2_ADDR);
   bmpParam->ac3 = BmpReadTwoByte(BMP_AC3_ADDR);
   bmpParam->ac4 = BmpReadTwoByte(BMP_AC4_ADDR);
   bmpParam->ac5 = BmpReadTwoByte(BMP_AC5_ADDR);
   bmpParam->ac6 = BmpReadTwoByte(BMP_AC6_ADDR);
   bmpParam->b1 = BmpReadTwoByte(BMP_B1_ADDR);
   bmpParam->b2 = BmpReadTwoByte(BMP_B2_ADDR);
   bmpParam->mb = BmpReadTwoByte(BMP_MB_ADDR);
   bmpParam->mc = BmpReadTwoByte(BMP_MC_ADDR);
   bmpParam->md = BmpReadTwoByte(BMP_MD_ADDR);
}

void BmpInit(void)			//接口与参数初始化
{
  BMP085IOInit();
  BmpGetParam(&bmp085ParamStruct);
}

//读取温度数据(未经过校正的)
s32 BmpReadTemp(void)			
{
  BmpWriteByte(CONTROL_REG_ADDR,BMP_COVERT_TEMP);	//启动温度转换
  DelayMs(5);					//等待转换完成,最大转换4.5MS
  return (s32)BmpReadTwoByte(BMP_TEMP_PRES_DATA_REG);	//返回温度数据
}

//读取压强数据未校正
s32 Bmp085ReadPressure(void)	
{
  s32 pressure = 0;
  BmpWriteByte(CONTROL_REG_ADDR,BMP_COVERT_PRES_0);	//启动温度转换
  DelayMs(10);					//等待转换完成,最大转换4.5MS,所以换算的时候要用OSS=0
  pressure =  (s32)BmpReadTwoByte(BMP_TEMP_PRES_DATA_REG);	//返回压力数据,默认使用16字节压力数据
  pressure &= 0x0000FFFF;
  return pressure;
}

//读取温度数据并校正转换
long BmpConvertTemp(void)
{
  u32 ut;
  long x1, x2, b5; 
  long  temperature;
  
  ut = BmpReadTemp();	   // 读取温度
  x1 = (((long)ut - (long)bmp085ParamStruct.ac6)*(long)bmp085ParamStruct.ac5) >> 15;
  x2 = ((long) bmp085ParamStruct.mc << 11) / (x1 + bmp085ParamStruct.md);
  b5 = x1 + x2;
  temperature = ((b5 + 8) >> 4);	//温度计算,官方公式
  #ifdef BMP085_DEBUG
  printf("bmp085 temp is %f 'C\r\n",((float)temperature)/10.0);
  #endif
  return temperature;
}

//读取压强数据并校正转换
long BmpConvertPressure(void)
{
  unsigned int ut;
  unsigned long up;
  long x1, x2, b5, b6, x3, b3, p;
  unsigned long b4, b7;
  long  pressure;
  
  ut = BmpReadTemp();	   // 读取温度,计算压强时需要温度做参数
  up = Bmp085ReadPressure();  // 读取压强
  
  x1 = (((long)ut - (long)bmp085ParamStruct.ac6)*(long)bmp085ParamStruct.ac5) >> 15;
  x2 = ((long)bmp085ParamStruct.mc << 11) / (x1 + bmp085ParamStruct.md);
  b5 = x1 + x2;
  
  b6 = b5 - 4000;
  // Calculate B3
  x1 = (bmp085ParamStruct.b2 * (b6 * b6)>>12)>>11;
  x2 = (bmp085ParamStruct.ac2 * b6)>>11;
  x3 = x1 + x2;
  b3 = (((((long)bmp085ParamStruct.ac1)*4 + x3)<<OSS) + 2)>>2;
  
  // Calculate B4
  x1 = (bmp085ParamStruct.ac3 * b6)>>13;
  x2 = (bmp085ParamStruct.b1 * ((b6 * b6)>>12))>>16;
  x3 = ((x1 + x2) + 2)>>2;
  b4 = (bmp085ParamStruct.ac4 * (unsigned long)(x3 + 32768))>>15;
  
  b7 = ((unsigned long)(up - b3) * (50000>>OSS));
  if (b7 < 0x80000000)
    p = (b7<<1)/b4;
  else
    p = (b7/b4)<<1;
  
  x1 = (p>>8) * (p>>8);
  x1 = (x1 * 3038)>>16;
  x2 = (-7357 * p)>>16;
  pressure = p+((x1 + x2 + 3791)>>4);
  
  #ifdef BMP085_DEBUG
  printf("bmp085 pressure is %f KPa\r\n",((float)pressure)/1000.0);
  #endif
  return pressure;
}







posted @ 2014-10-05 21:52  邓小俊  阅读(995)  评论(0编辑  收藏  举报