PCF8591使用及Python控制

PCF8591使用及Python控制

INTRODUCTION

  你們必定對於PCF8591芯片,對於其中的A0、A一、A二、channel0-channel三、AIN0-AIN三、AOUT、SCL、SDA等等以及I2C協議有着很大的困惑。同時也沒有徹底理解Python代碼中的0x40、0x4一、0x4二、0x4三、0x48究竟是什麼,bus.write_byte(0x48, 0x40) bus.write_byte(0x48, 0x40, value) bus.read_byte(0x48)這些代碼到底控制什麼。python

  這篇文章將幫助你們理清以上內容。git

1、PCF8591與Raspberry Pi的關係

  作一個很簡單的形象化,將整個系統看做一個進水/出水裝置。
MASTER與SLAVER示意圖
  裝置有四根進水管,分別命名爲AIN0, AIN1, AIN2, AIN3,還有惟一一根出水管,命名爲AOUT。還有四個中轉通道,分別命名爲channel0, channel1, channel2, channel3,從四根進水管進來的水能夠受控制地從某一個通道中流出,進入總管道,以後經過兩根出水管道——SDA和SCL,進入一個水的加工處理儲存設備。

微信

  Raspberry PI是「MASTER」,PCF是「SLAVER」,他們之間是主從關係。若是主器件要發送數據給從器件,則會啓動數據傳輸,併發送數據至從器件,最後終止數據傳輸;若是主器件想要接受從器件發來的數據,一樣也是發出命令啓動數據傳輸,接收從器件發來的數據,最後終止接收過程。
  強調一點:每次數據傳輸都是由「MASTER主動開啓的。
  是否清晰了一些?理清總的關係之後咱們再來看具體細節。

併發

2、PCF的地址與A0-A2引腳

  一個「MASTER」不可能只有一個「SLAVER」。因此對於「MASTER」來講,須要給「SLAVERS」編號,以便控制、命令。這個編號也就是PCF地址。地址的編碼規則以下圖:
ADDRESS編碼規則
  能夠看出,這是一個七位二進制數,前四位是fixed number,後三位分別對應PCF的三個引腳——A0, A1, A2。每個引腳的值均可以是0或1,一共2^3=8種排列方式,也即一共8個可以使用的地址。因此一個「MASTER」最多能同時控制8個「SLAVER」。對於PCF8591來講,其default address爲0x48(十六進制數),轉換爲二進制數是1001000,因此能夠知道他的A0、A一、A2三個引腳值均爲0。

學習

  須要再提早強調一點,Python代碼中全部0x十六進制數,只有0x48表明Address,也只有PCF擁有Address,其他全部十六進制數都不表明Address。(這是一個天坑)ui

3、I2C協議與SDA SCL

  「MASTER」與「SLAVERS」之間須要有溝通交流,給「SLAVER」分配工做或是讓「SLAVER」彙報工做,並告知「SLAVER」如何作。因此他們之間有一個特定的溝通方式,這個溝通方式就叫I^2 C通訊協議(Inter-Integrated Circuit)。I^2 C總線須要兩根線來實現鏈接於總線上的器件之間的信息傳輸,一根是SDA(Serial Data)串行數據線,另外一根是SCL(Serial Clock)串行時鐘線。編碼

  下圖是AD convert的I2C數據傳輸流程。
I2C傳輸
  是否是很使人迷惑?不要緊,形象化的解釋來了。以前有說過,I2C就是一種溝通方式,因此這個數據傳輸流程,就能夠理解爲在I2C這種特定溝通方式下的一次溝通。講的更通俗一點,就是一次聊天對話。如何理解?

spa

  聊天的第一步,是拿出手機打開微信。(對應START condition,當SDA和SCL兩條線的電平同時知足某一特定條件時,傳輸開始。)3d

  打開微信後須要找一個聯繫人才能聊天,咱們以前說「MASTER」有不少「SLAVERS」,這一步即是「MASTER」選擇與某一個「SLAVER」對話。(對應ADDRESS。)code

  第三步,「MASTER」肯定此次對話的目的的給「SLAVER」分配活兒幹,仍是讓「SLAVER」幹完活後彙報。(對應R/W,R means read,W means write,控制此次數據傳輸的目的是樹莓派寫入一個值到PCF中,仍是樹莓派從PCF中讀取一個值。)
能夠看到有不少ACK。ACK means Acknowledge character,即確認字符,意思是數據接收者告訴數據傳輸者,「已收到」。

  以後即是數據傳輸,每傳輸一個byte的數據,都會反饋一個ACK確認數據收到。
chatting

4、Python代碼控制讀取/寫入值

import smbus
import time

if __name__ == "__main__":
    bus=smbus.SMBus(1)
    bus.write_byte(0x48,0x42)
    bus.read_byte(0x48)
    while True:
        num= bus.read_byte(0x48)
        print(num)
        bus.write_byte_data(0x48,0x42,160)
        time.sleep(0.01)

  其實代碼很是簡單。以前參考文獻給出的代碼使人迷惑的緣由主要是自定義了不少方法。如今用最簡單的辦法寫出來,用到的核心方法只有三個:

