2015年4月9日 星期四

Python 按鍵檢測程式


完成了計數器程式後,接著是測試按鍵的程式。程式用來確保接線無誤之外,同時測試怎樣檢測會來得順暢,甚至是確保按鍵時的噪訊處理。以前開發遊戲程式時,在讀取搖桿訊號時會出現噪訊。例如按鍵時,從系統收到的訊號很多時候會像是 0000010101111111,而不會是 000000111111 這麼乾淨。這是硬件無法處理的情況,需要從軟件方面修正。Raspberry Pi 同樣有這種情況。

兩顆按鈕的接線非常簡單,一邊接地,另一方接 GPIO。在網上了解過後,我決定用較多人用的 BCM 作為 GPIO 的編號格式。同時選用了位於接口群右下方的 #20 及 #21 號腳;地線也選用了上兩格的 #18 號腳。它們位於外殻邊沿,能減少阻礙或鬆脫而導致接觸不良。焊接工作很快地完成了。正常來說,按鍵線路需要加入「上拉」或「下拉」設計,我曾向朋友 Peter 請教,了解兩者的分別及用法。主要都是用來確保未按鍵時的值,就像軟件中的 Initialise 一樣。然而,Raspberry Pi 已經內鍵了這兩種設計,GPIO.setup() 就是決定針腳是「上拉」或「下拉」。

我測試了兩款在 Raspberry Pi 的 Python 中的按鍵檢測方法。第一種 GPIO.wait_for_edge() 是等候按鍵時會一直停著,直到狀態成立才能繼續,套用到 #21 號腳;第二種 GPIO.add_event_detect() 是事件方式,當按鍵發生時會直接跳到已登記的程式,套用到 #20 號腳。
#!/usr/bin/python
##------------------------------------------------------------
##  AMIGO Camera Button Test
##  Copyright Pacess Studio, 2015.  All rights reserved.
##------------------------------------------------------------

import RPi.GPIO as GPIO
import time

##------------------------------------------------------------
def buttonPressed(channel):
    print("Button #20 pressed...")

##------------------------------------------------------------
##  Setup pins
GPIO.setmode(GPIO.BCM)
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.add_event_detect(20, GPIO.RISING, callback=buttonPressed, bouncetime=1000)

##------------------------------------------------------------
while True:
    time.sleep(1)

    ##  Pressed
    GPIO.wait_for_edge(21, GPIO.FALLING)
    print("Button #21 pressed...")

    ##  Released
    GPIO.wait_for_edge(21, GPIO.RISING)

##------------------------------------------------------------
GPIO.remove_event_detect(20)
GPIO.cleanup()

基本上兩種方法都能檢測到按鍵,兩種方法都有噪訊問題。但第二種方法因為提供了 bouncetime 設定,使得噪訊問題能較為容易解決;同時又不會使到其他程序停住,所以我決定選用 GPIO.add_event_detect 方法。

2 則留言:

李宜達 提到...

下面是一個用中斷檢測按鍵次數的python程式
* 是出錯的地方下面[ .. ]是出錯訊息
他一直說numS1是local variable
可是我在前面有宣告numS1=0
程式出錯在哪裡?
要如何用中斷檢測出按鍵被按下的次數
程式怎麼寫
請幫忙解惑謝謝

# int-2.py
#!/usr/bin/python3

import RPi.GPIO as GPIO
import time

# MAX7219 pin
DIN = 10
CS = 8
CLK = 11
S1 = 21
S2 = 20
numS1 = 0

# Max7219 register
DECODE_MODE = 0x09
INTENSITY = 0x0A
SCAN_LIMIT = 0x0B
SHUT_DOWN = 0x0C
DISPLAY_TEST = 0x0F

def S1_callback(channel):
print('Rising edge detected on S1')
* numS1 = numS1 + 1
[ UnboundLocalError: local variable 'numS1' referenced before assignment ]

if numS1 == 10:
numS1 = 0
write_max7219(1, numS1)


def main():
# Main program block
GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers
GPIO.setup(DIN, GPIO.OUT) # DIN
GPIO.setup(CS, GPIO.OUT) # CS
GPIO.setup(CLK, GPIO.OUT) # CLK
GPIO.setup(S1, GPIO.IN)
GPIO.setup(S2, GPIO.IN)
# GPIO.setup(S1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # S1
# GPIO.setup(S2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # S2
# numS1 = 0
numS2 = 10
max7219_init()

GPIO.add_event_detect(S1, GPIO.RISING, callback=S1_callback, bouncetime=300)

# add debounce
try:
while True:
print("Waiting for rising edge on port S2")
GPIO.wait_for_edge(S2, GPIO.RISING)
print("Rising edge detected on port S2")
val = GPIO.input(S2)
if val == True:
time.sleep(0.02)
val = GPIO.input(S2)
if val == True:
numS2 = numS2 - 1
if numS2 == 0:
numS2 = 10
while GPIO.input(S2) == True:
pass
write_max7219(2, numS2)
time.sleep(0.5)
except KeyboardInterrupt:
GPIO.cleanup()

def max7219_init():
GPIO.output(CS, 1) # CS=1
GPIO.output(CLK, 0) # CLK=0
GPIO.output(DIN, 0) # DIN=0
write_max7219(SHUT_DOWN, 0x01) # normal:0x01, shutdown:0x00
write_max7219(DISPLAY_TEST, 0x00) # normal:0x00, displat test:0x01
write_max7219(DECODE_MODE, 0x0F) # Dig0~Dig3
write_max7219(SCAN_LIMIT, 0x01) # Dig0~Dig1
write_max7219(INTENSITY, 0x01) # dark

def write_max7219(regNum, data):
GPIO.output(CS, 0) # CS=0 # Enable max7219
write_max7219_byte(regNum)
write_max7219_byte(data)
GPIO.output(CS, 1) # CS=1 # Disable max7219

def write_max7219_byte(temp):
# Send byte to data pins
for i in range(8):
GPIO.output(CLK, 0) # CLK=0
if temp&0x80 == 0x80:
GPIO.output(DIN, 1)
else:
GPIO.output(DIN, 0)
temp <<= 1
GPIO.output(CLK, 1) # CLK=1

if __name__ == '__main__':
main()

Pacess HO 提到...

李宜達你好,剛才方看到你的留言。

你的 numS1 是在函數裡面使用,得在函數裡使用 global numS1 後才能用。

參考:https://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-created-them