樹莓派基礎實驗12:PCF8591模數轉換器實驗

一、介紹

?? PCF8591 是單片、單電源低功耗8位CMOS數據采集器件,具有4個模擬輸入(其中一個為電壓模擬輸入)、一個模擬輸出和一個串行I2C總線接口。3個地址引腳A0、A1和A2用于編程硬件地址,允許將最多8個器件連接至I2C總線而不需要額外硬件。器件的地址、控制和數據通過兩線雙向I2C總線傳輸。器件功能包括多路復用模擬輸入、片上跟蹤和保持功能、8位模數轉換和8位數模擬轉換。最大轉換速率取決于I2C 總線的最高速率。


二、組件

★Raspberry Pi 3主板*1

★樹莓派電源*1

★40P軟排線*1

★PCF8591模數轉換器模塊*1

★雙色LED模塊*1

★面包板*1

★跳線若干

三、實驗原理

PCF8591模數轉換器

??PCF8591模塊的工作原理比較復雜,斷斷續續一個多月時間才基本理清,本文也經過多次修改,以后也會不斷回頭補充。對于小白來說,先使用,再明白就可以,不懂原理也不是天大的問題,只要堅持學習,總有一天會恍然大悟,后面也推薦有經典教材供深入學習。若有疑問,歡迎留言,看到了就會回復交流。
1、I2C總線:
??I2C總線是由Philips公司開發的一種簡單、雙向二線制同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息。
??主器件用于啟動總線傳送數據,并產生時鐘以開放傳送的器件,此時任何被尋址的器件均被認為是從器件。在總線上主和從、發和收的關系不是恒定的,而取決于此時數據傳送方向。如果主機要發送數據給從器件,則主機首先尋址從器件,然后主動發送數據至從器件,最后由主機終止數據傳送;如果主機要接收從器件的數據,首先由主器件尋址從器件,然后主機接收從器件發送的數據,最后由主機終止接收過程。在這種情況下,機負責產生定時時鐘和終止數據傳送。

I2C總線

??SDA(串行數據線)和SCL(串行時鐘線)都是雙向I/O線,接口電路為開漏輸出,需通過上拉電阻接電源VCC。當總線空閑時.兩根線都是高電平,連接總線的外同器件都是CMOS(Complementary Metal Oxide Semiconductor互補金屬氧化物半導體)器件,輸出級也是開漏電路。在總線上消耗的電流很小,因此,總線上擴展的器件數量主要由電容負載來決定,因為每個器件的總線接口都有一定的等效電容。
??主器件用于啟動總線傳送數據,并產生時鐘以開放傳送的器件,此時任何被尋址的器件均被認為是從器件。在總線上主和從、發和收的關系不是恒定的,而取決于此時數據傳送方向。如果主機要發送數據給從器件,則主機首先尋址從器件,然后主動發送數據至從器件,最后由主機終止數據傳送;如果主機要接收從器件的數據,首先由主器件尋址從器件,然后主機接收從器件發送的數據,最后由主機終止接收過程。在這種情況下,主機負責產生定時時鐘和終止數據傳送。

2、引腳定義:
本模塊左邊和右邊分別外擴2路排針接口,分別說明如下:

PCF8591

右邊JP1, 5對接口:
左排是:
??AOUT 芯片DA輸出接口
??AINO 芯片模擬輸入接口0
??AIN1 芯片模擬輸入接口1
??AIN2 芯片模擬輸入接口2
??AIN3 芯片模擬輸入接口3
右排是:
??GND 接地
??GND 接地
??INPUT2 熱敏電阻接口
??INPUT1 光敏電阻接口
??INPUT0 電位計接口
左邊J1, 4個接口:
??SCL IIC時鐘接口 接樹莓派的scl口(接樹莓派 I2C1 SCL口)
??SDA IIC數字接口 接樹莓派的sda口(接單樹莓派 I2C1 SDA口)
??GND 模塊地 外接地(接樹莓派GND)
??VCC 電源接口 外接3.3v-5v (接樹莓派電源)
這里用的是5V。

對應的端口分別作用如下:
INPUT0端口 用短路帽接上AIN0,選擇0-5V可調電壓接入電路

INPUT1端口 用短路帽接上AIN1,選擇光敏電阻接入電路

INPUT2端口 用短路帽接上AIN2,選擇熱敏電阻接入電路

