- 什麼是 GPIO
- 使用方法
- 使用微動開關點亮板載 LED
- 硬體需求
- 電路
- 程式碼
- 參考
什麼是 GPIO
GPIO 是 General Purpose Input Output 的縮寫,即“通用輸入輸出”。 Raspberry Pi Pico 左右兩側各有一列 GPIO 引腳, Pico 透過這兩列引腳進行一些硬體上的擴充套件,與感測器進行互動等等。
簡單的講,每一個 GPIO 引腳都有兩種模式:輸出模式(OUTPUT)和輸入模式(INPUT)。輸出模式類似於一個電源,Pico 可以控制這個電源是否向外供電,比如開啟外部的 LED 小燈,當然最有用的還是向外部裝置傳送訊號。和輸出模式相反,輸入模式是接收外部裝置發來的訊號。GPIO 通常採用標準邏輯電平,即高電平和低電平,用二進位制 0 和 1 表示。在這兩個值中間還有閾值電平,即高電平和低電平之間的界限。Arduino 會將 -0.5 ~ 1.5 V 讀取為低電平,3 ~ 5.5 V 讀取為高電平, Pico 未查到相關資料。GPIO 還可用於中斷請求,即設定 GPIO 為輸入模式,值達到相應的要求時進行中斷。
輸入模式還包含兩種特殊的輸入模式:上拉輸入(INPUT_PULLUP)和下拉輸入(INPUT_PULLDOWN)。上拉輸入就是晶片內部的電阻連線 VCC ,將該引腳設定為高電平狀態。在沒有外部訊號輸入的情況下,上拉輸入可以保持引腳處於高電平狀態,從而避免了訊號的不確定性。在上拉輸入模式中,如果外部輸入了低電平訊號,由於電阻的存在,引腳會讀取到低電平,但不會產生大電流。這樣,微控制器可以輕易穩定地讀取低電平訊號。上拉輸入的優勢在於它可以提供穩定的高電平狀態,直到檢測到明確的低電平輸入。下拉輸入則相反,是將晶片內部的電阻連線 GND,將引腳設定為低電平狀態,也是為了避免了訊號的不確定性。上拉、下拉輸入模式適用於一些特定場合,例如需要檢測按鈕按壓(通常連線到低電平)或其他二進位制開關狀態。
使用方法
使用 MicroPython 控制 GPIO 要使用 machine
包中的 Pin
類。
from machine import Pin
要獲取引腳物件,我們先來看一看建構函式有哪些引數。
Pin(id: Any, mode: int = -1, pull: int = -1, *, value: Optional[int] = None, drive: Optional[int] = None, alt: Optional[int] = None)
id
是指引腳的編號。對於 Pico 而言就是引腳圖中GPxx
中的數字編號,這個引數是必填的。mode
是指引腳的模式。常用的有Pin.IN
輸入模式,Pin.OUT
輸出模式。pull
用來設定引腳輸入的模式。Pin.PULL_UP
上拉輸入,Pin.PULL_DOWN
下拉輸入,None
不設定。value
設定輸出引腳的預設狀態。引數可以是任何轉換為布林值的變數,1
預設輸出高電平,0
預設輸出低電平。drive
指定引腳的輸出功率。引數可以是Pin.LOW_POWER
、Pin.MED_POWER
、Pin.HIGH_POWER
,實際的輸出功率還是取決於具體的引腳。
常見的用法參考下面的例子:
pin0 = Pin(0, mode=Pin.IN) # 設定 0 號引腳為輸入模式
pin1 = Pin(1, mode=Pin.IN, pull=Pin.PULL_UP) # 設定 1 號引腳為上拉輸入模式
pin2 = Pin(2, mode=Pin.OUT, value=0) # 設定 2 號引腳為輸出模式,預設輸出低電平
在獲取到引腳物件後,可以使用 Pin.value(x: Optional[int] = None)
方法設定或讀取引腳的值。引數 x
可以是任何轉換為布林值的變數,1
輸出高電平,0
輸出低電平。當不傳遞引數 x
時,方法為讀取引腳的值。
pin2.value(1) # 設定 2 號引腳輸出高電平
print(pin1.value()) # 讀取 1 號引腳的輸入值
當想要重新改變引腳的設定時,除了重新例項化物件之外,還可以使用 Pin.init(value: int, drive: int, alt: int, mode: int = -1, pull: int = -1)
、Pin.mode(mode: Optional[int])
、Pin.pull(pull: Optional[int])
對引腳重新設定。
pin0.mode(Pin.OUT) # 設定 0 號引腳為輸出模式
pin1.pull(Pin.PULL_DOWN) # 設定 1 號引腳為下拉輸入模式
pin2.init(mode=Pin.IN, pull=Pin.PULL_UP) # 設定 2 號引腳為上拉輸入模式
有些時候當外部輸入訊號發生改變時,微控制器需要執行一些操作,比如按下開關時亮燈。這就需要用到中斷,中斷是來自裝置的一個訊號,通知處理器暫停當前正在執行的任務,以優先處理該訊號代表的工作,在處理完中斷請求後,處理器才會恢復之前的任務。要設定引腳的中斷,可以使用 Pin.irq(handler: Callable[[Pin], Any] = None, trigger: int = (IRQ_FALLING | IRQ_RISING), priority: int = 1, wake: int = None)
方法。
handler
是中斷觸發時要呼叫的方法。trigger
用來設定觸發中斷的條件。Pin.IRQ_FALLING
下降沿(高電平變低電平)觸發,Pin.IRQ_RISING
上升沿(低電平變高電平)觸發,Pin.IRQ_LOW_LEVEL
低電平觸發,Pin.IRQ_HIGH_LEVEL
高電平觸發。這些值可以一起進行或運算,從而設定多條件觸發。priority
中斷的優先順序,值越大優先順序越高。wake
設定中斷喚醒系統的電源模式。machine.IDLE
空閒狀態,可以快速恢復裝置的執行,machine.SLEEP
淺睡眠狀態,喚醒後從請求睡眠的點恢復執行,machine.DEEPSLEEP
深睡眠狀態,喚醒後類似硬重置,程式從頭執行。
# 設定 0 號引腳為上拉輸入模式
pin = Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
# 定義一箇中斷處理函式
def irq_handler(pin, event):
print(pin, event)
# 設定引腳的中斷,觸發條件為下降沿
pin.irq(handler=irq_handler, trigger=machine.Pin.IRQ_FALLING)
使用微動開關點亮板載 LED
給 Pico 接入一個外部開關,當按下開關時,板載的 LED 小燈點亮。
硬體需求
名稱 | 數量 |
---|---|
微動開關 | x1 |
杜邦線 | 若干 |
電路
微動開關
- 引腳 1 - GP2
- 引腳 2 - GND
程式碼
將 Pico 的 GP2
引腳配置成上拉輸入模模式,將開關的一端與 GP2
連線。由於上拉輸入在沒有外部輸入時讀取始終為高電平訊號,因此開關的另一段需要連線 GND
。當按下開關時 GP2
引腳會檢測到低電平訊號。那麼怎樣去不斷檢測開關是否被按下?最簡單的一種方式,可以使用 while 迴圈,在迴圈體內不停讀取 GP2
的值,從而判斷開關的狀態。具體的程式碼如下。
from machine import Pin
# 初始化引腳
button = Pin(2, mode=Pin.IN, pull=Pin.PULL_UP)
led = Pin('LED', mode=Pin.OUT)
# 在迴圈體內不停讀取,當檢測到低電平訊號時,表明開關被按下
while True:
if not button.value():
led.value(1)
else:
led.value(0)
這個程式雖然能夠實現效果,但也有一個致命問題:不斷的迴圈使得程式只能檢測開關是否被按下,而做不了任何其他事情。使用中斷可以將檢測與 CPU 處理完全分離,無需不斷掃描引腳的值。當硬體檢測到訊號更改時,中斷都會在訊號變化後觸發功能執行。具體的程式碼如下。
from machine import Pin
# 初始化引腳
button = Pin(2, mode=Pin.IN, pull=Pin.PULL_UP)
led = Pin('LED', mode=Pin.OUT)
# 定義一箇中斷服務方法,當檢測到低電平訊號時,改變 LED 的狀態
def button_isr(pin):
led.value(not led.value())
# 配置中斷,下降沿觸發
button.irq(trigger=Pin.IRQ_FALLING, handler=button_isr)
while True:
pass # 可以做一些其他事情
在執行上面的程式碼時,你可能已經注意到,按下按鈕後 LED 存在閃爍的現象,這是為什麼?是程式碼的錯誤嗎?這是因為按鈕並不是一個完美的開關,由於機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上穩定地接通,在斷開時也不會一下子斷開,因而在閉合及斷開的瞬間均伴隨有一連串的“抖動”。訊號從穩定狀態移動,經過不穩定的過渡狀態,最終到達新的穩定狀態,如下圖所示。
針對這種抖動現象,可以透過硬體進行去除,比如利用電容的充放電平滑的補償訊號的抖動。也可以利用軟體進行去抖,訊號抖動的狀態有時間限制,新增一個短暫的延時再去檢測電平訊號。
from machine import Pin
import utime
last_time = 0 # 記錄按下的時間
# 初始化引腳
button = Pin(2, mode=Pin.IN, pull=Pin.PULL_UP)
led = Pin('LED', mode=Pin.OUT)
# 定義一箇中斷服務方法,當檢測到低電平訊號時,改變 LED 的狀態
def button_isr(pin):
global last_time
new_time = utime.ticks_ms()
# 延時
if (new_time - last_time) > 50:
led.value(not led.value())
last_time = new_time
# 配置中斷,下降沿觸發
button.irq(trigger=Pin.IRQ_FALLING, handler=button_isr)
while True:
pass # 可以做一些其他事情
參考
- MicroPython documentation:https://docs.micropython.org/en/latest/library/machine.Pin.html
- MicroPython for Kids:https://www.coderdojotc.org/micropython/advanced-labs/02-interrupt-handlers