一、RPI.GPIO模塊
GPIO(General Purpose I/O Ports)意思為通用輸入/輸出端口,通俗地說,就是一些引腳,可以通過它們輸出高低電平或者通過它們讀入引腳的狀態(tài)-是高電平或是低電平。GPIO是個(gè)比較重要的概念,用戶可以通過GPIO口和硬件進(jìn)行數(shù)據(jù)交互(如UART),控制硬件工作(如LED、蜂鳴器等),讀取硬件的工作狀態(tài)信號(hào)(如中斷信號(hào))等。GPIO口的使用非常廣泛。掌握了GPIO,差不多相當(dāng)于掌握了操作硬件的能力。
二、安裝RPI.GPIO
# 更換鏡像源
pi@raspberrypi:~ $ sudo sed -i 's#://raspbian.raspberrypi.org#s://mirrors.ustc.edu.cn/raspbian#g' /etc/apt/sources.list
pi@raspberrypi:~ $ sudo sed -i 's#://archive.raspberrypi.org/debian#s://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian#g' /etc/apt/sources.list.d/raspi.list
# 更新軟件包
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install python3-rpi.gpio
三、針腳編號(hào)
第一種編號(hào)是BOARD編號(hào),這和樹莓派電路板上的物理引腳編號(hào)相對(duì)應(yīng)。使用這種編號(hào)的好處是,你的硬件將是一直可以使用的,不用擔(dān)心樹莓派的版本問題。因此,在電路板升級(jí)后,你不需要重寫連接器或代碼。
第二種編號(hào)是BCM規(guī)則,是更底層的工作方式,它和Broadcom的片上系統(tǒng)中信道編號(hào)相對(duì)應(yīng)。在使用一個(gè)引腳時(shí),你需要查找信道號(hào)和物理引腳編號(hào)之間的對(duì)應(yīng)規(guī)則。對(duì)于不同的樹莓派版本,編寫的腳本文件也可能是無法通用的。
對(duì)于GPIO號(hào)接口的12引腳GPIO信號(hào)18.如果你使用GPIO.BCM模式,你可以使用數(shù)字18使用它,但如果你使用GPIO.BOARD模式,你需要使用數(shù)字12使用它。
帶GPIO的為BCM編號(hào)。
指定一種編號(hào)規(guī)則:
GPIO.setmode(GPIO.BOARD)
# or
GPIO.setmode(GPIO.BCM)
# 返回被設(shè)置的編號(hào)規(guī)則
mode = GPIO.getmode()
四、警告
如果RPi.GRIO檢測(cè)到一個(gè)引腳已經(jīng)被設(shè)置成了非默認(rèn)值,那么你將看到一個(gè)警告信息。你可以通過下列代碼禁用警告:
GPIO.setwarnings(False)
四、引腳設(shè)置
在使用一個(gè)引腳前,你需要設(shè)置這些引腳作為輸入還是輸出。配置一個(gè)引腳的代碼如下:
# 將引腳設(shè)置為輸入模式
GPIO.setup(channel, GPIO.IN)
# 將引腳設(shè)置為輸出模式
GPIO.setup(channel, GPIO.OUT)
# 為輸出的引腳設(shè)置默認(rèn)值
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
五、釋放
一般來說,程序到達(dá)最后都需要釋放資源,這個(gè)好習(xí)慣可以避免偶然損壞樹莓派。釋放腳本中的使用的引腳:
GPIO.cleanup()
注意,GPIO.cleanup()只會(huì)釋放掉腳本中使用的GPIO引腳,并會(huì)清除設(shè)置的引腳編號(hào)規(guī)則。
六、輸出
要想點(diǎn)亮一個(gè)LED燈,或者驅(qū)動(dòng)某個(gè)設(shè)備,都需要給電流和電壓他們,這個(gè)步驟也很簡單,設(shè)置引腳的輸出狀態(tài)就可以了,代碼如下:
GPIO.output(channel, state)
狀態(tài)可以設(shè)置為0 / GPIO.LOW / False / 1 / GPIO.HIGH / True。如果編碼規(guī)則為,GPIO.BOARD,那么channel就是對(duì)應(yīng)引腳的數(shù)字。
如果想一次性設(shè)置多個(gè)引腳,可使用下面的代碼:
chan_list = [11,12]
GPIO.output(chan_list, GPIO.LOW)
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW))
你還可以使用Input()函數(shù)讀取一個(gè)輸出引腳的狀態(tài)并將其作為輸出值,例如:
GPIO.output(12, not GPIO.input(12))
七、輸入
我們也常常需要讀取引腳的輸入狀態(tài),獲取引腳輸入狀態(tài)如下代碼:
GPIO.input(channel)
低電平返回0 / GPIO.LOW / False,高電平返回1 / GPIO.HIGH / True。
如果輸入引腳處于懸空狀態(tài),引腳的值將是漂動(dòng)的。換句話說,讀取到的值是未知的,因?yàn)樗]有被連接到任何的信號(hào)上,直到按下一個(gè)按鈕或開關(guān)。由于干擾的影響,輸入的值可能會(huì)反復(fù)的變化。
使用如下代碼可以解決問題:
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
需要注意的是,上面的讀取代碼只是獲取當(dāng)前一瞬間的引腳輸入信號(hào)。
如果需要實(shí)時(shí)監(jiān)控引腳的狀態(tài)變化,可以有兩種辦法。最簡單原始的方式是每隔一段時(shí)間檢查輸入的信號(hào)值,這種方式被稱為輪詢。如果你的程序讀取的時(shí)機(jī)錯(cuò)誤,則很可能會(huì)丟失輸入信號(hào)。輪詢是在循環(huán)中執(zhí)行的,這種方式比較占用處理器資源。另一種響應(yīng)GPIO輸入的方式是使用中斷(邊緣檢測(cè)),這里的邊緣是指信號(hào)從高到低的變換(下降沿)或從低到高的變換(上升沿)。
八、輪詢方式
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01) # 等10毫秒給CPU一個(gè)信號(hào)
九、邊緣檢測(cè)
邊緣是指信號(hào)狀態(tài)的改變,從低到高(上升沿)或從高到低(下降沿)。通常情況下,我們更關(guān)心于輸入狀態(tài)的該邊而不是輸入信號(hào)的值。這種狀態(tài)的該邊被稱為事件。
先介紹兩個(gè)函數(shù):
wait_for_edge() 函數(shù)。
wait_for_edge()被用于阻止程序的繼續(xù)執(zhí)行,直到檢測(cè)到一個(gè)邊沿。也就是說,上文中等待按鈕按下的實(shí)例可以改寫為:
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
print('Timeout occurred')
else:
print('Edge detected on channel', channel)
add_event_detect() 函數(shù)
該函數(shù)對(duì)一個(gè)引腳進(jìn)行監(jiān)聽,一旦引腳輸入狀態(tài)發(fā)生了改變,調(diào)用event_detected()函數(shù)會(huì)返回true,如下代碼:
do_something()
// 下面的代碼放在一個(gè)線程循環(huán)執(zhí)行。
if GPIO.event_detected(channel):
print('Button pressed')
上面的代碼需要自己新建一個(gè)線程去循環(huán)檢測(cè)event_detected()的值,還算是比較麻煩的。
不過可采用另一種辦法輕松檢測(cè)狀態(tài),這種方式是直接傳入一個(gè)回調(diào)函數(shù):
def my_callback(channel):
print('This is a edge event callback function!')
print('Edge detected on channel %s'%channel)
print('This is run in a different thread to your main program')
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)
如果你想設(shè)置多個(gè)回調(diào)函數(shù),可以這樣:
def my_callback_one(channel):
print('Callback one')
def my_callback_two(channel):
print('Callback two')
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)
注意:回調(diào)觸發(fā)時(shí),并不會(huì)同時(shí)執(zhí)行回調(diào)函數(shù),而是根據(jù)設(shè)置的順序調(diào)用它們。