一蛇券、介紹
?? BMP180氣壓計(jì)是新型數(shù)字氣壓傳感器缀壤,具有非常高的性能,可用于高級(jí)移動(dòng)設(shè)備纠亚,如智能手機(jī)塘慕,平板電腦和運(yùn)動(dòng)設(shè)備。它兼容BMP085傳感器菜枷,但是在該基礎(chǔ)上擁有許多改進(jìn)苍糠,如更小的尺寸和更多的數(shù)字接口叁丧。
??BMP180不僅可以實(shí)時(shí)的測(cè)量大氣壓力啤誊,還能測(cè)量實(shí)時(shí)溫度岳瞭。同時(shí)它還具有IIC總線的接口,便于單片機(jī)進(jìn)行訪問蚊锹。另外它的使用也很方便瞳筏,不需要太多的操作就可讀取到氣壓及測(cè)量數(shù)據(jù)。
??其它I2C總線實(shí)驗(yàn)可以查看前面的PCF8591相關(guān)實(shí)驗(yàn)牡昆,如:
??樹莓派基礎(chǔ)實(shí)驗(yàn)12:PCF8591模數(shù)轉(zhuǎn)換器實(shí)驗(yàn)
二姚炕、組件
★Raspberry Pi 3主板*1
★樹莓派電源*1
★40P軟排線*1
★BMP180氣壓傳感器模塊*1
★面包板*1
★跳線若干
三、實(shí)驗(yàn)原理
測(cè)量溫度和壓力
?? UP =壓力數(shù)據(jù)(16到19位) UT =溫度數(shù)據(jù)(16位)
??開始測(cè)量溫度值UT和壓力值UP的時(shí)序圖如下所示丢烘。在啟動(dòng)后柱宦,主機(jī)發(fā)送器件地址寫入,寄存器地址和控制寄存器數(shù)據(jù)播瞳。當(dāng)接收到數(shù)據(jù)時(shí)掸刊,BMP180每8個(gè)數(shù)據(jù)位發(fā)送一個(gè)確認(rèn)(ACKS)。主機(jī)在最后一次ACKS后發(fā)送停止條件赢乓。
??為了讀出溫度數(shù)據(jù)字UT(16位)忧侧,壓力數(shù)據(jù)字UP(16到19位)和E2PROM數(shù)據(jù)如下進(jìn)行:
??在啟動(dòng)后,主機(jī)發(fā)送模塊地址寫入命令和寄存器地址牌芋。寄存器地址選擇讀取寄存器:E2PROM數(shù)據(jù)寄存器0xAA至0xBF溫度或壓力值UT或UP 0xF6(MSB)蚓炬,0xF7(LSB),可選0xF8(XLSB)然后躺屁,主設(shè)備發(fā)送重啟條件肯夏,然后讀取模塊地址,BMP180(ACKS)將對(duì)其進(jìn)行確認(rèn)犀暑。BMP180首先發(fā)送8個(gè)MSB熄捍,由主設(shè)備(ACKM)確認(rèn),然后是8個(gè)LSB母怜。主機(jī)發(fā)送“不確認(rèn)”(NACKM)余耽,最后發(fā)送停止條件。時(shí)序?yàn)椋?/p>
??下圖顯示了壓力和溫度測(cè)量的詳細(xì)算法:
四苹熏、實(shí)驗(yàn)步驟
??第1步:連接電路碟贾。
樹莓派 | T型轉(zhuǎn)接板 | BMP180氣壓傳感器 |
---|---|---|
SCL | SCL | SCL |
SDA | SDA | SDA |
3.3V | 3.3V | VCC |
GND | GND | GND |
??第2步:PCF8591模塊采用的是I2C(IIC)總線進(jìn)行通信的,但是在樹莓派的鏡像中默認(rèn)是關(guān)閉的轨域,在使用該傳感器的時(shí)候袱耽,我們必須首先允許IIC總線通信。
??第3步:查詢LCD1602的地址干发。得出地址為0x77朱巨。
pi@raspberrypi:~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
??第4步:編寫驅(qū)動(dòng)程序。這里需要下載安裝庫函數(shù)枉长,核心是BMP085.py文件冀续,后面編寫一個(gè)python控制程序時(shí)引入這個(gè)庫文件琼讽,調(diào)用這個(gè)文件中的函數(shù)實(shí)現(xiàn)更復(fù)雜的功能。
??該庫函數(shù)來自:
https://github.com/adafruit/Adafruit_Python_BMP/
??這是由Adafruit Industries公司制作的Python庫洪唐,在該地址可以將整個(gè)Adafruit_Python_BMP文件夾下載使用钻蹬。該公司成立于2005年,創(chuàng)始人是來自麻省理工學(xué)院的女工程師Limor凭需,目標(biāo)是打造一個(gè)學(xué)習(xí)電子產(chǎn)品相關(guān)知識(shí)和為各個(gè)年齡階段與技能水平不等的客戶設(shè)計(jì)產(chǎn)品的場(chǎng)所问欠。總部位于美國(guó)紐約的中心區(qū)粒蜈。
??第5步:安裝I2C的smbus及一些實(shí)用程序顺献。
pi@raspberrypi:~ $ sudo apt-get install python-smbus i2c-tools
pi@raspberrypi:~ $ sudo apt-get install build-essential python-dev
??第6步:將下載的整個(gè)Adafruit_Python_BMP文件夾復(fù)制到Pi目錄下,進(jìn)入Adafruit_Python_BMP目錄枯怖,安裝BMP的操作庫:
pi@raspberrypi:~ $ cd Adafruit_Python_BMP/
pi@raspberrypi:~/Adafruit_Python_BMP $ sudo python setup.py install
??該文件夾下有一些參考資料滚澜,BMP085.py文件就在第二個(gè)文件夾Adafruit_BMP里面:
下面是核心文件BMP085.py的代碼,供深入研究的同學(xué)參考:
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import logging
import time
# BMP085 default address.
BMP085_I2CADDR = 0x77
# Operating Modes
BMP085_ULTRALOWPOWER = 0
BMP085_STANDARD = 1
BMP085_HIGHRES = 2
BMP085_ULTRAHIGHRES = 3
# BMP085 Registers
BMP085_CAL_AC1 = 0xAA # R Calibration data (16 bits)
BMP085_CAL_AC2 = 0xAC # R Calibration data (16 bits)
BMP085_CAL_AC3 = 0xAE # R Calibration data (16 bits)
BMP085_CAL_AC4 = 0xB0 # R Calibration data (16 bits)
BMP085_CAL_AC5 = 0xB2 # R Calibration data (16 bits)
BMP085_CAL_AC6 = 0xB4 # R Calibration data (16 bits)
BMP085_CAL_B1 = 0xB6 # R Calibration data (16 bits)
BMP085_CAL_B2 = 0xB8 # R Calibration data (16 bits)
BMP085_CAL_MB = 0xBA # R Calibration data (16 bits)
BMP085_CAL_MC = 0xBC # R Calibration data (16 bits)
BMP085_CAL_MD = 0xBE # R Calibration data (16 bits)
BMP085_CONTROL = 0xF4
BMP085_TEMPDATA = 0xF6
BMP085_PRESSUREDATA = 0xF6
# Commands
BMP085_READTEMPCMD = 0x2E
BMP085_READPRESSURECMD = 0x34
class BMP085(object):
def __init__(self, mode=BMP085_STANDARD, address=BMP085_I2CADDR, i2c=None, **kwargs):
self._logger = logging.getLogger('Adafruit_BMP.BMP085')
# Check that mode is valid.
if mode not in [BMP085_ULTRALOWPOWER, BMP085_STANDARD, BMP085_HIGHRES, BMP085_ULTRAHIGHRES]:
raise ValueError('Unexpected mode value {0}. Set mode to one of BMP085_ULTRALOWPOWER, BMP085_STANDARD, BMP085_HIGHRES, or BMP085_ULTRAHIGHRES'.format(mode))
self._mode = mode
# Create I2C device.
if i2c is None:
import Adafruit_GPIO.I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(address, **kwargs)
# Load calibration values.
self._load_calibration()
def _load_calibration(self):
self.cal_AC1 = self._device.readS16BE(BMP085_CAL_AC1) # INT16
self.cal_AC2 = self._device.readS16BE(BMP085_CAL_AC2) # INT16
self.cal_AC3 = self._device.readS16BE(BMP085_CAL_AC3) # INT16
self.cal_AC4 = self._device.readU16BE(BMP085_CAL_AC4) # UINT16
self.cal_AC5 = self._device.readU16BE(BMP085_CAL_AC5) # UINT16
self.cal_AC6 = self._device.readU16BE(BMP085_CAL_AC6) # UINT16
self.cal_B1 = self._device.readS16BE(BMP085_CAL_B1) # INT16
self.cal_B2 = self._device.readS16BE(BMP085_CAL_B2) # INT16
self.cal_MB = self._device.readS16BE(BMP085_CAL_MB) # INT16
self.cal_MC = self._device.readS16BE(BMP085_CAL_MC) # INT16
self.cal_MD = self._device.readS16BE(BMP085_CAL_MD) # INT16
self._logger.debug('AC1 = {0:6d}'.format(self.cal_AC1))
self._logger.debug('AC2 = {0:6d}'.format(self.cal_AC2))
self._logger.debug('AC3 = {0:6d}'.format(self.cal_AC3))
self._logger.debug('AC4 = {0:6d}'.format(self.cal_AC4))
self._logger.debug('AC5 = {0:6d}'.format(self.cal_AC5))
self._logger.debug('AC6 = {0:6d}'.format(self.cal_AC6))
self._logger.debug('B1 = {0:6d}'.format(self.cal_B1))
self._logger.debug('B2 = {0:6d}'.format(self.cal_B2))
self._logger.debug('MB = {0:6d}'.format(self.cal_MB))
self._logger.debug('MC = {0:6d}'.format(self.cal_MC))
self._logger.debug('MD = {0:6d}'.format(self.cal_MD))
def _load_datasheet_calibration(self):
# Set calibration from values in the datasheet example. Useful for debugging the
# temp and pressure calculation accuracy.
self.cal_AC1 = 408
self.cal_AC2 = -72
self.cal_AC3 = -14383
self.cal_AC4 = 32741
self.cal_AC5 = 32757
self.cal_AC6 = 23153
self.cal_B1 = 6190
self.cal_B2 = 4
self.cal_MB = -32767
self.cal_MC = -8711
self.cal_MD = 2868
def read_raw_temp(self):
"""Reads the raw (uncompensated) temperature from the sensor."""
self._device.write8(BMP085_CONTROL, BMP085_READTEMPCMD)
time.sleep(0.005) # Wait 5ms
raw = self._device.readU16BE(BMP085_TEMPDATA)
self._logger.debug('Raw temp 0x{0:X} ({1})'.format(raw & 0xFFFF, raw))
return raw
def read_raw_pressure(self):
"""Reads the raw (uncompensated) pressure level from the sensor."""
self._device.write8(BMP085_CONTROL, BMP085_READPRESSURECMD + (self._mode << 6))
if self._mode == BMP085_ULTRALOWPOWER:
time.sleep(0.005)
elif self._mode == BMP085_HIGHRES:
time.sleep(0.014)
elif self._mode == BMP085_ULTRAHIGHRES:
time.sleep(0.026)
else:
time.sleep(0.008)
msb = self._device.readU8(BMP085_PRESSUREDATA)
lsb = self._device.readU8(BMP085_PRESSUREDATA+1)
xlsb = self._device.readU8(BMP085_PRESSUREDATA+2)
raw = ((msb << 16) + (lsb << 8) + xlsb) >> (8 - self._mode)
self._logger.debug('Raw pressure 0x{0:04X} ({1})'.format(raw & 0xFFFF, raw))
return raw
def read_temperature(self):
"""Gets the compensated temperature in degrees celsius."""
UT = self.read_raw_temp()
# Datasheet value for debugging:
#UT = 27898
# Calculations below are taken straight from section 3.5 of the datasheet.
X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
B5 = X1 + X2
temp = ((B5 + 8) >> 4) / 10.0
self._logger.debug('Calibrated temperature {0} C'.format(temp))
return temp
def read_pressure(self):
"""Gets the compensated pressure in Pascals."""
UT = self.read_raw_temp()
UP = self.read_raw_pressure()
# Datasheet values for debugging:
#UT = 27898
#UP = 23843
# Calculations below are taken straight from section 3.5 of the datasheet.
# Calculate true temperature coefficient B5.
X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
B5 = X1 + X2
self._logger.debug('B5 = {0}'.format(B5))
# Pressure Calculations
B6 = B5 - 4000
self._logger.debug('B6 = {0}'.format(B6))
X1 = (self.cal_B2 * (B6 * B6) >> 12) >> 11
X2 = (self.cal_AC2 * B6) >> 11
X3 = X1 + X2
B3 = (((self.cal_AC1 * 4 + X3) << self._mode) + 2) / 4
self._logger.debug('B3 = {0}'.format(B3))
X1 = (self.cal_AC3 * B6) >> 13
X2 = (self.cal_B1 * ((B6 * B6) >> 12)) >> 16
X3 = ((X1 + X2) + 2) >> 2
B4 = (self.cal_AC4 * (X3 + 32768)) >> 15
self._logger.debug('B4 = {0}'.format(B4))
B7 = (UP - B3) * (50000 >> self._mode)
self._logger.debug('B7 = {0}'.format(B7))
if B7 < 0x80000000:
p = (B7 * 2) / B4
else:
p = (B7 / B4) * 2
X1 = (p >> 8) * (p >> 8)
X1 = (X1 * 3038) >> 16
X2 = (-7357 * p) >> 16
p = p + ((X1 + X2 + 3791) >> 4)
self._logger.debug('Pressure {0} Pa'.format(p))
return p
def read_altitude(self, sealevel_pa=101325.0):
"""Calculates the altitude in meters."""
# Calculation taken straight from section 3.6 of the datasheet.
pressure = float(self.read_pressure())
altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa, (1.0/5.255)))
self._logger.debug('Altitude {0} m'.format(altitude))
return altitude
def read_sealevel_pressure(self, altitude_m=0.0):
"""Calculates the pressure at sealevel when given a known altitude in
meters. Returns a value in Pascals."""
pressure = float(self.read_pressure())
p0 = pressure / pow(1.0 - altitude_m/44330.0, 5.255)
self._logger.debug('Sealevel pressure {0} Pa'.format(p0))
return p0
??第5步:編寫控制程序嫁怀。每隔一秒打印一次氣壓和溫度數(shù)據(jù)设捐,由于我的所在地為高海拔的拉薩,所以氣壓數(shù)據(jù)低一些塘淑。
實(shí)際控制程序比較簡(jiǎn)單了萝招,如下:
#!/usr/bin/env python
#---------------------------------------------------------
#
# This is a program for Barometer Pressure Sensor
# Module. It could measure the pressure and temperature.
#
# This program depend on BMP085.py writted by
# Adafruit.
#
# Barometer Module Pi
# VCC ----------------- 3.3V
# GND ------------------ GND
# SCL ----------------- SCL1
# SDA ----------------- SDA1
#
#---------------------------------------------------------
import Adafruit_BMP.BMP085 as BMP085
import RPi.GPIO as GPIO
import time
def setup():
print '\n Barometer begins...'
def loop():
while True:
sensor = BMP085.BMP085()
temp = sensor.read_temperature() # Read temperature to veriable temp
pressure = sensor.read_pressure() # Read pressure to veriable pressure
print ''
print ' Temperature = {0:0.2f} C'.format(temp) # Print temperature保留小數(shù)點(diǎn)后兩位
print ' Pressure = {0:0.2f} Pa'.format(pressure) # Print pressure
#字符串中大括號(hào)和其中的字符會(huì)被替換成傳入 str.format() 的參數(shù)。
#字段名后允許可選的 ':' 和格式指令存捺。{0:0.2f}保留小數(shù)點(diǎn)后兩位
time.sleep(1)
print ''
def destory():
GPIO.cleanup() # Release resource
if __name__ == '__main__': # Program start from here
setup()
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destory()
其中使用的Python format 格式化函數(shù)詳情參見:
https://www.runoob.com/python/att-string-format.html