樹莓派筆記(三) 使用 RPi.GPIO 模組

2020-10-08 12:01:03

樹莓派筆記(三) 使用 RPi.GPIO 模組

RPi.GPIO

RPI.GPIO是python的一個模組,樹莓派官方系統預設已經安裝

使用python控制GPIO需要匯入RPI.GPIO模組

  1. 匯入模組
#匯入模組並檢查它是否成功:
import RPi.GPIO  as GPIO 
tryimport RPi.GPIO  as GPIO 
except RuntimeError :
    print"匯入RPi.GPIO時出錯,可能是許可權問題"

引腳簡介

引腳編號

RPi.GPIO中使用的IO引腳編號有兩種方法。

  1. BOARD編號系統。如下圖中物理介面。使用此編號系統的優點是,無論樹莓派的版本如何,您的硬體將始終可以工作。您無需重新連線聯結器或更改程式碼。
  2. BCM編號系統。不同版本的樹莓派不一樣可能要重新修改程式碼,這是一種較低階別的工作方式-指Broadcom SOC上的通道號。您必須始終使用哪個通道號到達樹莓派板上哪個引腳的圖表。您的指令碼可能會在樹莓派板的修訂版之間中斷。

引腳功能
如下圖,功能名一欄寫名了樹莓派引腳的功能
主要有如下分類

  • 電源引腳
    • 5v、3.3v :為輸出5v、3.3v電源
    • 0v /GND :即負極,或接地級
  • GPIO引腳
    • 通用輸入輸出引腳,可程式化控制高低電平
  • 其他功能引腳
    • i2c --> SDA,SCL
    • SPI —> MOSI,MISO,SCLK 等等

查詢引腳編號
若忘了引腳編號,又找不到圖,你可以在樹莓派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  =  [ 1112 ]      
                       #你可以用元組代替,即:
                       #chan_list =11,12)
GPIO.setup(chan_list, GPIO.OUT)

注意 引腳的編號為你上面指定的編號系統 ❗️❗️❗️

釋放引腳

程式結束不釋放引腳是一個很危險的行為❗️❗️❗️❗️❗️❗️

假如執行程式時 你設定引腳輸出為高電平,而程式結束時你未釋放引腳它將保持這一狀態,一旦意外接觸到該引腳與GND將會短路,燒燬你的樹莓派

釋放引腳:

GPIO.cleanup()

注意,同時GPIO.cleanup()也會清除正在使用的引腳編號系統。

輸出

根據上文,進行輸出操作前,應有

import RPi.GPIO  as GPIO 
tryimport 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)是指用微處理器的數位輸出來對類比電路進行控制,是一種對模擬訊號電平進行數位編碼的方法

建立一個 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()

輸入

輸入要比輸出複雜一些

上拉/下拉電阻

如果輸入引腳沒有接其他器件(或者開關關閉),那麼這個引腳將處於浮動的狀態,即可能高電平可能低電平,或者在兩者之間切換。這時候讀取引腳的值沒有意義。所以我們需要上拉/下拉電阻,使引腳狀態確定

  1. 物理方法
    將一個 10K 的電阻連線在輸入通道與 3.3V(上拉)
    或 輸入通道與0V之間(下拉)
  2. 程式方法
 上拉電阻 
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)

檢測到邊緣時執行執行緒回撥函數

  1. 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)
  1. 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)

注意,在該範例中,回撥函數為順序執行而不是同時執行。這是因為當前只有一個程序供回撥使用,而回撥的執行順序是依據它們被定義的順序。

開關防抖

每次按鈕按下時,回撥操作被呼叫不止一次。這種現象被稱作「開關抖動 」。這裡有兩種方法解決開關抖動問題:

  1. 物理方法 將一個 0.1uF 的電容連線到開關上。
  2. 軟體方法防止抖動
    使用軟體方式抖動,可以在您指定的回撥函數中新增 bouncetime= 引數。
    抖動時間需要使用毫秒為單位進行書寫。例如:
    在通道上新增上升臨界值檢測,忽略由於開關抖動引起的小於 200ms 的邊緣操作
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)