2024年10月25日 星期五

MicroPython 學習筆記 : LOLIN D32 開發板的按鈕與 LED 測試

最近在做 MicroPython 測試時主要使用 LOLIN D32 開發板, 此板的主要規格與介面特性如下 :
  • ESP32-WROOM-32 模組
  • 240 MHz 雙核 Tensilica LX6 MPU (算力 600 DMIPS)
  • 520KB SRAM (無 PSRAM)
  • 4MB Flash 記憶體
  • 22 個數位 GPIO 腳 (每一個均有 PWM 功能與計時器中斷輸出)
  • 12 個 ADC (類比至數位轉換) 輸入 : 12~14, 25~27, 32~36, 39 
  • 2 個 DAC (數位至類比轉換) 輸出 : 25 (DAC1), 26 (DAC2) 腳
  • 具備 PH-2 2.0mm 鋰電池充放電插槽 (Lipo 3.7V, 充電電流 500 mA)
其針腳排列如下 :




注意, 我買的是 LOLIN D32, 板子中間有明確標示, 參考 :


市面上還有一款很像的 LOLIN32, 它同樣具有 LiPo 鋰電池插槽, 但方向與 USB 垂直, 針腳排列也不相同, 參考 :


為了確定使用下列程式掃描幾個 GPIO 腳 : 

from machine import Pin  
import time  
pins=[2, 5, 16, 17, 18, 19, 21, 22, 23]
for pin in pins:
    print(f"正在測試 GPIO{pin}...")
    led=Pin(pin_num, Pin.OUT)    
    # 閃爍三次來測試 LED
    for _ in range(3):
        led.value(1)  # 開啟 LED
        time.sleep(0.5)
        led.value(0)  # 關閉 LED
        time.sleep(0.5)
    # 延遲一秒後繼續測試下一個
    time.sleep(1)

結果如下 :
    
正在測試 GPIO2...
正在測試 GPIO5...  (有閃爍) 
正在測試 GPIO16...
正在測試 GPIO17...
正在測試 GPIO18...
正在測試 GPIO19...
正在測試 GPIO21...
正在測試 GPIO22...
正在測試 GPIO23...

可見板上 LED 確實是在 GPIO5, 此 LED 是陽極經內部限流電阻接 Vcc, 因此要輸出 0 才會點亮, 輸出 1 熄滅 :

>>> from machine import Pin   
>>> led=Pin(5, Pin.OUT)      
>>> led.value(1)      # 燈熄滅
>>> led.value(0)      # 燈點亮

下面是在 GPIO14 接一個按鈕 (要接短邊接點, 長邊接點是連通的), 另一端接地, 執行下面程式後, 按下按鈕時 GPIO14 接地點亮板上 LED, 鬆開按鈕則熄滅 :

>>> from machine import Pin   
>>> import time   
>>> button=Pin(14, Pin.IN, Pin.PULL_UP)   # 預設 High 熄滅 LED
>>> led=Pin(5, Pin.OUT)     
>>> while True:      
    state=button.value()
    print(state)      
    led.value(state)  
    time.sleep(0.1)     

結果如下 : 

1
1
1
1
0    (LED 亮)
0    (LED 亮)
1
1
1
1
1
1
1
0    (LED 亮)
0    (LED 亮)
0    (LED 亮)

下列程式修改自之前的測試 :


from machine import Pin
import time

led=Pin(5, Pin.OUT)
button=Pin(14, Pin.IN)
led.value(1)  # 預設熄滅 LED

while True:
    if not button.value():  # 按鈕按下
        led.value(not led.value())    # 原點亮就熄滅, 原熄滅就點亮
        while not button.value():     # 若還按住就不反應
            pass

但有時不靈光, 按下去雖有改變狀態, 但馬上又恢復原狀態, 這是因為按鈕會有機械彈跳現象所致. 這種輪詢法 (Polling) 若要消除彈跳現象必須設置時間差窗口來濾掉彈跳, 例如 :

from machine import Pin
import time

button=Pin(14, Pin.IN, Pin.PULL_UP)   # 預設 High 熄滅 LED
led=Pin(5, Pin.OUT) 
led_state=False  # 記錄 LED 狀態
last_button_state=button.value()  # 紀錄上次按鈕狀態
debounce_delay=50  # 設定防彈跳延遲 (毫秒)
last_press_time=0  # 上次按下按鈕的時間初始值
while True:
    current_button_state=button.value() # 讀取當前按鈕狀態
    # 檢查按鈕是否被按下 (低電位)
    if last_button_state == 1 and current_button_state == 0:        
        current_time=time.ticks_ms()  # 取得當前時間
        # 如果從上次按下後超過 debounce_delay 時間表示按鈕已穩定按下
        if time.ticks_diff(current_time, last_press_time) > debounce_delay:
            led_state=not led_state
            led.value(led_state)  # 切換 LED 狀態            
            last_press_time=current_time  # 更新上次按壓的時間
    last_button_state=current_button_state  # 更新上次按鈕狀態
    time.sleep(0.01)  # 加入一點延遲避免浪費過多 CPU 資源

此程式使用一個 50ms 的時間差窗口來過濾彈跳, 測試結果確實能交替明滅 LED. 

也可以用偵測按鈕按下時觸發 IRQ 中斷, 例如 :

from machine import Pin
import time

def toggle_led(pin):
    global led_state, last_press_time
    current_time=time.ticks_ms()  # 取得當前時間
    # 如果從上次按下後超過 debounce_delay 時間表示按鈕已穩定按下
    if time.ticks_diff(current_time, last_press_time) > debounce_delay:
        led_state=not led_state
        led.value(led_state)  # 切換 LED 狀態            
        last_press_time=current_time  # 更新上次按壓的時間

button=Pin(14, Pin.IN, Pin.PULL_UP)   # 預設 High 熄滅 LED
led=Pin(5, Pin.OUT) 
led_state=False  # 記錄 LED 狀態
debounce_delay=50  # 設定防彈跳延遲 (毫秒)
last_press_time=0  # 上次按下按鈕觸發中斷的時間初始值
button.irq(trigger=Pin.IRQ_FALLING, handler=toggle_led) # 偵測按鈕下降沿(按下)

while True:  # 等待中斷觸發
    time.sleep(0.1)

但測試發現效果沒有 Polling 那麼好. 

參考 : 


沒有留言:

張貼留言