订阅趣无尽微信公众号

使用 RPi.GPIO 模块的输入(Input)功能

这篇日志的内容应该算是《RPi.GPIO 模块使用基础》Input 部分的扩展讲解,详细讲解了 Input 部分的一些高级应用技巧。
目前有几种途径可以在您的程序中获得 GPIO 的输入信息。第一种也是最简易的一种为在某个时间点检查输入值。这即是所谓的“轮询(polling)”,而且如果您的程序在错误的时间里进行了读取,可能会错过某个输入值。在循环中运用轮询,有可能使处理器资源紧张。另一种对 GPIO 输入进行响应的方式可以使用“中断(interruots)”(边缘检测(edge detection))。边缘可以是从 HIGH 到 LOW 的过度(下降临界值(falling edge))或从 LOW 到 HIGH 的过度(上升临界值(rising edge))。

上拉/下拉电阻

如果您在输入针脚上没有连接任何元件,那么它将是“浮动(float)”的。换句话说,因为您没有连接任何元件,在按下按钮或开关之前,读取的值是没有意义的。由于电源的波动,获取到的值可能会有很大的变化。

为了解决这个问题,我们需要使用上拉/下拉电阻。这样,我们就可设定输入的默认值了。在这里,可以使用硬件或软件对电阻进行上拉/下拉。使用硬件方式,将一个 10K 的电阻连接在输入通道与 3.3V(上拉)或 0V(下拉)之间是常用的做法。而 RPi.GPIO 也允许您通过软件的方式对配置 Broadcom SOC 来达到目的:

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)

或者

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

(通道编号是基于您所使用的编号系统所指定的(BOARD 或 BCM)。)

输入测试(轮询(polling))

您可以在某个时间点获得一次输入的快照:

if GPIO.input(channel):
    print('Input was HIGH')
else:
    print('Input was LOW')

在循环中等待按钮被按下后进行轮询:

while GPIO.input(channel) == GPIO.LOW:
    time.sleep(0.01)  # 为 CPU 留出 10 毫秒,供其处理其它事物

(这里假设为当按下按钮时,输入状态从 LOW 到 HIGH)

中断和边检检测

边缘的定义为电信号从 LOW 到 HIGH(上升临界值)或从 HIGH 到 LOW(下降临界值)状态的改变。正常情况下,对于输入的值来说,我们更关心的是输入的状态是否发生了改变。这种状态上的改变是很重要的。

为了避免您的程序在忙于处理其它的事物时而错过了您按下按钮的操作,这里有两种方法可以解决:

wait_for_edge() 函数
event_detected() 函数
在检测到边缘时执行线程回调函数

wait_for_edge() 函数

wait_for_edge() 函数被设计用于在检测到边缘之前阻止程序的运行。换句话说,上面的示例中,等待按钮被按下的语句可以改写为:

GPIO.wait_for_edge(channel, GPIO.RISING)

注意,您可以输入 GPIO.RISING、GPIO.FALLING、GPIO.BOTH 对边缘进行检测。这种方式的优点是占用 CPU 资源很少,因此系统可以有充裕的资源处理其它事物。

event_detected() 函数 event_detected() 函数被设计用于循环中有其它东西时使用,但不同于轮询的是,它不会错过当 CPU 忙于处理其它事物时输入状态的改变。这在类似使用 Pygame 或 PyQt 时主循环实时监听和响应 GUI 的事件是很有用的。

GPIO.add_event_detect(channel, GPIO.RISING)  # 在通道上添加上升临界值检测
do_something()
if GPIO.event_detected(channel):
    print('Button pressed')

注意,您可以输入 GPIO.RISING、GPIO.FALLING、GPIO.BOTH 对边缘进行检测。

线程回调

RPi.GPIO 在第二条线程中执行回调函数。这意味着回调函数可以同您的主程序同时运行,并且可以立即对边缘进行响应。例如:

def my_callback(channel):
    print('这是一个边缘事件回调函数!')
    print('在通道 %s 上进行边缘检测'%channel)
    print('该程序与您的主程序运行在不同的进程中')
 
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # 在通道上添加上升临界值检测
... 其它程序代码 ...

如果您需要多个回调函数:

def my_callback_one(channel):
    print('回调 1')
 
def my_callback_two(channel):
    print('回调 2')
 
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)

注意,在该示例中,回调函数为顺序运行而不是同时运行。这是因为当前只有一个进程供回调使用,而回调的运行顺序是依据它们被定义的顺序。

开关防抖

您可能会注意到,每次按钮按下时,回调操作被调用不止一次。这种现象被称作“开关抖动(switch bounce)”。这里有两种方法解决开关抖动问题:

将一个 0.1uF 的电容连接到开关上。
软件防止抖动
两种方式一起用
使用软件方式抖动,可以在您指定的回调函数中添加 bouncetime= 参数。
抖动时间需要使用毫秒为单位进行书写。例如:

# 在通道上添加上升临界值检测,忽略由于开关抖动引起的小于 200ms 的边缘操作
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)

或者

GPIO.add_event_callback(channel, my_callback, bouncetime=200)
remove_event_detect()

由于某种原因,您不希望您的程序检测边缘事件,您可以将它停止:

GPIO.remove_event_detect(channel)

相关网站

原文地址:RPi.GPIO module Inputs
项目地址:RPi.GPIO
原文翻译:dreamcolor.net

本文来自:树莓派实验室
链接地址:http://shumeipai.nxez.com/2016/09/28/rpi-gpio-module-inputs.html

2 Responses

  1. Lucky 2016年10月14日 / 14:38

    文章很好很实用,我的声控功能有救了,大赞一下!

  2. jiaxv 2016年11月21日 / 14:54

    很好的文章,也提到了一些电子知识。

发表评论

电子邮件地址不会被公开。 必填项已用*标注