一起玩转树莓派(4)——用开关控制蜂鸣器发声

简介: 在本系列的前几篇博客中,我们使用桌面软件控制过红绿灯,也编程点亮过炫彩的3色LED 灯。在这些实验的过程中,相信你对树莓派GPIO引脚如何输出高低电平,以及如何使用PWM技术来控制电压输出等都有了了解。本篇博客,我们将再探索一些树莓派编程更多新鲜好玩的领域,来尝试使用硬件来控制硬件。

一起玩转树莓派(4)——用开关控制蜂鸣器发声

在本系列的前几篇博客中,我们使用桌面软件控制过红绿灯,也编程点亮过炫彩的3色LED 灯。在这些实验的过程中,相信你对树莓派GPIO引脚如何输出高低电平,以及如何使用PWM技术来控制电压输出等都有了了解。本篇博客,我们将再探索一些树莓派编程更多新鲜好玩的领域,来尝试使用硬件来控制硬件。

现在,我们将尝试完成这样一个实验,使用按压开关控制蜂鸣器的发声。将开关元件和蜂鸣器都连接到树莓派,当按下开关时,让蜂鸣器发声,当松开开关时,蜂鸣器停止发声。

一、主角登场

开始动手实验前,我们先来认识一下将要出场的主角们,当然,无论什么实验,树莓派都是当之无愧的第一主角,但我相信你已经对它已经有了足够的了解(如果没有,可以查看本系列的前几篇博客),因此我们将介绍的重点放在两个新登场的“角色”上:蜂鸣器与按压开关。

1.本实验的“主角甲”——有源蜂鸣器

蜂鸣器,顾名思义,其是一种发声元件。广泛应用于计算机、打印机、电话、玩具等电子设备中。

蜂鸣器可以分为“有源蜂鸣器”和“无源蜂鸣器”。其中的源并非指电源,而是指蜂鸣器内部是否有震荡源。有源蜂鸣器自带一个震荡源模块,其使用起来非常简单,只要接通直流电,其就会自动发出声音。而无源蜂鸣器内部没有震荡源,需要外接使用一定频率的方波来驱动其发声。关于无源蜂鸣器更多深入的用法,我们后续博客再专门介绍。这里,我们先着重理解有源蜂鸣器的工作原理。

本实验中,我们采用的是低电平触发的有源蜂鸣器,元件如下图所示:

其有3个引脚,GND引脚用来接地,VCC引脚用来接3.3V的电源,I/O引脚用来进行蜂鸣器是否播放声音的控制。你可能有些疑惑,为什么低电平也可以触发元件的“开”状态,这要归功于PNP型三极管的功劳,对于PNP型三极光,当输入管脚为低电平时,三极管处于导通状态,使得蜂鸣器被加电压,当输入管脚为高电平时,三极管处于截止状态,蜂鸣器无电压。原理如下图所示:

现在,我们先来编写一段简单的Python程序,来使蜂鸣器发出声音。

2. 触发蜂鸣器播放声音

我们使用的蜂鸣器的3个引脚,其中VCC接电源,GND接地,I/O引脚我们可以选择树莓派BCM编码为27的GPIO引脚,通过查表,可以得到其物理编码为13。连线示意如下:

编写Python程序代码如下:

#coding:utf-8

# 导入GPIO控制薄块
import RPi.GPIO as GPIO
# 导入time模块
import time

# 定义引脚
fm = 13
# 设置使用的引脚编码模式
GPIO.setmode(GPIO.BOARD)
# 进行引脚的初始化,因为是低电平触发,初始时设置为高电平
GPIO.setup(fm,GPIO.OUT, initial=GPIO.HIGH)

# 进行一长两短的声音播放
# 播放1秒声音
GPIO.output(fm, GPIO.LOW)
time.sleep(1)
GPIO.output(fm, GPIO.HIGH)
time.sleep(0.1)
# 播放0.5秒声音
GPIO.output(fm, GPIO.LOW)
time.sleep(0.5)
GPIO.output(fm, GPIO.HIGH)
time.sleep(0.1)
# 播放0.5秒声音
GPIO.output(fm, GPIO.LOW)
time.sleep(0.5)
# 停止蜂鸣器播放
GPIO.output(fm, GPIO.HIGH)
GPIO.cleanup()