bus.read_byte(ADDRESS)
bus.write_byte(ADDRESS, CONTROL_byte)
bus.write_byte_data(ADDRESS, CONTROL_byte, value)

  顧名思義,read_byte就是從PCF中讀取一個byte的數據, write_byte就是向PCF中寫入一個byte的數據,write_byte_data就是像PCF中寫入data。

  有人就會問了,write_byte和write_byte_data有什麼區別?

  還記得在PART1中強調的點嗎?每次數據傳輸必定是由「MASTER」主動開啓的,因此write_byte的目的是明確「MASTER」與哪一個「SLAVER」對話,創建聯繫,並以一個「CONTROL byte」告知「SLAVER」如何工做,並不傳輸value。這也是爲何代碼中的第一步是bus.read_byte(0x48)

  樹莓派主動開啓對話,與0x48地址對應的PCF8591創建聯繫,並經過一個CONTROL byte明確工做模式(Analog Input Mode)。

  第二個問題來了,CONTROL byte是什麼?那不是個地址嗎?

  這時候應該要想起PART2強調的內容(只有0x48是地址)。write_byte方法的第二個傳參是一個CONTROL byte。
analog input mode
  這是官方文檔對於CONTROL byte的解釋。8位二進制碼,其中第一位和第五位都是fixed number—0,可被編寫的部分是第二位——表明是否開啓AOUT輸出口,0表示不開啓,1表示開啓,只有該位是1的時候,DA纔會工做,數字信號被轉爲模擬信號從AOUT端口輸出;第三四位——表明四種不一樣的analog input mode,用以明確AIN端口與channel之間的數據關係。
?
  在第一幅圖中有一個大大的問號,analog input mode即是明確這個問號。有兩種對應關係:一種是single-ended,即一對一。例如在00模式下,從channel0中讀到的數據就是AIN0口輸入的模擬量通過AD轉換後的數字值,00模式的四個AIN端口都是分別一一對應於一個channel;在10模式下,AIN0和AIN1是一一對應channel0和channel1。另一種對應關係是differential,即差分。例如在01模式下,channel0的值是AIN0與AIN3的差值;在10模式下,channel2的值是AIN2與AIN3的差值。



  CONTROL byte的第七八位是AD channel number,用來控制從哪一個channel讀取數據。第六位是auto-increment,值爲1時,每次讀取數據都會自動切換channel,第一次讀channel0的數據,下一次讀到的就是channel1的數據,以此類推。

  在Python代碼中,CONTROL byte是一個十六進制數,轉換爲二進制後就是八位二進制。控制方法爲:肯定CONTROL byte中可編碼位上你想要的取值,獲得一個八位二進制數,轉爲十六進制數,輸入Python方法。

  講到這裏後就幾乎結束了大部分的內容。繼續代碼示例,read_byte,讀值過程不須要CONTROL byte,因此傳參只有ADDRESS。

  能夠看到,在真正讀取想要的num以前,還有一次空讀。這是由於PCF8591在接到讀取命令後,同時進行上一次的轉換數據的傳輸及本次的數據轉換,因此第一次空讀的數據是一個不肯定的數,第一次的空讀只是爲了啓動下一次正常的讀取。

bus.write_byte_data(0x48,0x42,160)

  write_byte_data這個方法再多解釋一下,寫入的值會直接做爲一個digital value,通過轉換後以電壓形式的模擬量從AOUT口輸出。咱們使用的PCF8591是一個8bits的AD/DA轉換器,8bits的意思是AD/DA共有 2^8=256個刻度,因此傳輸的digital值範圍是0-255。舉個例子,你但願從AOUT口輸出一個3.13V的電壓給LED燈,應該寫入的值爲3.13v/5v*255=160(這裏ADC接5v的基準電壓),可是實際上用電壓表測得此時LED燈兩端電壓爲2.64v,與理想值有必定偏差。

5、Something More

  我幾乎嘗試了全部的analog input mode,將麥克風接入不一樣的AIN口,選擇不一樣的channel,用示波器/電壓表+python畫出的圖進行了不少不少實驗,在差分模式下仍是有不少難以解釋的試驗現象,可是基本能夠確定整個原理是沒有錯的。

  補充一點,single-ended模式下,AD轉換的模擬量就是該AIN口與GND的電壓;differential模式下,AD轉換的模擬量則是兩個AIN口之間的電壓差,因此若是隻使用一個AIN口且還想要用差分模式,那麼另外一個AIN口要記着接地。

6、Learning Experience

本次超過30個小時的學習過程獲得的經驗:
①永遠不要認爲你以前的理解都是對的。發現整個理解過程出現錯誤,有時 候有必要推倒重建。
②網上資料良莠不齊,錯誤百出,絕對不能盲目相信。
③實驗要清楚記錄條件及現象,否則會由於沒記清現象/條件而作不少次重複實驗。
④在學習一個全新的知識系統的過程當中會有不少信息涌入,每每在查找資料解決問題1的時候會發現問題2,而後注意力轉移到問題2上,以後又會發現新的問題,進入一個死循環。因此須要適時中止,思考一下你是誰,你在哪兒,你要幹什麼。
⑤把問題記錄下來,而後接着探索。頗有可能在次日的時候你回看第一天的問題,會發出感嘆:我怎麼會問這麼愚蠢的問題。
⑥隊友很重要。討論/止損/提問/覆盤/總結。





個人第一篇CSDN博客,但願講的清楚。但願你們喜歡!!

相關文章
相關標籤/搜索