一起玩轉樹莓派(7)——樹莓派模數/數模轉換實踐
如果你跟随本系列部落格閱讀到此,那麼我相信,你已經能夠使用樹莓派的GPIO接口做許多有趣的事情了。但是不知你是否有發現,GPIO功能接口無論是輸入還是輸出,都隻有高電平或低電平這兩種選項,抽象到數學中,即隻有0和1兩種數值選項,這種離散數值的信号我們将其稱為數字信号。更多時候,我們需要使用的元件并非隻有開和關兩種狀态,這時候就需要用數值連續的信号來表達,這種連續的信号我們稱之為模拟信号。本篇部落格,我們将初步涉獵在樹莓派上如何處理模拟信号。
一、樹莓派模數轉換子產品基礎
其實,直接使用GPIO的功能引腳并非完全不能處理模拟信号,我們知道無論需要多少種狀态信号,通過GPIO的電平高低來處理都是無法實作的,但是你一定還記得PWM脈沖寬度調制技術,這其實就是一種将數字信号轉換成模拟信号的方式,例如在前面的RGB實驗中,我們曾使用PWM技術來控制3種發光二極管的發光明暗程度,如果将二極管的全亮定義為值1,全暗定義為值0,則在明暗之間可以通過設定不同的占空比來實作,例如需要亮度值為0.5時,隻需要設定占空比為50%即可。通過PWM技術,雖然我們每個精确的時刻輸出的依然是數字信号(0或1),但最終作用到元件上的信号是模拟的,這就完成了最簡單的數模轉換。
同樣的道理,對于某些外接元件來說,其輸出的信号也可能是模拟的,例如溫度、光亮傳感器,如果直接通過GPIO來擷取這些元件的信号是模拟的,解析會非常困難,這時我們就需要一個數模轉換子產品來幫助我們将其轉換為數字信号。
1、關于模數轉換
數模轉換器,又稱D/A轉換器,簡稱DAC。用來把數字量轉換成模拟量的器件,關于數模轉換器的電路和工作原理,不在本篇部落格的核心讨論範圍内,如果你有興趣可以查閱數字電路和模拟電路的相關書籍。對其對應,模數轉換器,又稱為A/D轉換器,簡稱ADC。用來把模拟信号轉換為數字信号。樹莓派本身沒有內建模數轉換子產品,我們需要外接一個轉換單元,在本實驗中,我們采用基于PCF8591晶片的模數轉換子產品。
2、PCF8591晶片
PCF8591是一個單片內建,單獨供電,低功耗的8位數A/D轉換器,其晶片本身如下圖所示:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yMjNTOidDNkNWOwY2Y5QDO2IjN1EDZ4cjZ0gzNhJ2NyQTZh1Cc19CX0VmbjN3bvwFdl5mLh5WaoN2cv5yZtl2Yz92Lc9CX6MHc0RHaiojIsJye.png)
可以看到其左右各有8個引腳,引腳功能圖如下:
如上圖所示,其AIN0到AIN3都是模拟輸入引腳,其用來輸入外接元件的模拟信号,它還有1個模拟輸入引腳AOUT,用來輸出模拟信号。
A0,A1,A2這3個引腳用來進行硬體位址程式設計,在嵌入式程式設計中,每個可程式設計的引腳我們将其了解為一個二進制位,3個引腳可以表達0-7這8個數字(000,001,010,011,100,101,110,111),是以在同一個I2C總線上,實際可以同時連接配接8個PCF8591晶片進行工作(I2C總線我們後面會介紹)。
VSS是接地引腳,VDD是工作電壓引腳,AGND是模拟信号接地引腳,VREF是基準電源引腳。
OSC引腳為外部時鐘輸入端或内部時鐘輸出端,EXT如果接地,表示使用内部時鐘,否則表示使用外部時鐘。
SCL和SDA是I2C總線接口引腳,我們使用這兩個引腳來進行資料傳輸,後面會介紹。
下面是PCF8591工作的電路原理圖:
溫馨提示,在閱讀本篇部落格時,難度可能要比本系列的前幾篇部落格大,你可能會感覺雲裡霧裡,不要着急,後面我們會通過實驗來幫助你了解模數轉換以及I2總線的工作方式,掌握了這些,你的樹莓派開發本領将進一步提升。
3、PCF8591實驗子產品
上一小節我們介紹了PCF8591晶片的引腳用法,實際在本實驗中,我們并不會直接使用這個晶片,而是使用基于PCF8591晶片的一個工作子產品,如下圖所示:
如上圖所示,此子產品功能非常豐富,我們逐一介紹。
左邊一列引腳作用分别為:
- AOUT :内部晶片的DA 輸出接口
- AIN0 :内部晶片的模拟輸入接口 0
- AIN1 :内部晶片的模拟輸入接口 1
- AIN2 :内部晶片的模拟輸入接口 2
- AIN3 :内部晶片的模拟輸入接口 3
右邊一列引腳的作用為:
- SCL :I2C 通信接口
- SDA :I2C 通信接口
- GND :子產品地
- VCC :電源接口 外接 3.3v-5v
除此之外,P4,P5,P6三個地方我們可以通過接短路帽的方式來選擇是否接入相關功能。
P4:接上短路帽,表示将熱敏電阻接入電路。
P5:接上短路帽,表示将光敏電阻接入電路。
P6:接上短路帽,表示将0-5V的可調節電壓接入電路。
如果P4,P5,P6都不接短路帽,可以使用外部元件通過4個AIN引腳進行輸入。關于這幾個功能如何使用,後面我們會介紹。
此子產品的工作示意圖如下:
4.I2C總線基礎知識
I2C總線是有Philips公司開發的一種簡單、雙向的同步串行總線。隻需要兩根線即可實作連接配接在總線上的裝置間進行通信。在I2C總線的通訊協定中,SDA用來傳輸資料,SCL是串行的時鐘線,傳輸流程如下圖所示:
隻看圖你可能會感覺非常疑惑,結合樹莓派工作起來,其實要了解并不複雜,在I2C協定中,樹莓派就是主機,連接配接的PCF8591就是從機,主機與從機間無論是發送資料還是接受資料,都是由主機先發起,形象化的流程如下:
1.主機觸發開始通信條件(SDA和SCL電平滿足一定條件)。
2.通過位址找到要通信的從機(即PCF8591的3個位址程式設計引腳設定的位址)。
3.主機發送工作指令,即指定要從從機讀資料還是寫資料,讀哪個通道的資料等(PCF8591有4個輸入通道)。
4.收到資料會發送ACK回執。
5.結束此次通訊後,主機發送完成指令給從機(SDA和SCL電平滿足一定條件)。
5.幾個重要的名額概念
1.位數
ADC元件的位數非常重要,它描述了一種可讀概念,以PCF8591為例,其實8位的數模轉換晶片,也就是說其輸出的數字量是0到255之間的數值,一共256種(2的8次方)。
2.基準電壓
基準電壓用來标準數模轉換晶片輸出的數值與電壓之間關系,例如基準電壓為5V,則0到255這256個刻度相當于将5V的電壓平均分成了255份,數值沒增加1,相當于電壓約增加0.019V。
二、實驗:測定溫度和光亮度
現在,我們已經準備好了需要使用到的基礎知識,可以開始我們的試驗了。PCF8591自帶光感與溫度傳感器,我們嘗試使用這兩個傳感器來讀取環境的溫度與亮度。
1、連線
首先,我們可以先使用3個短路帽将PCF8591子產品的P4、P5、P6都進行短接,如下圖所示:
短接後,相當于我們将PCF8591子產品自帶的可調節輸出電壓、光感信号輸出和溫度信号輸出都接入了電路,可以直接通過I2C總線對其資料進行擷取。
之後,我們将PCF8591子產品子產品的SCL、SDA分别連接配接樹莓派對應功能的引腳,GND進行接地,VCC接5V。關于找到樹莓派上這些引腳的位置,可以參考下面部落格:
https://my.oschina.net/u/2340880/blog/5123429筆者這裡使用擴充闆來接線,會更加直覺友善,如下圖所示:
現在,我們已經完成了接線部分的工作。
2、代碼編寫
因為需要使用到樹莓派的I2C總線功能,在開始編碼前,我們需要将樹莓派的I2C功能開啟,打開樹莓派的配置頁面,在其interfaces選項中打開SPI和I2C選項,如下圖所示。
我們先将完整的實驗代碼奉上:
#coding:utf-8
#SMBus (System Management Bus,系統管理總線)
import smbus #在程式中導入“smbus”子產品
import time
bus = smbus.SMBus(1) #建立一個smbus執行個體
# 資料讀取的方法
def read(chn): #channel
if chn == 0:
#發送一個控制位元組到裝置 表示要讀取AIN0通道的資料
bus.write_byte(0x48,0x00)
if chn == 1:
#發送一個控制位元組到裝置 表示要讀取AIN1通道的資料
bus.write_byte(0x48,0x01)
if chn == 2:
#發送一個控制位元組到裝置 表示要讀取AIN2通道的資料
bus.write_byte(0x48,0x02)
if chn == 3:
#發送一個控制位元組到裝置 表示要讀取AIN3通道的資料
bus.write_byte(0x48,0x03)
bus.read_byte(0x48) # 空讀一次,消費掉無效資料
return bus.read_byte(0x48) # 傳回某通道輸入的模拟值A/D轉換後的數字值
if __name__ == "__main__":
while True:
print('電位計 AIN3 = ', 0.0196 * read(3)) #電位計模拟信号轉化的數字值
print('光敏電阻 AIN0 = ', 255 - read(0)) #光敏電阻模拟信号轉化的數字
print('熱敏電阻 AIN1 = ',255-read(1)) #熱敏電阻模拟信号轉化的數字值
time.sleep(2)
下面我們來詳細解釋下上面的代碼,首先smbus是一個Python子產品,我們之前并沒使用過,我們這次實驗不使用GPIO,使用smbus來進行I2C總線管理。
在核心邏輯執行前,首先需要進行總線管理器的執行個體化,使用如下方法:
bus = smbus.SMBus(1)
其中的參數表示要使用的I2C版本。
前面我們分析過,在I2C總線通信中,主機先要發送指令告訴從機要做什麼,對應代碼中就是write_byte方法,這個方法用來向PCF晶片發送一個8位的控制指令,通過查閱晶片手冊,很容易找到這個8位的控制指令每一位的意義,如下圖所示:
我們從左往右來分析上圖。
對于上圖,我們在多解釋一下,這個8位的控制指令,第1位和第5位都是預設填充位,都用0填充,目前沒有任何意義。
第2位表示是否開啟AOUT輸出口,隻有這一位為1,數模轉換才會工作,數字信号才會被轉換為模拟信号從AOUT引腳輸出。(本次實驗我們暫不使用)
第3位和第4位用來程式設計控制輸入模式,這兩位可以組成的數值有00,01,10,11共四種,設定為00表示AIN端口與輸入通道一一對應。01,10,11這3種模式通道的值都是經過 處理的,上圖有明确的标注,比如在11模式下,通道0的值是AIN0和AIN1輸入的差分,通道1的值是AIN2和AIN3輸入的差分。(本次實驗我們也無需使用這一功能)
第6位的作用是設定是否自動切換通道,當其設定為1時,每次讀取資料後,通道都會切換,例如第一次讀取通道0的數值,第二次會在讀取時,會自動讀取通道1的數值。
第7位和第8位用來指定從哪個通道讀取數值,有4個通道可用,分别通過00,01,10,11進行設定。
我們再來看下write_byte方法:
bus.write_byte(0x48,0x00)
這個方法有兩個參數,第2個參數就是我們上面說的指令參數,其用16進制表示,例如0x03設定為:
第1位 | 第2位 | 第3位 | 第4位 | 第5位 | 第6位 | 第7位 | 第8位 |
---|---|---|---|---|---|---|---|
0(占位) | 0(不使用AOUT) | 0(一一對應模式) | 0(不啟動切換通道) | 1(設定要讀取的通道) |
write_byte這個方法的第1個參數用來設定I2C總線中要為哪一個從機設定指令,PCF的位址設定是7位,查閱手冊如下:
可以看到第4位到第7位都是固定的,前3位是可程式設計的,還記得PCF的A0到A3引腳吧,就是讓主機來配置設定從機位址使用的。我們可以知道,PCF從機的位址一定在二進制數01001000(0x48)到01001111(0x4F)之間,要查閱目前連接配接到樹莓派的PCF從機的位址,可以在樹莓派終端輸入如下指令:
sudo i2cdetect -y 1
效果如下圖所示:
從圖中可以看到,樹莓派目前隻連接配接了一個I2C從機子產品,配置設定的位址是0x48。
現在,上面的代碼對你來說非常好了解了,需要注意,其中除了0x48表示的是PCF從機的位址外,其他十六進制數都是指令。指令設定完成後,接下來就可以進行資料的通信,使用如下方法:
bus.read_byte(0x48)
這個方法用來通過I2C總線從PCF讀取資料,上面代碼中我們在讀取資料時連續調用了兩次,這是因為PCF8591接收到讀取資料的指令後會同時進行上一次轉換資料的傳輸和本次資料的轉換,空讀一次可以消費掉不正确的資料。
還有一點需要注意,對于我們使用的PCF8591子產品來說,其自帶的可調節電壓會輸出到第4個通道,光敏傳感器的資料會輸出到第1個通道,熱敏傳感器的資料會輸出到第2個通道,在本次實驗中,第3個通道不被使用(如果你使用的PCF8591子產品和筆者的不太一樣,對應通道可能會不同,可以自行測試)。
三、盡情玩耍
現在,你可以嘗試在樹莓派上運作寫好的程式,通過輸出可以看到目前的可調節電壓、環境亮度和環境溫度的情況。你可以嘗試使用螺絲刀對電位計進行調節,可以實時的看到輸出電壓的改變,可以關燈和開燈來改變環境亮度,你也可以将熱敏傳感器握在手中,這些都可以明顯的從輸出資訊上看到環境資料的變化。如下圖所示:
這次,關于樹莓派程式設計,我們學到了一些新的東西,使用I2C總線和數模轉換子產品我們可以做出更多有意思的東西,比如紅外控制的自制電視機?比如有有馬達驅動的遙控小車?等等...。你可以做好準備,我們後面會一起開發一些更好玩的東西!
專注技術,懂的熱愛,願意分享,做個朋友