在树莓派上运行代码,你应该已经可以听到蜂鸣器发出一长两短的鸣叫声了。

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输入引脚中断的相关用法,需要你多多练习。说了这么多,你有没有发现这次我们完成的实验很像生活中的一种东西?没错,就是门铃,你已经可以使用树莓派实现一个简单的门铃器件啦。

专注技术,懂的热爱,愿意分享,做个朋友

QQ:316045346

目录
相关文章
一起玩转树莓派(5)——让蜂鸣器播放音乐
前面博客中,我们尝试使用开关控制有源蜂鸣器的播放。有源蜂鸣器的一大特点是使用简单,无需复杂的程序控制即可发声,然而其缺陷也很明显,其发声的频率是一定的,我们无法通过频率控制器音调高低。本次实验,我们将尝试使用无源蜂鸣器来进行音乐的播放。
1563 0
一起玩转树莓派(5)——让蜂鸣器播放音乐
|
JSON Dart 安全
Flutter App混淆加固、保护与优化原理
Flutter App混淆加固、保护与优化原理
259 0
|
机器学习/深度学习 编译器 TensorFlow
【ASPLOS2024】RECom:通过编译器技术加速推荐模型推理,论文中选并获得荣誉奖项!
2024年5月,关于推荐模型自动编译优化的论文《RECom: A Compiler Approach to Accelerate Recommendation Model Inference with Massive Embedding Columns》在系统领域顶会ASPLOS 2024上中选并进行了展示,并被授予了Distinguished Artifact Award 荣誉,以表彰RECom的易用性与结果的可复现性。
|
存储 网络协议 Linux
聊一聊 Python 的 socket,以及 select、poll、epoll 又是怎么一回事?
聊一聊 Python 的 socket,以及 select、poll、epoll 又是怎么一回事?
737 2
|
自然语言处理 数据可视化 安全
【第十届“泰迪杯”数据挖掘挑战赛】C题:疫情背景下的周边游需求图谱分析 问题一方案及Python实现
第十届“泰迪杯”数据挖掘挑战赛C题的解决方案,涉及疫情背景下周边游需求图谱分析,包括微信公众号文章分类、周边游产品热度分析、本地旅游图谱构建与分析,以及疫情前后旅游产品需求变化分析的Python实现方法。
341 1
【第十届“泰迪杯”数据挖掘挑战赛】C题:疫情背景下的周边游需求图谱分析 问题一方案及Python实现
|
前端开发 Devops 持续交付
【前端自动化新高度】Angular与Azure DevOps完美结合:从零构建持续集成与持续部署的全自动流水线,提升开发效率与软件交付质量!
【8月更文挑战第31天】Angular作为领先的前端框架,以强大功能和灵活性深受开发者喜爱。Azure DevOps提供一站式DevOps服务,涵盖源码管理、持续集成(CI)及持续部署(CD)。本文将指导你如何在Azure DevOps中搭建Angular项目的CI/CD流程,并通过具体示例代码展示整个过程。首先,我们将创建一个Angular项目并初始化Git仓库;然后,在Azure DevOps中设置CI流水线,定义YAML文件以自动化构建和部署流程。最终实现每次提交代码后自动构建并部署至Azure Web App,极大提升了开发效率和软件交付速度,使团队更专注于创新。
230 0
|
存储 缓存 NoSQL
Redis 实战:逐步指南,让你轻松在 Linux 上安装与部署
Redis 实战:逐步指南,让你轻松在 Linux 上安装与部署
1029 2
|
Android开发 Kotlin
android开发,使用kotlin学习Fragment
android开发,使用kotlin学习Fragment
305 0
|
存储 JSON 关系型数据库
mysql8和mysql5的安装过程都有!!!超多图超详细保姆级教程最新教程新手小白轻松上手,带你了解清楚你安装过程的每一个术语(一)
mysql8和mysql5的安装过程都有!!!超多图超详细保姆级教程最新教程新手小白轻松上手,带你了解清楚你安装过程的每一个术语
610 0