今天要練習使用的東西是BMP085的大氣壓力量測晶片。
1. 首先介紹Sensor
BMP085是一種超低能耗的壓力感測器,採用的8-pin 陶瓷無引線晶片承載(LCC)超薄封裝,可以通過I2C匯流排直接與各種微處理器相連,因此我們接線時,除了供應電源&GND,再接上SDA和SCL兩個Pin腳,就可以把其數位的訊號帶進Arduino的板子裡了。其規格如下:
壓力範圍:30 ~ 110 kPa (海拔 9000 至-500公尺
電源電壓:1.8V ~ 3.6V
解析度為:6 Pa (0.5米)
含溫度輸出,同時也已做溫度補償
I²C介面
壓力範圍:30 ~ 110 kPa (海拔 9000 至-500公尺
電源電壓:1.8V ~ 3.6V
解析度為:6 Pa (0.5米)
含溫度輸出,同時也已做溫度補償
I²C介面
2. 特別說明一下I²C
I²C (Inter-Integrated Circuit)是一種串列通訊匯流排,使用多主從架構,由飛利浦公司在1980年代為了讓主機板、嵌入式系統用以連接低速週邊裝置而發展。I²C使用兩條雙向開放集極 (Open Drain),一條為資料線(SDA),另一條為時脈線(SCL)。I²C允許多主多僕系統,所以電氣上,SCL 和 SDA二線要透過高接電阻接於正電源。
I²C具有以下優點:
(1) 不需額外的解碼電路。
(2) 資料傳送的協定可以用軟體規劃,具有高度的彈性。
(3) 具有 I²C 匯流排無論從系統中移去或加入,都不會影響其他裝置的功能。
I²C具有以下優點:
(1) 不需額外的解碼電路。
(2) 資料傳送的協定可以用軟體規劃,具有高度的彈性。
(3) 具有 I²C 匯流排無論從系統中移去或加入,都不會影響其他裝置的功能。
(4) 匯流排是兩線式的匯流排,除錯、維修變得很容易。
但是它速度不快,只適合當 IC 間溝通的橋樑。通常用在一些簡單的系統或者晶片組 IC 間溝通(命令傳遞,不是資料傳遞)的場合看到她的影子。
3. 實際接線
所以說明一下SDA與SCL之後,我們就是把這兩條線接到BMP085對應的SDA與SCL上,記得不同種類的Arduino板子,其SDA與SCL的Pin腳位置是不一樣的。接法如下圖:
4. Code
這個Code有點複雜,我後來還是直接找別人寫好的過來看 Base from bildr.blog (下面有參考資料的來源),我把我看懂之後的註解寫上去,也增加自己的理解。
/*Based from bildr.blog
利用BMP085取得溫度&大氣壓力,並換算成高度
鮑率用9600,並用Serial.print顯示出來
*/
#include <Wire.h>
#define BMP085_ADDRESS 0x77
// I2C address of BMP085
const unsigned char OSS = 0;
//
Oversampling Setting
// 定義校準值
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;
long b5;
// bmp085GetTemperature(...)這個指令會先計算出b5這個變數
//然後b5又會被拿去用在bmp085GetPressure(...)校正壓力、高度
//所以Temperature(...)必須在Pressure(...)之前先執行
void setup(){
Serial.begin(9600);
Wire.begin();
bmp085_Calibration();
}
void loop() {
float temperature =
bmp085GetTemperature(bmp085ReadUT());
float pressure =
bmp085GetPressure(bmp085ReadUP());
float atm = pressure / 101325;
float altitude = calcAltitude(pressure);
//Uncompensated caculation - in Meters
//先叫出溫度的變數,再叫出氣壓變數,1atm = 101325 Pa
//最後再叫出高度的變數
Serial.print("Temperature: ");
Serial.print(temperature,
2); //顯示小數位下兩位
Serial.println("deg C");
Serial.print("Pressure: ");
Serial.print(pressure, 0); //0代表顯示整數
Serial.println (" Pa");
Serial.print("Standard
Atmosphere: ");
Serial.println(atm, 4); //顯示小數位下四位
Serial.print("Altitude: ");
Serial.print(altitude, 2); //顯示小數位下兩位
Serial.println (" M");
Serial.println (); //空白行
delay(1000); //每1000毫秒跑一次
}
//前面一開始就有呼叫一個子程式bmp085_Calibration()
//這是要用來校準溫度與壓力的程式
void bmp085_Calibration() {
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);
}
// 計算溫度
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;
}
--------------------------------------------------------------------------------
5. 結果
最後跑出來的結果如下圖,總共出現4行,包括
(1) 溫度
(2) 大氣壓力Pa
(3) 大氣壓力atm (=Pa/101325)
(4) 海拔高度
參考資料
請問要怎麼把測得的數據顯示在螢幕面板上?
回覆刪除可以參考 http://ming-shian.blogspot.tw/2013/09/arduino-16x2-lcd.html
刪除改一下要顯示的data。
請問可以麻煩你告訴我SDA PIN20 和SCL PIN21 在擴展板上面的位置嗎?我不知道該差在哪裡?感謝
回覆刪除不好意思,我不知道您的shield是那一種,因為shield太多了,你可以直接google你的shield的接線圖,看那些pin是對應 SDA跟SCL的腳。
刪除可以查看 arduino 網站: http://www.arduino.cc/en/Main/Products
我的板子是UNO R3的,請問要怎麼接?
回覆刪除我的板子是UNO R3的,請問在擴充版上該如何接那四條線?感恩
回覆刪除先找出你的SDA跟SCL,就可以如圖上的接法了
刪除你可以上arduino的網站查,你的UNO中,SDA跟SCL到底是那隻腳。
謝謝你的回覆,我已經利用擴充板接BMP085,並測量出結果,請問要怎樣修改CODE才能使結果顯示於LCD上?
回覆刪除請問,如果量測的氣壓超過規格書上寫的。修改library有可能達成嗎?
回覆刪除超出規格量出來的應該都不是準的,以Sensor一般性質來說
刪除請問,可以提供您的line嗎? 想跟您私聊一下
回覆刪除請問能私訊嗎
回覆刪除