國外的設計接口設計得很棒,包括問題:讀臟與防抖,還包括讀這個數據提供了兩種方式,一種是阻塞等待方式,還有一種是回調函數,前一種是通訊中常用的方式,后一種來自系統架構設計的整體性考慮。這種硬件接口設計的思路值得我們學習。
Inputs(輸入)
There are several ways of getting GPIO input into your program. The first and simplest way is to check the input value at a point in time. This is known as 'polling' and can potentially miss an input if your program reads the value at the wrong time. Polling is performed in loops and can potentially be processor intensive. The other way of responding to a GPIO input is using 'interrupts' (edge detection). An edge is the name of a transition from HIGH to LOW (falling edge) or LOW to HIGH (rising edge).
中文速讀:有很多方式將gpio輸入我們的程序,最簡單的是輪詢,這種方式在不同時間采集數據會丟失,還多花費CPU資源,另一種方式為中斷,中斷依據邊緣進行觸發(數字電路中學過的)到么下邊界觸發,要么上邊界觸發。
Pull up / Pull down resistors(下拉、下拉電阻)
If you do not have the input pin connected to anything, it will 'float'. In other words, the value that is read in is undefined because it is not connected to anything until you press a button or switch. It will probably change value a lot as a result of receiving mains interference.
To get round this, we use a pull up or a pull down resistor. In this way, the default value of the input can be set. It is possible to have pull up/down resistors in hardware and using software. In hardware, a 10K resistor between the input channel and 3.3V (pull-up) or 0V (pull-down) is commonly used. The RPi.GPIO module allows you to configure the Broadcom SOC to do this in software:
中文速讀:當接口沒接任何設備時,這個值讀出來可能有多種結果,這是“變動的”,解決它我們可以用下拉或下拉數阻,通常10k數阻。那么gpio通過如下形式配置:
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP) # or GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
(where channel is the channel number based on the numbering system you have specified - BOARD or BCM).
Testing inputs (polling)測試輸入
You can take a snapshot of an input at a moment in time:
if GPIO.input(channel): print('Input was HIGH') else: print('Input was LOW')
To wait for a button press by polling in a loop:
while GPIO.input(channel) == GPIO.LOW: time.sleep(0.01) # wait 10 ms to give CPU chance to do other things慢0.01讓cpu去做其它事,其它進程的並發性能考慮
(this assumes that pressing the button changes the input from LOW to HIGH)
中文速讀:這里假設按鍵從低到高改變輸入
Interrupts and Edge detection(中斷與邊界檢測)
An edge is the change in state of an electrical signal from LOW to HIGH (rising edge) or from HIGH to LOW (falling edge). Quite often, we are more concerned by a change in state of an input than it's value. This change in state is an event.
To avoid missing a button press while your program is busy doing something else, there are two ways to get round this:
中文速讀:改變邊界狀態是從電平信號從低到高,或者從高到低,與此同時,我們更關心的是輸入的狀態值,這種改變就是事件,為了避免並發丟失,即事件丟失,gpio提供了兩種方式來解決忙中丟失問題:阻塞等待與線程回調函數
- the wait_for_edge() function
- the event_detected() function
- a threaded callback function that is run when an edge is detected
wait_for_edge() function(阻斷意外,等邊界檢測)
The wait_for_edge() function is designed to block execution of your program until an edge is detected. In other words, the example above that waits for a button press could be rewritten as:
GPIO.wait_for_edge(channel, GPIO.RISING)
Note that you can detect edges of type GPIO.RISING, GPIO.FALLING or GPIO.BOTH. The advantage of doing it this way is that it uses a negligible amount of CPU, so there is plenty left for other tasks.
If you only want to wait for a certain length of time, you can use the timeout parameter:
# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000) if channel is None: print('Timeout occurred') else: print('Edge detected on channel', channel)
中文速讀:這種方式是指阻塞了,等了多長時間可以在GPIO上設timeout參數,這種適用於異步工作方式,我們不清楚要等多少時間,因些應用場景對實時要求不高的。
event_detected() function
The event_detected() function is designed to be used in a loop with other things, but unlike polling it is not going to miss the change in state of an input while the CPU is busy working on other things. This could be useful when using something like Pygame or PyQt where there is a main loop listening and responding to GUI events in a timely basis.
GPIO.add_event_detect(channel, GPIO.RISING) # add rising edge detection on a channel do_something() if GPIO.event_detected(channel): print('Button pressed')
Note that you can detect events for GPIO.RISING, GPIO.FALLING or GPIO.BOTH.
中文速讀:主動測試
Threaded callbacks
RPi.GPIO runs a second thread for callback functions. This means that callback functions can be run at the same time as your main program, in immediate response to an edge. For example:
def my_callback(channel): print('This is a edge event callback function!') print('Edge detected on channel %s'%channel) print('This is run in a different thread to your main program') GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback) # add rising edge detection on a channel ...the rest of your program...
If you wanted more than one callback function:
def my_callback_one(channel): print('Callback one') def my_callback_two(channel): print('Callback two') GPIO.add_event_detect(channel, GPIO.RISING) GPIO.add_event_callback(channel, my_callback_one) GPIO.add_event_callback(channel, my_callback_two)
Note that in this case, the callback functions are run sequentially, not concurrently. This is because there is only one thread used for callbacks, in which every callback is run, in the order in which they have been defined.
中文速讀:就是回調函數
Switch debounce(消抖)
You may notice that the callbacks are called more than once for each button press. This is as a result of what is known as 'switch bounce'. There are two ways of dealing with switch bounce:
- add a 0.1uF capacitor across your switch.
- software debouncing
- a combination of both
To debounce using software, add the bouncetime= parameter to a function where you specify a callback function. Bouncetime should be specified in milliseconds. For example:
# add rising edge detection on a channel, ignoring further edges for 200ms for switch bounce handling
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
or
GPIO.add_event_callback(channel, my_callback, bouncetime=200)
Remove event detection(取消事件測試)
If for some reason, your program no longer wishes to detect edge events, it is possible to stop them:
GPIO.remove_event_detect(channel)