为了能够在步态判断过程中准确地判断上楼步态与下楼步态,尝试引入气压计,通过测试上下楼气压的变化间接算出水平面高度的变化,从而判断出上楼或者下楼的步态。

当然,使用气压传感器的用途还有很多,借用一下 BMP085datasheet  上面的介绍,气压传感器还可以用到以下的场合:盲区推估;户外导航;天气预测;垂直运动速度测量。

其实气压传感器除了在一些关于气象、天气方面的应用以外,基本上都是用来测试海拔高度。从应用场合来看,盲区推估;户外导航;垂直运动速度测量也都是用到了测量海拔高度这个应用。所以要想用好气压传感器,正确理解气压和海拔高度的关系是很重要的,本文将在第二章介绍气压与海拔的一些基本关系。

希望通过阅读本文可以帮助大家很快的搭建基于气压计的项目应用

使用气压传感器之前要考虑的问题

作为一个物理量,气压的大小有着很深刻的物理含义。这里对具体的物理意义就不详细介绍了。主要说一下气压与海拔高度的关系。

 

气压与海拔高度之间的关系

海拔高度与大气压力的关系在大气物理学里面有明确的定义。根据不同的大气模型,会有不同的气压与海拔的对应关系。详细资料可以参考大气物理学的书籍。

但是看大气物理学的书比家复杂,所以可以参考公式 2.-1-1。Pb 就是压力传感器测试出来的压力值,h 就是相应的海拔高度。有一点特别需要注意,就是海拔高度与压力大小的关系受温度的影响。很多情况下,压力传感器芯片的 Datasheet(规格书) 会提供压力与海拔高度的对应关系,也可以用来借鉴。

 

影响压力测量的因素

影响压力测量的因素有很多,除了关键的海拔高度以及温度的影响,诸如空气的流动(诸如风,空调等等)都影响空气压力的测量。

 

规格书中的重要参数

 

Tab1.0

Parameter Symbol Condition Max Unit
Operating temperature TA
Supply voltage
Supply current @1SPS At 25℃ uA
Peak current uA
uA
uA
uA
uA
uA
uA
MHz
Ms
Ms
Ms
Ms
Ms
hPa
hPa
hPa
hPa
Absolute accuracy Pressure

VDD=3.3V

hPa
Resolution of output data hPa
Solder drift Minimum solder height 50uM ±1..0 hPa
Long term stability 12 months ±1..0 hPa

 

  • absolute accuracy:绝对精度,也就是在一个测量范围内的测量精度。举例来说,如果测量的气压从700hPa一直到1100hPa,理论上读数应该是变化了400hPa,但实际上的读数可能是 400hPa±absolute accuracy。
  • relative accuracy:相对精度,也就是在一个固定测量值的测量精度。举例来说,如果测量 500hPa 的气压,实际的测量值是 500hPa±relative accuracy。
  • Solder drift:焊接影响,该器件对于焊接的要求比较高,如果焊接造成了一定的器件变形,等价于器件存在了一个内部的残余应力,所以会对实际测量产生一定的偏差。

 

BMP085 的数据读取方式

从 BMP085 读取数据的方法如图 Fig4.4 所示

从 BMP085 读取数据的步骤如下:

  1. 发送模块地址+W(表示写操作),如 Fig4.4 中的 0xEE。
  2. 送寄存器地址(register address),如 Fig4.4 中的第一个 0xF6。
  3. 重新开始 IIC 传输(Restart)。
  4. 发送模块地址+R(表示要进行读操作),如 Fig4.4 中的 0xEF。
  5. 读取测量值的高 8 位(MSB)。
  6. 读取测量值的低 8 位(LSB)。
  7. 不同寄存器地址的意义如 Tab4.2 所示

Tab4.2

寄存器名称 寄存器地址
EEPROM 0xAA To 0xBF
Temperature or pressure value(UT or UP) 0xF6(MSB)、0x F7(LSB)  、0xF8(XLSB)

 

 

 

程序控制总结

从 Fig4.4  与 Fig4.3  可以清楚地看出 MCU 控制 BMP085 的方法,这里再进行一些简单的概括。其实对 BMP085 的控制可以概括为两句话:向固定的寄存器(0xF4)写特定值(Tab4.1 中的 control register value),从特定的寄存器(Tab4.2中的寄存器地址)读返回值。每次通讯时的 Module address 都是一个固定的值,主要是为了符合 IIC 协议。

  1. 向固定的寄存器(0xF4)写特定值(Tab4.1 中的 control register value)其实就是向 0xF4 地址写不同的值从而完成温度测量或者不同的压力精度的测量。
  2. 从特定的寄存器(Tab4.2 中的寄存器地址)读返回值从 EEPROM 读取 Calibration  所需要的数据,共有 11 个 Word(双字节)。从 0xF6,0xF7,0xF8  读取 UT 或者 UP,具体是 UP 还是 UT 要由前面进行的操作决定(进行了温度转换就存有温度数据,进行了压力转换就存有压力数据)。

例程:

 

//Arduino 1.0+ Only
//Arduino 1.0+ Only

/*Based largely on code by  Jim Lindblom

Get pressure, altitude, and temperature from the BMP085.
Serial.print it out at 9600 baud to serial monitor.
*/

#include <Wire.h>