PCF8591模數轉換器原理圖

??PCF8591是具有I2C總線接口的8位A/D及D/A轉換器。有4路A/D轉換輸入,1路D/A模擬輸出。I2C總線是Philips公司推出的串行總線,整個系統僅靠數據線(SDA)和時鐘線(SCL)實現完善的全雙工數據傳輸,即CPU與各個外圍器件僅靠這兩條線實現信息交換。I2C總線系統與傳統的并行總線系統相比具有結構簡單、可維護性好、易實現系統擴展、易實現模塊化標準化設計、可靠性高等優點。
AIN0~AIN3:模擬信號輸入端。

A0~A3:引腳地址端。

VDD、VSS:電源端(2.5~6V)

SDA、SCL:I2C總線的數據線、時鐘線。

OSC:外部時鐘輸入端,內部時鐘輸出端。

EXT:內部、外部時鐘選擇線,使用內部時鐘時EXT接地。

AGND:模擬信號地。

AOUT:D/A轉換輸出端。

VREF:基準電源端。


PCF8591結構圖

3、第一字節:器件地址

PCF8591地址字節

??PCF8591采用典型的I2C總線接口器件尋址方法,即總線地址由器件地址、引腳地址和方向位組成。飛利蒲公司規定A/D器件地址為1001。引腳地址為A2A1A0,其值由用戶選擇,因此I2C系統中最多可接23=8個具有I2C總線接口的A/D器件。地址的最后一位為方向位R/ ,當主控器對A/D器件進行讀操作時為1,進行寫操作時為0??偩€操作時,由器件地址、引腳地址和方向位組成的從地址為主控器發送的第一字節。

4、第二字節:控制字節

??控制字節用于控制器件的各種功能,如模擬信號由哪幾個通道輸入等??刂谱止澊娣旁诳刂萍拇嫫髦?,總線操作時為主控器發送的第二字節。其格式如下所示:

PCF8591?控制字節

其中:
D1、D0 兩位是A/D通道編號:00通道0,01通道1,10通道2,11通道3
D2 自動增量選擇(0為禁止自動增量,1為允許自動增量),如果允許自動增量,則在每次A/D轉換后,通道編號會自動遞增。
D3 特征位:固定值為:0。

D5、D4 模擬量輸入選擇:00為四路單端輸入、01為三路差分輸入、10為兩路單端與一路差分輸入、11為兩路差分輸入。
D6 使能模擬輸出AOUT有效(1為有效,0為無效)。
D7 特征位:固定值為:0。

后面的編程會遇到,“bus.write_byte(address,0x40) ” 語句就是發送控制字“0x40”,40就代表控制字“0100 0000”,主要表示模擬輸出有效,四路單端輸入,禁止自動增量,A/D通道為0。

具體如下圖所示:


控制字

??當系統為A/D轉換時,模擬輸出允許為0。模擬量輸入選擇位取值由輸入方式決定:四路單端輸入時取00,三路差分輸入時取01,單端與差分輸入時取10,二路差分輸入時取11。最低兩位時通道編號位,當對0通道的模擬信號進行A/D轉換時取00,當對1通道的模擬信號進行A/D轉換時取01,當對2通道的模擬信號進行A/D轉換時取10,當對3通道的模擬信號進行A/D轉換時取11。

??在進行數據操作時,首先是主控器發出起始信號,然后發出讀尋址字節,被控器做出應答后,主控器從被控器讀出第一個數據字節,主控器發出應答,主控器從被控器讀出第二個數據字節,主控器發出應答…一直到主控器從被控器中讀出第n個數據字節,主控器發出非應答信號,最后主控器發出停止信號。

5、A/D轉換應用開發流程

?? 一個A/D轉換的周期的開始,總是在發送有效的讀設備地址給PCF8591之后,A/D轉換在應答時鐘脈沖的后沿被觸發。PCF8591的A/D轉換程序設計流程,可以分為四個步驟:
1--發送寫設備地址,選擇IIC總線上的PCF8591器件。
2--發送控制字節,選擇模擬量輸入模式和通道。
3--發送讀設備地址,選擇IIC總線上的PCF8591器件。
4--讀取PCF8591中目標通道的數據。
(1)、AD的位數:表明這個AD共有2n個刻度,8位AD,輸出的刻度是0~255. 8591就是8為精度的,因此它digtalRead的數據在0-255之間。
(2)、分辨率:就是AD能夠分辨的最小的模擬量變化,假設5.10V的系統用8位的AD采樣,那么它能分辨的最小電壓就是5.10/255=0.02V。

