为了能够在步态判断过程中准确地判断上楼步态与下楼步态,尝试引入气压计,通过测试上下楼气压的变化间接算出水平面高度的变化,从而判断出上楼或者下楼的步态。 当然,使用气压传感器的用途还有很多,借用一下 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 读取数据的步骤如下:
- 发送模块地址+W(表示写操作),如 Fig4.4 中的 0xEE。
- 送寄存器地址(register address),如 Fig4.4 中的第一个 0xF6。
- 重新开始 IIC 传输(Restart)。
- 发送模块地址+R(表示要进行读操作),如 Fig4.4 中的 0xEF。
- 读取测量值的高 8 位(MSB)。
- 读取测量值的低 8 位(LSB)。
- 不同寄存器地址的意义如 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 协议。
- 向固定的寄存器(0xF4)写特定值(Tab4.1 中的 control register value)其实就是向 0xF4 地址写不同的值从而完成温度测量或者不同的压力精度的测量。
- 从特定的寄存器(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
#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)<>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<> (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; }