#define BMP085_ADDRESS 0x77  // I2C address of BMP085

const unsigned char OSS = 0;  // Oversampling Setting

// Calibration values
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;

// b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...)
// so ...Temperature(...) must be called before ...Pressure(...).
long b5;

void setup(){
  Serial.begin(9600);
  Wire.begin();

  bmp085Calibration();
}

void loop()
{
  float temperature = bmp085GetTemperature(bmp085ReadUT()); //MUST be called first
  float pressure = bmp085GetPressure(bmp085ReadUP());
  float atm = pressure / 101325; // "standard atmosphere"
  float altitude = calcAltitude(pressure); //Uncompensated caculation - in Meters

  Serial.print("Temperature: ");
  Serial.print(temperature, 2); //display 2 decimal places
  Serial.println("deg C");

  Serial.print("Pressure: ");
  Serial.print(pressure, 0); //whole number only.
  Serial.println(" Pa");

  Serial.print("Standard Atmosphere: ");
  Serial.println(atm, 4); //display 4 decimal places

  Serial.print("Altitude: ");
  Serial.print(altitude, 2); //display 2 decimal places
  Serial.println(" M");

  Serial.println();//line break

  delay(1000); //wait a second and get values again.
}

// Stores all of the bmp085's calibration values into global variables
// Calibration values are required to calculate temp and pressure
// This function should be called at the beginning of the program
void bmp085Calibration()
{
  ac1 = bmp085ReadInt(0xAA);
  ac2 = bmp085ReadInt(0xAC);
  ac3 = bmp085ReadInt(0xAE);
  ac4 = bmp085ReadInt(0xB0);
  ac5 = bmp085ReadInt(0xB2);
  ac6 = bmp085ReadInt(0xB4);
  b1 = bmp085ReadInt(0xB6);
  b2 = bmp085ReadInt(0xB8);
  mb = bmp085ReadInt(0xBA);
  mc = bmp085ReadInt(0xBC);
  md = bmp085ReadInt(0xBE);
}

// Calculate temperature in deg C
float bmp085GetTemperature(unsigned int ut){
  long x1, x2;

  x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
  x2 = ((long)mc << 11)/(x1 + md);
  b5 = x1 + x2;

  float temp = ((b5 + 8)>>4);
  temp = temp /10;

  return temp;
}

// Calculate pressure given up
// calibration values must be known
// b5 is also required so bmp085GetTemperature(...) must be called first.
// Value returned will be pressure in units of Pa.
long bmp085GetPressure(unsigned long up){
  long x1, x2, x3, b3, b6, p;
  unsigned long b4, b7;

  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12)>>11;
  x2 = (ac2 * b6)>>11;
  x3 = x1 + x2;
  b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;

  // Calculate B4
  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<<1)/b4;
  else
    p = (b7/b4)<<1;

  x1 = (p>>8) * (p>>8);
  x1 = (x1 * 3038)>>16;
  x2 = (-7357 * p)>>16;
  p += (x1 + x2 + 3791)>>4;

  long temp = p;
  return temp;
}

// Read 1 byte from the BMP085 at 'address'
char bmp085Read(unsigned char address)
{
  unsigned char data;

  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP085_ADDRESS, 1);
  while(!Wire.available())
    ;

  return Wire.read();
}

// Read 2 bytes from the BMP085
// First byte will be from 'address'
// Second byte will be from 'address'+1
int bmp085ReadInt(unsigned char address)
{
  unsigned char msb, lsb;

  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP085_ADDRESS, 2);
  while(Wire.available()<2)
    ;
  msb = Wire.read();
  lsb = Wire.read();

  return (int) msb<<8 | lsb;
}

// Read the uncompensated temperature value
unsigned int bmp085ReadUT(){
  unsigned int ut;

  // Write 0x2E into Register 0xF4
  // This requests a temperature reading
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x2E);
  Wire.endTransmission();

  // Wait at least 4.5ms
  delay(5);

  // Read two bytes from registers 0xF6 and 0xF7
  ut = bmp085ReadInt(0xF6);
  return ut;
}

// Read the uncompensated pressure value
unsigned long bmp085ReadUP(){

  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;

  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS<<6));
  Wire.endTransmission();

  // Wait for conversion, delay time dependent on OSS
  delay(2 + (3<<OSS));

  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  msb = bmp085Read(0xF6);
  lsb = bmp085Read(0xF7);
  xlsb = bmp085Read(0xF8);

  up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);

  return up;
}

void writeRegister(int deviceAddress, byte address, byte val) {
  Wire.beginTransmission(deviceAddress); // start transmission to device
  Wire.write(address);       // send register address
  Wire.write(val);         // send value to write
  Wire.endTransmission();     // end transmission
}

int readRegister(int deviceAddress, byte address){

  int v;
  Wire.beginTransmission(deviceAddress);
  Wire.write(address); // register to read
  Wire.endTransmission();

  Wire.requestFrom(deviceAddress, 1); // read a byte

  while(!Wire.available()) {
    // waiting
  }

  v = Wire.read();
  return v;
}

float calcAltitude(float pressure){

  float A = pressure/101325;
  float B = 1/5.25588;
  float C = pow(A,B);
  C = 1 - C;
  C = C /0.0000225577;

  return C;
}