3.本实验的“主角乙”——按压开关
按压开关是电子设备中最为常用的一种元件,关于按压开关本身的结构和原理,其实非常简单,其按键内部会有弹簧进行控制,当没有外力作用时,弹簧会将开关自动顶起,接触器上升。当外力将按键按下时,接触器下降。通过接触器的上升和下降来控制电路的导通。如下图所示:
本次实验,我们使用的按压开关元件如下图所示:
我们使用的此开关当按键按下式,会向引脚输入低电平,当按键松开时,会向引脚输入低电平。在连线时,按压开关的“-”引脚接地,中间引脚节3.3V电压,“S”引脚接GPIO信号引脚,我们可以将其接BCM编码为GPIO5的引脚,其物理编码为29。连线如下图所示:
下面,我们要来学习点新东西了,如何获取到GPIO引脚的输入。
4. 使用GPIO输入信号
之前,我们都是将GPIO作为输出引脚进行使用,即让GPIO输出高电平或低电平从而控制元件。其实还有很多场景,需要读取GPIO的输入信号,例如各种传感器和开关元件。通常,我们可以采用两种方式来获取GPIO的输入信号:轮询式或中断式。
轮询式很好理解,即我们不停的询问当前GPIO引脚:输入的信号是什么啊?之后GPIO会将当前的输入信息告诉我们,这种不停的读取某个GPIO引脚的值的方式就是轮询式获取输入信号。更多时候,我们会采取中断式,中断式是指在程序运行过程中,如果出现外部事件,则中断当前程序运行来处理外部事件。在本实验中,开关的按下和松开就可以理解为中断事件。
需要注意,对于作为输入使用的GPIO引脚来说,当我们未接任何输入源时,其值可能是任意的,即其值是浮动且不可控的,因此我们需要为其配置一个默认值,硬件上,我们通常使用下拉电阻或上拉电阻。软件上我们也可以通过GPIO库的相关接口来实现同样的功能,例如使用下面的方式初始化引脚:
# 将引脚初始化为输入引脚,同时设置默认值为高电平
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# 将引脚初始化为输入引脚,同时设置默认值为低电平
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
若要获取到某个输入引脚当前的电平情况,可以使用如下方法:
# 1表示高电平 0表示低电平
GPIO.input(channel)
例如可以通过如下的方式轮训检查某个引脚的输入:
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01)
我们通常只需要关心电平的变化,以及变化方向,以本实验为例,当按下开关时,电平由低变高,当松开开关时,电平由高到低。下面方法可以中断当前程序的运行,等待电平变化的信号:
# 当channel引脚由低电平变到高电平时会触发向后执行, timeout设置超时时间
GPIO.wait_for_edge(channel, GPIO.RISING, timeout=5000)
GPIO.RISING表示要等待的是由低到高的电平变化,与之对应的还有GPIO.FALLING表示由高到低的电平变化,GPIO.BOTH表示两个方向的电平变化都会触发。
对于本实验的场景,GPIO.wait_for_edge方法并不实用,如果我们要采用轮询的方式获取GPIO输入信号,则可以实用下面的方法:
# 为channel输入引脚添加[从低到高]的电平变化监听
GPIO.add_event_detect(channel, GPIO.RISING)
# 下面的判断可以放在轮询中,只要有[从低到高]的电平变化,即会命中if判断
if GPIO.event_detected(channel):
print('从低到高变化')
需要注意,GPIO.add_event_detect方法比直接轮询通过GPIO.input方法获取信号要可靠的多,直接轮询可能会错过某些电平变化事件,而GPIO.add_event_detect 不会。在使用GPIO.add_event_detect方法时,我们也可以直接设置回调函数,当有变化发生时,即会执行到回调函数,例如:
# 定义回调函数
def my_callback(channel):
pass
# 注册回调函数 bouncetime参数用来进行防抖
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
上面示例代码中,bouncetime参数能够提供软件防抖的功能,其单位为毫秒,200毫秒内的多次电平变化来回将被忽略。
最后,如果你不再需要监听,可以使用如下方法移除:
GPIO.remove_event_detect(channel)
5. 获取按压开关元件的状态
现在,我们可以尝试使用树莓派读取按压开关元件的开关状态了。示例代码如下:
#coding:utf-8
# 导入GPIO控制薄块
import RPi.GPIO as GPIO
# 导入time模块
import time
# 设置使用的引脚编码模式
GPIO.setmode(GPIO.BOARD)
# 定义开关引脚
swi = 29
# 进行开关引脚的初始化,设置为输入引脚,且默认为高电平
GPIO.setup(swi, GPIO.IN, pull_up_down=GPIO.PUD_PU)
# 定义状态变化的回调函数
def switch(channel):
# 高电平的开关松开
if GPOI.input(channel):
print("开关松开")
# 低电平为开关按下
else:
print("开关按下")
# 添加输入引脚电平变化的回调函数
GPIO.add_event_detect(swi, GPIO.BOTH, callback=switch, bouncetime=200)
# 开启循环
while True:
pass
在树莓派运行上面代码,通过按压和松开开关,观察控制台的输出是否正确。
二、开关控制蜂鸣器播放
如果你成功做完了前面的工作,那么好了,本节的内容将非常简单,只需要将上面编写的蜂鸣器程序和开关程序进行结合,你就可以使用开关来控制蜂鸣器了。示例代码如下:
#coding:utf-8
# 导入GPIO控制薄块
import RPi.GPIO as GPIO
# 导入time模块
import time
# 设置使用的引脚编码模式
GPIO.setmode(GPIO.BOARD)
# 定义开关引脚
swi = 29
# 定义蜂鸣器引脚
fm = 13
# 进行开关引脚的初始化,设置为输入引脚,且默认为高电平
GPIO.setup(swi, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# 进行蜂鸣器引脚的初始化,因为是低电平触发,初始时设置为高电平
GPIO.setup(fm,GPIO.OUT, initial=GPIO.HIGH)
# 定义状态变化的回调函数
def switch(channel):
# 高电平的开关松开
if GPI0.input(channel):
GPIO.output(fm, GPIO.HIGH)
# 低电平为开关按下
else:
GPIO.output(fm, GPIO.LOW)
# 添加输入引脚电平变化的回调函数
GPIO.add_event_detect(swi, GPIO.BOTH, callback=switch, bouncetime=200)
# 开启循环
while True:
pass
现在,运行程序,用开关来控制蜂鸣器的声音播放吧。恭喜!你已经能够使用硬件来控制硬件了!
三、休息一下吧
通过本篇博客,你应该收获了GPIO输入信号的编程方法,以及通过GPIO读取有输入功能的传感器数据的方法。本篇博客涉及的内容很多,重点在于GPIO输入引脚中断的相关用法,需要你多多练习。说了这么多,你有没有发现这次我们完成的实验很像生活中的一种东西?没错,就是门铃,你已经可以使用树莓派实现一个简单的门铃器件啦。