??AD轉換的原理簡單來理解就是通過電路將非電信號轉為電信號,然后通過一個基準電壓(PCF8591的基準電壓是5V),然后判斷這個電信號的電壓高低,然后得到一個0-255(8位精度)的比值。

四、實驗步驟

??第1步:在本實驗中,AIN0(模擬輸入0)端口用于接收來自電位計模塊的模擬信號。AOUT(模擬輸出)用于將模擬信號輸出到雙色LED模塊,以便改變LED的亮度。傳感器上可以看見,可調電阻在傳感器上是標識的是“0”,使用INPUT0端口,用短路帽連接AIN0和INPUT0。
??光敏電阻模塊是INPUT1端口,熱敏電阻模塊是INPUT2端口。

樹莓派 T型轉接板 PCF8591模塊
SDA SDA SDA
SCL SCL SCL
5V 5V VCC
GND GND GND
雙色LED模塊 T型轉接板 PCF8591模塊
R(紅色端口) * AOUT
GND GND GND
G(綠色端口) * *
PCF8591實驗電路圖
PCF8591實驗實物連接圖

??第2步:PCF8591模塊采用的是I2C(IIC)總線進行通信的,但是在樹莓派的鏡像中默認是關閉的,在使用該傳感器的時候,我們必須首先允許IIC總線通信。

打開I2C總線通信

??第3步:開始編程。這里先編寫一個PCF8591.py庫文件,后面再編寫一個python程序引入這個庫文件。
??PCF8591.py庫文件就是PCF8591模塊的程序,單獨編寫是為了便于重用。在這個腳本中,我們使用了一個放大器用于模擬輸入和一個LED燈用于模擬輸出,模擬輸入不能超過3.3V!
??該程序也可以單獨運行,用于測試3個電阻模塊的功能。需用短路帽連接AIN0和INPUT0(電位計模塊),連接AIN1和INPUT1(光敏電阻模塊),以及連接AIN2和INPUT2(熱敏電阻模塊)。
??連接LED燈,AIN0(模擬輸入0)端口用于接收來自電位計模塊的模擬信號,AOUT(模擬輸出)用于將模擬信號輸出到雙色LED模塊,以便改變LED的亮度。

#!/usr/bin/env python
#------------------------------------------------------
#
#       您可以使用下面語句將此腳本導入另一個腳本:
#           “import PCF8591 as ADC”                
#   
#   ADC.Setup(Address)  # 查詢PCF8591的地址:“sudo i2cdetect -y 1”
# i2cdetect  is  a  userspace  program to scan an I2C bus for devices.
# It outputs a table with the list of detected devices on the specified bus.
#   ADC.read(channal)   # Channal范圍從0到3 
#   ADC.write(Value)    # Value范圍從0到255
#
#------------------------------------------------------
#SMBus (System Management Bus,系統管理總線) 
import smbus   #在程序中導入“smbus”模塊
import time

# for RPI version 1, use "bus = smbus.SMBus(1)"
# 0 代表 /dev/i2c-0, 1 代表 /dev/i2c-1 ,具體看使用的樹莓派那個I2C來決定
bus = smbus.SMBus(1)         #創建一個smbus實例

#在樹莓派上查詢PCF8591的地址:“sudo i2cdetect -y 1”
def setup(Addr):
    global address
    address = Addr

def read(chn): #channel
    if chn == 0:
        bus.write_byte(address,0x40)   #發送一個控制字節到設備
    if chn == 1:
        bus.write_byte(address,0x41)
    if chn == 2:
        bus.write_byte(address,0x42)
    if chn == 3:
        bus.write_byte(address,0x43)
    bus.read_byte(address)         # 從設備讀取單個字節,而不指定設備寄存器。
    return bus.read_byte(address)  #返回某通道輸入的模擬值A/D轉換后的數字值

def write(val):
    temp = val  # 將字符串值移動到temp
    temp = int(temp) # 將字符串改為整數類型
    # print temp to see on terminal else comment out
    bus.write_byte_data(address, 0x40, temp) 
    #寫入字節數據,將數字值轉化成模擬值從AOUT輸出

