RPI.GPIO是python的一個模組,樹莓派官方系統預設已經安裝
使用python控制GPIO需要匯入RPI.GPIO模組
#匯入模組並檢查它是否成功:
import RPi.GPIO as GPIO
try:
import RPi.GPIO as GPIO
except RuntimeError :
print("匯入RPi.GPIO時出錯,可能是許可權問題")
RPi.GPIO中使用的IO引腳編號有兩種方法。
引腳功能
如下圖,功能名一欄寫名了樹莓派引腳的功能
主要有如下分類
查詢引腳編號
若忘了引腳編號,又找不到圖,你可以在樹莓派linux終端中輸入命令查詢引腳編號,如下
gpio readall
上面提到,樹莓派有多個編號系統,在程式設計之前需要指定一個編號系統。指定後,即使用該編號進行程式設計,若使用其他編號會導致錯誤
GPIO.setmode(GPIO.BOARD) #指定為BOARD編號
# or
GPIO.setmode(GPIO.BCM)#指定為BCM編號
若要檢測已經使用了什麼編號可使用
mode = GPIO.getmode()
mode為GPIO.BOARD,GPIO.BCM、None,其意義顯而易見
GPIO引腳是通用輸入輸出引腳,其是可以作為輸入或輸出所用
因此使用之前,你需要告訴系統,該引腳你需要作為輸入還是輸出
設定為輸入模式
GPIO.setup(引腳,GPIO.IN)
還可以設定上拉電阻(具體用處後面會介紹)
GPIO.setup(引腳,GPIO.IN,pull_up_down=GPIO.PUD_UP)
下拉電阻
GPIO.setup(引腳,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
設定為輸出模式
GPIO.setup(引腳,GPIO.OUT)
設定為輸出並初始為為HIGH或LOW
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
可以同時 設定多個引腳
chan_list = [ 11 ,12 ]
#你可以用元組代替,即:
#chan_list =(11,12)
GPIO.setup(chan_list, GPIO.OUT)
注意 引腳的編號為你上面指定的編號系統 ❗️❗️❗️
程式結束不釋放引腳是一個很危險的行為❗️❗️❗️❗️❗️❗️
假如執行程式時 你設定引腳輸出為高電平,而程式結束時你未釋放引腳它將保持這一狀態,一旦意外接觸到該引腳與GND將會短路,燒燬你的樹莓派
釋放引腳:
GPIO.cleanup()
注意,同時GPIO.cleanup()也會清除正在使用的引腳編號系統。
根據上文,進行輸出操作前,應有
import RPi.GPIO as GPIO
try:
import RPi.GPIO as GPIO
except RuntimeError :
print("匯入RPi.GPIO時出錯,可能是許可權問題")
GPIO.setmode(GPIO.BCM)#指定為BCM編號
GPIO.setup(17,GPIO.OUT)
設定 GPIO 針腳的輸出狀態:
GPIO.output(channel, state)
state可以是0 / GPIO.LOW / False --- 低電平
或者 1 / GPIO.HIGH / True --- 高電平
輸出切換,高變低,低變高
GPIO.output(channel, not GPIO.input(channel))
脈寬調變(PWM)是指用微處理器的數位輸出來對類比電路進行控制,是一種對模擬訊號電平進行數位編碼的方法
建立一個 PWM 範例:
p = GPIO.PWM(channel, frequency)
啟用 PWM:
p.start(dc) # dc 代表佔空比(範圍:0.0 <= dc >= 100.0)
更改頻率:
p.ChangeFrequency(freq) # freq 為設定的新頻率,單位為 Hz
更改佔空比:
p.ChangeDutyCycle(dc) # 範圍:0.0 <= dc >= 100.0
停止 PWM:
p.stop()
範例
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 50) # 通道為 12 頻率為 50Hz
p.start(0)
try:
while 1:
for dc in range(0, 101, 5):
p.ChangeDutyCycle(dc)
time.sleep(0.1)
for dc in range(100, -1, -5):
p.ChangeDutyCycle(dc)
time.sleep(0.1)
except KeyboardInterrupt:
pass
p.stop()
GPIO.cleanup()
輸入要比輸出複雜一些
如果輸入引腳沒有接其他器件(或者開關關閉),那麼這個引腳將處於浮動的狀態,即可能高電平可能低電平,或者在兩者之間切換。這時候讀取引腳的值沒有意義。所以我們需要上拉/下拉電阻,使引腳狀態確定
上拉電阻
GPIO.setup(引腳,GPIO.IN,pull_up_down=GPIO.PUD_UP)
下拉電阻
GPIO.setup(引腳,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
這是最簡易的一種方式,在某個時間點檢查輸入值。這即是所謂的「輪詢 」,而且如果您的程式在錯誤的時間裡進行了讀取,可能會錯過某個輸入值。在迴圈中運用輪詢,有可能使處理器資源緊張。
範例
if GPIO.input(channel):
print('Input was HIGH')
else:
print('Input was LOW')
迴圈中等待按鈕被按下後進行輪詢
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01) # 為 CPU 留出 10 毫秒,供其處理其它事物
用這種方法輸入不會因為cpu在忙其他事而錯過輸入,且佔用 CPU 資源很少
邊緣就是是從 HIGH 到 LOW 的過度(下降臨界值falling edge)或從 LOW 到 HIGH 的過度(上升臨界值rising edge)
檢測到邊緣時執行執行緒回撥函數
wait_for_edge(channel, state) 函數
用於在檢測到邊緣之前阻止程式的執行。
上面的範例中,等待按鈕被按下的語句可以改寫為:
GPIO.wait_for_edge(channel, GPIO.RISING)
如果您只想等待一段時間,則可以使用timeout引數:
#上升沿等待最多5秒(超時以毫秒為單位)
pin= GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if pin is None:
print('Timeout occurred')
else:
print('Edge detected on pin', pin)
event_detected(channel, state) 函數
函數被設計用於迴圈中有其它東西時使用,但不同於輪詢的是,它不會錯過當 CPU 忙於處理其它事物時輸入狀態的改變。這在類似使用 Pygame 或 PyQt 時主迴圈實時監聽和響應 GUI 的事件是很有用的。
GPIO.add_event_detect(channel, GPIO.RISING) # 在通道上新增上升臨界值檢測
do_something()
if GPIO.event_detected(channel):
print('Button pressed')
state可以為GPIO.RISING、GPIO.FALLING、GPIO.BOTH
RPi.GPIO 在第二條執行緒中執行回撥函數。這意味著回撥函數可以同您的主程式同時執行,並且可以立即對邊緣進行響應。例如:
def my_callback(channel):
print('這是一個邊緣事件回撥函數!')
print('在通道 %s 上進行邊緣檢測'%channel)
print('該程式與您的主程式執行在不同的程序中')
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback) # 在通道上新增上升臨界值檢測
... 其它程式程式碼 ...
如果您需要多個回撥函數:
def my_callback_one(channel):
print('回撥 1')
def my_callback_two(channel):
print('回撥 2')
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)
注意,在該範例中,回撥函數為順序執行而不是同時執行。這是因為當前只有一個程序供回撥使用,而回撥的執行順序是依據它們被定義的順序。
每次按鈕按下時,回撥操作被呼叫不止一次。這種現象被稱作「開關抖動 」。這裡有兩種方法解決開關抖動問題:
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
或者
GPIO.add_event_callback(channel, my_callback, bouncetime=200)
remove_event_detect()
如果你不希望你的程式檢測邊緣事件,可以將它停止:
GPIO.remove_event_detect(channel)