一、前言
在實際的項目開發(fā)中,基本都會把2個以上的模塊組合使用,因此,各個模塊之間的相互配合很調用。我們這一講簡單介紹一下模塊之間的通信該如何處理。
二、原理分析
首先我們要分析兩個模塊之間的調用關系,比如我現在要關聯(lián)BMP280氣壓傳感器和OLED實現氣壓實時顯示的效果,那我們就要先分析這兩個模塊之間的信號關系,首先要通過IIC通訊把BMP280的氣壓數據拿到,然后再把這個數據傳輸到OLED的屏幕上,是不是就可以了。
想清楚了這個之后,下一步就是要考慮怎么把氣壓這個數據給到OLED。方法其實有很多,最簡單的就是通過函數入參傳遞,在BMP280采集的這個函數里面調用oled顯示的接口就可以把這個數據傳過去了。
三、代碼編寫
我這里的代碼分成了兩個部分,SSD1606驅動部分和應用部分。
(其實最好是分成三個部分,SSD1606驅動部分,BMP280驅動部分,應用層,為什么沒分呢,主要是懶得寫了…想早點把入門部分教程講完好進入下一階段)
bmp280-oled.py
#-*- coding:utf-8 -*-
import time
import smbus
import spidev as SPI
import SSD1306
import time
from PIL import Image,ImageDraw,ImageFont
# Raspberry Pi pin configuration:
RST = 19
DC = 16
bus = 0
device = 0
# 128x64 display with hardware SPI:
disp = SSD1306.SSD1306(RST, DC, SPI.SpiDev(bus, device))
# Initialize library.
disp.begin()
# Clear display.
disp.clear()
disp.display()
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)
# Load default font.
font = ImageFont.load_default()
# BMP280 iic address.
BMP280_I2C_ADDRESS = 0x76 # SDO = 0
# Registers value
BMP280_ID_Value = 0x58 # BMP280 ID
BMP280_RESET_VALUE = 0xB6
# BMP280 Registers definition
BMP280_TEMP_XLSB_REG = 0xFC # Temperature XLSB Register
BMP280_TEMP_LSB_REG = 0xFB # Temperature LSB Register
BMP280_TEMP_MSB_REG = 0xFA # Temperature LSB Register
BMP280_PRESS_XLSB_REG = 0xF9 # Pressure XLSB Register
BMP280_PRESS_LSB_REG = 0xF8 # Pressure LSB Register
BMP280_PRESS_MSB_REG = 0xF7 # Pressure MSB Register
BMP280_CONFIG_REG = 0xF5 # Configuration Register
BMP280_CTRL_MEAS_REG = 0xF4 # Ctrl Measure Register
BMP280_STATUS_REG = # Status Register
BMP280_RESET_REG = 0xE0 # Softreset Register
BMP280_ID_REG = 0xD0 # Chip ID Register
# calibration parameters
BMP280_DIG_T1_LSB_REG = 0x88
BMP280_DIG_T1_MSB_REG = 0x89
BMP280_DIG_T2_LSB_REG = 0x8A
BMP280_DIG_T2_MSB_REG = 0x8B
BMP280_DIG_T3_LSB_REG = 0x8C
BMP280_DIG_T3_MSB_REG = 0x8D
BMP280_DIG_P1_LSB_REG = 0x8E
BMP280_DIG_P1_MSB_REG = 0x8F
BMP280_DIG_P2_LSB_REG = 0x90
BMP280_DIG_P2_MSB_REG = 0x91
BMP280_DIG_P3_LSB_REG = 0x92
BMP280_DIG_P3_MSB_REG = 0x93
BMP280_DIG_P4_LSB_REG = 0x94
BMP280_DIG_P4_MSB_REG = 0x95
BMP280_DIG_P5_LSB_REG = 0x96
BMP280_DIG_P5_MSB_REG = 0x97
BMP280_DIG_P6_LSB_REG = 0x98
BMP280_DIG_P6_MSB_REG = 0x99
BMP280_DIG_P7_LSB_REG = 0x9A
BMP280_DIG_P7_MSB_REG = 0x9B
BMP280_DIG_P8_LSB_REG = 0x9C
BMP280_DIG_P8_MSB_REG = 0x9D
BMP280_DIG_P9_LSB_REG = 0x9E
BMP280_DIG_P9_MSB_REG = 0x9F
class BMP180(object):
def __init__(self, address=BMP280_I2C_ADDRESS):
self._address = address
self._bus = smbus.SMBus(1)
# Load calibration values.
if self._read_byte(BMP280_ID_REG) == BMP280_ID_Value: # read bmp280 id
self._load_calibration() # load calibration data
# BMP280_T_MODE_1 << 5 | BMP280_P_MODE_1 << 2 | BMP280_SLEEP_MODE;
ctrlmeas = 0xFF
# BMP280_T_SB1 << 5 | BMP280_FILTER_MODE_1 << 2;
config = 0x14
self._write_byte(BMP280_CTRL_MEAS_REG, ctrlmeas) # write bmp280 config
# sets the data acquisition options
self._write_byte(BMP280_CONFIG_REG, config)
else:
print("Read BMP280 id error!rn")
def _read_byte(self, cmd):
return self._bus.read_byte_data(self._address, cmd)
def _read_u16(self, cmd):
LSB = self._bus.read_byte_data(self._address, cmd)
MSB = self._bus.read_byte_data(self._address, cmd+1)
return (MSB << 8) + LSB
def _read_s16(self, cmd):
result = self._read_u16(cmd)
if result > 32767:
result -= 65536
return result
def _write_byte(self, cmd, val):
self._bus.write_byte_data(self._address, cmd, val)
def _load_calibration(self): # load calibration data
"load calibration"
""" read the temperature calibration parameters """
self.dig_T1 = self._read_u16(BMP280_DIG_T1_LSB_REG)
self.dig_T2 = self._read_s16(BMP280_DIG_T2_LSB_REG)
self.dig_T3 = self._read_s16(BMP280_DIG_T3_LSB_REG)
""" read the pressure calibration parameters """
self.dig_P1 = self._read_u16(BMP280_DIG_P1_LSB_REG)
self.dig_P2 = self._read_s16(BMP280_DIG_P2_LSB_REG)
self.dig_P3 = self._read_s16(BMP280_DIG_P3_LSB_REG)
self.dig_P4 = self._read_s16(BMP280_DIG_P4_LSB_REG)
self.dig_P5 = self._read_s16(BMP280_DIG_P5_LSB_REG)
self.dig_P6 = self._read_s16(BMP280_DIG_P6_LSB_REG)
self.dig_P7 = self._read_s16(BMP280_DIG_P7_LSB_REG)
self.dig_P8 = self._read_s16(BMP280_DIG_P8_LSB_REG)
self.dig_P9 = self._read_s16(BMP280_DIG_P9_LSB_REG)
def compensate_temperature(self, adc_T):
"""Returns temperature in DegC, double precision. Output value of "1.23"equals 51.23 DegC."""
var1 = ((adc_T) / 16384.0 - (self.dig_T1) / 1024.0) * (self.dig_T2)
var2 = (((adc_T) / 131072.0 - (self.dig_T1) / 8192.0) *
((adc_T) / 131072.0 - (self.dig_T1) / 8192.0)) * (self.dig_T3)
self.t_fine = var1 + var2
temperature = (var1 + var2) / 5120.0
return temperature
def compensate_pressure(self, adc_P):
"""Returns pressure in Pa as double. Output value of "6386.2"equals 96386.2 Pa = 963.862 hPa."""
var1 = (self.t_fine / 2.0) - 64000.0
var2 = var1 * var1 * (self.dig_P6) / 32768.0
var2 = var2 + var1 * (self.dig_P5) * 2.0
var2 = (var2 / 4.0) + ((self.dig_P4) * 65536.0)
var1 = ((self.dig_P3) * var1 * var1 / 524288.0 +
(self.dig_P2) * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * (self.dig_P1)
if var1 == 0.0:
return 0 # avoid exception caused by division by zero
pressure = 1048576.0 - adc_P
pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1
var1 = (self.dig_P9) * pressure * pressure / 2147483648.0
var2 = pressure * (self.dig_P8) / 32768.0
pressure = pressure + (var1 + var2 + (self.dig_P7)) / 16.0
return pressure
def get_temperature_and_pressure(self):
"""Returns pressure in Pa as double. Output value of "6386.2"equals 96386.2 Pa = 963.862 hPa."""
xlsb = self._read_byte(BMP280_TEMP_XLSB_REG)
lsb = self._read_byte(BMP280_TEMP_LSB_REG)
msb = self._read_byte(BMP280_TEMP_MSB_REG)
adc_T = (msb << 12) | (lsb << 4) | (
xlsb >> 4) # temperature registers data
temperature = self.compensate_temperature(
adc_T) # temperature compensate
xlsb = self._read_byte(BMP280_PRESS_XLSB_REG)
lsb = self._read_byte(BMP280_PRESS_LSB_REG)
msb = self._read_byte(BMP280_PRESS_MSB_REG)
adc_P = (msb << 12) | (lsb << 4) | (
xlsb >> 4) # pressure registers data
pressure = self.compensate_pressure(
adc_P) # pressure compensate
return temperature, pressure
if __name__ == '__main__':
import time
print("BMP280 Test Program ...n")
bmp280 = BMP180()
while True:
time.sleep(1)
temperature, pressure = bmp280.get_temperature_and_pressure()
print('Temperature = %.2f C Pressure = %.3f kPa' %
(temperature, pressure/1000))
# 計算海拔(考慮溫度補償)
h1 = (pow(101325 / pressure, 1.0 / 5.257) - 1) * (temperature + 273.15) / 0.0065
# 計算海拔(不考慮溫度補償)
h2 = 44330 * (1 - pow(pressure / 101325, 1.0 / 5.255))
# 清屏
draw.rectangle((0,0,width,height), outline=0, fill=0)
# 寫入氣壓
draw.text((0, 0), "P:", font=font, fill=255)
# pressure轉換成kPa并保留3位小數,然后再由浮點型轉換成字符串,并顯示
draw.text((20, 0), str(round(pressure/1000, 3)), font=font, fill=255)
draw.text((100, 0), "kPa", font=font, fill=255)
# 寫入溫度
draw.text((0, 15), "T:", font=font, fill=255)
draw.text((20, 15), str(round(temperature, 2)), font=font, fill=255)
draw.text((100, 15), "C", font=font, fill=255)
# 寫入海拔1(考慮溫度補償)
draw.text((0, 30), "h1:", font=font, fill=255)
draw.text((20, 30), str(round(h1, 2)), font=font, fill=255)
draw.text((100, 30), "m", font=font, fill=255)
# 寫入海拔2(不考慮溫度補償)
draw.text((0, 45), "h2:", font=font, fill=255)
draw.text((20, 45), str(round(h2, 2)), font=font, fill=255)
draw.text((100, 45), "m", font=font, fill=255)
# 顯示寫入的信息
disp.image(image)
disp.display()
SSD1306.py
import spidev
import RPi.GPIO as GPIO
import time
# Constants
SSD1306_SETCONTRAST = 0x81
SSD1306_DISPLAYALLON_RESUME = 0xA4
SSD1306_DISPLAYALLON = 0xA5
SSD1306_NORMALDISPLAY = 0xA6
SSD1306_INVERTDISPLAY = 0xA7
SSD1306_DISPLAYOFF = 0xAE
SSD1306_DISPLAYON = 0xAF
SSD1306_SETDISPLAYOFFSET = 0xD3
SSD1306_SETCOMPINS = 0xDA
SSD1306_SETVCOMDETECT = 0xDB
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
SSD1306_SETPRECHARGE = 0xD9
SSD1306_SETMULTIPLEX = 0xA8
SSD1306_SETLOWCOLUMN = 0x00
SSD1306_SETHIGHCOLUMN = 0x10
SSD1306_SETSTARTLINE = 0x40
SSD1306_MEMORYMODE = 0x20
SSD1306_COLUMNADDR = 0x21
SSD1306_PAGEADDR = 0x22
SSD1306_COMSCANINC = 0xC0
SSD1306_COMSCANDEC = 0xC8
SSD1306_SEGREMAP = 0xA0
SSD1306_CHARGEPUMP = 0x8D
SSD1306_EXTERNALVCC = 0x1
SSD1306_SWITCHCAPVCC = 0x2
# Scrolling constants
SSD1306_ACTIVATE_SCROLL = 0x2F
SSD1306_DEACTIVATE_SCROLL = 0x2E
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
class SSD1306(object):
"""class for SSD1306 128*64 0.96inch OLED displays."""
def __init__(self,rst,dc,spi):
self.width = 128
self.height = 64
self._pages = 8
self._buffer = [0]*(self.width*self._pages)
#Initialize DC RST pin
self._dc = dc
self._rst = rst
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(self._dc,GPIO.OUT)
GPIO.setup(self._rst,GPIO.OUT)
#Initialize SPI
self._spi = spi
def command(self,cmd):
"""Send command byte to display"""
GPIO.output(self._dc,GPIO.LOW)
self._spi.writebytes([cmd])
def data(self,val):
"""Send byte of data to display"""
GPIO.output(self._dc,GPIO.HIGHT)
self._spi.writebytes([val])
def begin(self,vccstate=SSD1306_SWITCHCAPVCC):
"""Initialize dispaly"""
self._vccstate = vccstate
self.reset()
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ra tio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x3F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x12)
self.command(SSD1306_SETCONTRAST) # 0x81
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x9F)
else:
self.command(0xCF)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
self.command(SSD1306_DISPLAYON)
def reset(self):
"""Reset the display"""
GPIO.output(self._rst,GPIO.HIGH)
time.sleep(0.001)
GPIO.output(self._rst,GPIO.LOW)
time.sleep(0.010)
GPIO.output(self._rst,GPIO.HIGH)
def display(self):
"""Write display buffer to physical display"""
self.command(SSD1306_COLUMNADDR)
self.command(0) #Cloumn start address
self.command(self.width-1) #Cloumn end address
self.command(SSD1306_PAGEADDR)
self.command(0) #Page start address
self.command(self._pages-1) #Page end address
#Write buffer data
GPIO.output(self._dc,GPIO.HIGH)
self._spi.writebytes(self._buffer)
def image(self, image):
"""Set buffer to value of Python Imaging Library image."""
if image.mode != '1':
raise ValueError('Image must be in mode 1.')
imwidth, imheight = image.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display
({0}x{1}).' .format(self.width, self.height))
pix = image.load()
# Iterate through the memory pages
index = 0
for page in range(self._pages):
# Iterate through all x axis columns.
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
bits = 0
# Don't use range here as it's a bit slow
for bit in [0, 1, 2, 3, 4, 5, 6, 7]:
bits = bits << 1
bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1
# Update buffer byte and increment to next byte.
self._buffer[index] = bits
index += 1
def clear(self):
"""Clear contents of image buffer"""
self._buffer = [0]*(self.width*self._pages)
def set_contrast(self, contrast):
"""Sets the contrast of the display.
Contrast should be a value between 0 and 255."""
if contrast < 0 or contrast > 255:
raise ValueError('Contrast must be a value from 0 to 255).')
self.command(SSD1306_SETCONTRAST)
self.command(contrast)
def dim(self, dim):
"""Adjusts contrast to dim the display if dim is True,
otherwise sets the contrast to normal brightness if dim is False."""
# Assume dim display.
contrast = 0
# Adjust contrast based on VCC if not dimming.
if not dim:
if self._vccstate == SSD1306_EXTERNALVCC:
contrast = 0x9F
else:
contrast = 0xCF
運行結果:
四、結束語
模塊的組合使用是一門必修課,但好像又沒有專門的課程講解,需要不斷的實踐和優(yōu)化才能逐漸熟練掌握。模塊化的代碼要力求做到結構清晰,各模塊各行其道,可以獨立使用,也可以組合使用的境界,這好像又涉及到架構的問題了,那么問題來了,我什么時候才能到這個水平呢。
好了,這一講的內容就這么多了,如果對你有幫助,可以給個收藏,如果想了解更多樹莓派的知識可以關注我,后續(xù)我會繼續(xù)更新更多的教程。
教程相關的軟件和源碼:https://pan.baidu.com/s/1-lVAZyH2s-VTn5qeSnEPhA ,提取碼:qwer