if __name__ == "__main__":
    setup(0x48) 
 #在樹莓派終端上使用命令“sudo i2cdetect -y 1”,查詢出PCF8591的地址為0x48
    while True:
        print '電位計   AIN0 = ', read(0)   #電位計模擬信號轉化的數字值
        print '光敏電阻 AIN1 = ', read(1)   #光敏電阻模擬信號轉化的數字
        print '熱敏電阻 AIN2 = ', read(2)   #熱敏電阻模擬信號轉化的數字值
        tmp = read(0)
        tmp = tmp*(255-125)/255+125 
# 125以下LED不會亮,所以將“0-255”轉換為“125-255”,調節亮度時燈不會熄滅
        write(tmp)
        time.sleep(2)

??若想深入學習模/數轉換等模電知識,強烈推薦以下書籍,國外大學最經典模電教程,比國內教程生動有趣更易懂。掃碼購買或者點擊下面的鏈接。

模擬電子基礎

《模擬電子基礎》點擊購買:https://u.dangdang.com/OIVk

??上面的程序中import smbus(System Management BUS,即系統管理總線),SMBUS總線規范是基于I2C的總線規范,但與I2C總線規范也有一定的區別。python smbus 有如下函數:

# Send only the read / write bit 
long write_quick(int addr)
 
# Read a single byte from a device, without specifying a device register. 
long read_byte(int addr)
 
# Send a single byte to a device 
long write_byte(int addr, char val)
 
# Read Byte Data transaction. 
long read_byte_data(int addr, char cmd)
 
# Write Byte Data transaction. 
long write_byte_data(int addr, char cmd, char val)
 
# Read Word Data transaction. 
long read_word_data(int addr, char cmd)
 
# Write Word Data transaction. 
long write_word_data(int addr, char cmd, int val)
 
# Process Call transaction. 
long process_call(int addr, char cmd, int val)
 
#Read Block Data transaction.  
long[] read_block_data(int addr, char cmd)
    
# Write up to 32 bytes to a device.  This fucntion adds an initial byte indicating the 
# length of the vals array before the valls array.  Use write_i2c_block_data instead! 
write_block_data(int addr,char cmd,long vals[])
 
# Block Process Call transaction.  
long[] block_process_call(int addr, char cmd, long vals[])
 
   
# I2C Access Functions
# Block Read transaction. 
long[] read_i2c_block_data(int addr, char cmd)
 
#Block Write transaction. 
write_i2c_block_data(int addr, char cmd, long vals[])
 
 
#Code Example
 
#!/usr/bin/python
 
import smbus
 
bus = smbus.SMBus(1)    # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
 
DEVICE_ADDRESS = 0x15      #7 bit address (will be left shifted to add the read write bit)
DEVICE_REG_MODE1 = 0x00
DEVICE_REG_LEDOUT0 = 0x1d
 
#Write a single register
bus.write_byte_data(DEVICE_ADDRESS, DEVICE_REG_MODE1, 0x80)
 
#Write an array of registers
ledout_values = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
bus.write_i2c_block_data(DEVICE_ADDRESS, DEVICE_REG_LEDOUT0, ledout_values)

??第4步:編輯運行本次實驗程序。用小平口起子調節藍白色的“103”可變電阻,LED燈的亮度會隨之變化;同時程序運行終端上會不停地打印可變電阻大小A/D轉換后的數字值。
??當然,這里僅僅用短路帽連接AIN0和INPUT0(電位計模塊)就可以了,光敏電阻模塊以及熱敏電阻模塊就不需要短路帽連接了。

#!/usr/bin/env python
import PCF8591 as ADC

def setup():
    ADC.setup(0x48)

def loop():
    while True:
        print ADC.read(0) 
  #打印電位計電壓大小A/D轉換后的數字值(從AIN0借口輸入的)
  #范圍是0~255,0時LED燈熄滅,255時燈最亮
        ADC.write(ADC.read(0)) 
  #將0通道輸入的電位計電壓數字值轉化成模擬值從AOUT輸出
  #給LED燈提供電源VCC輸入

def destroy():
    ADC.write(0)

if __name__ == "__main__":
    try:
        setup()
        loop()
    except KeyboardInterrupt:
        destroy()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容