張高興的 MicroPython 入門指南:(二)GPIO 的使用

张高兴發表於2024-07-07

目錄
  • 什麼是 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_POWERPin.MED_POWERPin.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  # 可以做一些其他事情

參考

  1. MicroPython documentation:https://docs.micropython.org/en/latest/library/machine.Pin.html
  2. MicroPython for Kids:https://www.coderdojotc.org/micropython/advanced-labs/02-interrupt-handlers

相關文章