编码不仅仅是在屏幕上做事情,你还可以控制连接到树莓派的 GPIO 引脚上的电子元件。
当人们想到“编程”或“编码”时,他们通常——而且自然地想到的是软件。不过,编程不仅仅是关于软件:它还可以通过硬件影响现实世界。这就是所谓的硬件交互。顾名思义,硬件交互就是用你的程序控制现实世界中的事物——硬件,而不是软件。当你在洗衣机上设置程序,改变可编程恒温器的温度,或者在交通灯下按下按钮安全过马路时,你就在使用硬件交互。
树莓派是学习硬件交互的一个很好的设备,这要归功于一个关键功能:通用输入/输出(GPIO)接口。
GPIO 接口介绍
GPIO(通用输入/输出)接口位于树莓派电路板的顶部边缘,或者 Raspberry Pi 400 的背面,看起来像两排长长的金属针脚,你可以通过它将 LED 和开关等硬件连接到树莓派上,以便在你创建的程序下进行控制。这些引脚可以用于输入和输出。
树莓派的 GPIO 接口是由40个公引脚组成的。有些针脚可供你在硬件交互项目中使用,有些针脚提供电源,而其他针脚则是为与 Sense HAT 等附加硬件进行通信而保留的(见第7章)。
Raspberry Pi 400 有相同的 GPIO 接口,有所有相同的引脚,但与其他树莓派型号相比,它被倒置了。这张图假设你是从 Raspberry Pi 400 的背面看 GPIO 接口的。在将任何东西连接到 Raspberry Pi 400 的 GPIO 接口时,一定要仔细检查你的接线——尽管外壳上有“Pin 40”和“Pin 1”的标签,但还是很容易忘记!
GPIO 扩展
使用 Raspberry Pi 400 的 GPIO 接口是完全可能的,但你可能会发现使用一个扩展器更容易。有了扩展器,引脚就会被带到 Raspberry Pi 400 的侧面,这意味着你可以检查和调整你的接线,而不必一直要把设备翻过来。 兼容的扩展器包括 pimoroni.com 的 Black HAT Hack3r 系列和 adafruit.com 的 Pi T-Cobbler Plus。 如果你买了一个扩展,一定要检查它是如何接线的——有些扩展比如 Pi T-Cobbler Plus,改变了GPIO引脚的布局。当有疑问时,注意使用制造商的说明。
有几类引脚类型,每一类都有一个特定的功能。
3V3 | 3.3伏电源 | 一个永久开启的3.3V电源,与树莓派内部运行的电压相同。 |
---|---|---|
5V | 5伏电源 | 一个永久接通的5V电源,与树莓派在微型 USB 电源连接器上获得的电压相同。 |
地线(GND) | 0伏地线 | 一个地线连接,用于完成与电源连接的电路。 |
GPIO XX | GPIO 引脚编号“XX” | 可用于你的程序的GPIO引脚,由2至27的数字标识。 |
ID EEPROM | 保留特殊用途引脚 | 预留的针脚,用于硬件附加(HAT)和其他配件的使用。 |
警告!
树莓派的 GPIO 是实验硬件交互的一种有趣而安全的方式,但必须小心对待它。在连接和断开硬件时,要小心不要弯曲引脚。除非在项目说明中明确告知,否则千万不要将两个引脚直接连接在一起:这被称为短路,根据引脚的不同,可能会永久性地损坏你的树莓派。
电子元件
GPIO 接口只是你开始进行硬件交互工作所需的一部分;另一半是由电子元件组成的,也就是你将从 GPIO 头控制的设备。有数以千计的不同组件可供选择,但大多数 GPIO 项目都使用以下常见的部件。
面包板,也被称为无焊面包板,可以使硬件交互项目大大简化。面包板不是有一堆需要用电线连接的独立元件,而是让你插入元件并通过隐藏在其表面下的金属轨道将它们连接。许多面包板还包括用于配电的部分,使你的电路更容易建立。你不需要面包板来开始进行硬件交互,但它肯定会有帮助。
跳线,也被称为杜邦线,将元件连接到你的树莓派上,如果你不使用面包板,可以使用跳线连接各个元件。它们有三种版本:公对母(M2F),你需要用它来连接面包板和 GPIO 引脚;母对母(F2F),如果你不使用面包板,它可以用来把单个元件连接在一起;公对公(M2M),用来从面包板的一个部分连接到另一个。根据你的项目,你可能需要所有三种类型的跳线;如果你使用面包板,你通常可以只使用 M2F 和 M2M 跳线。
按钮开关,也被称为瞬时开关,是你用来控制游戏机的那种开关。常见的有两个或四个引脚——两种类型都可以与树莓派一起使用,按钮是一种输入设备:你可以告诉你的程序侦测到它被按下后执行一项任务。另一种常见的开关类型是闭锁开关;而按钮只在你按住它的时候处于激活状态,但闭锁开关——就像你在电灯开关中体验到的那样,在你拨动它一次时激活,然后保持激活,直到你再次拨动它关闭。
发光二极管(LED)是一个输出设备,你可以直接从你的程序中控制它。发光二极管开着的时候会亮起来,你会发现它们在你周围到处都是,小到让你知道你的洗衣机什么时候在运行,大到可能把你的房间照亮。LED 有各种各样的形状、颜色和尺寸,但并不是所有的 LED 都适合与树莓派一起使用:避免使用那些标明是为5V或12V电源而设计的产品。
电阻器是控制电流流动的元件,有不同的数值,用一个叫欧姆(Ω)的单位来衡量。欧姆数越高,提供的电阻就越大。对于树莓派硬件交互项目来说,它们最常见的用途是保护 LED,防止其接收到过多的电流而损坏自己或你的树莓派;为此,你需要额定值为 330Ω 左右的电阻,尽管许多电器供应商出售包含若干不同常用值的方便包,以便你有灵活的选择。
压电式蜂鸣器,通常只被称为蜂鸣器或发声器,是另一种输出设备。不过,LED 产生的是光,而蜂鸣器产生的是噪音——事实上是嗡嗡的噪音。蜂鸣器的塑料外壳内有一对金属薄片;活动时,这些金属片相互振动,产生嗡嗡声。蜂鸣器有两种类型:主动蜂鸣器和被动蜂鸣器。确保有一个可用的蜂鸣器,因为这些是最简单的使用方法。
其他常见的电子元件包括还有:电机,在连接到树莓派之前需要一个特殊的控制板;检测运动的红外传感器;可用于侦测天气的温度和湿度传感器;以及光敏电阻(LDR),通过检测光线像反向 LED 一样工作的输入设备。
世界各地的卖家都提供使用树莓派进行硬件交互的部件,可以是单独的部件,也可以是提供你开始使用所需的一切的套件。要找到卖家,请访问 rpf.io/products,点击 Raspberry Pi 4,你会得到你所在国家或地区的树莓派合作在线商店(认可的经销商)的列表。
要完成本章的项目,你至少应该有:
电阻器的数值范围很广,从零电阻版本(实际上只是几根电线)到高电阻版本(像你的大腿一样粗大)。不过,很少有电阻的数值是以数字形式印在上面的:相反,它们使用的是印在电阻体周围的彩色条纹或带子的特殊代码。
要读出一个电阻的数值,要把它放在一组带子的左边,孤独的带子在右边。从第一条带子开始,在表格的 “第1、2条带子 “栏中查找其颜色,以获得第一和第二位数字。这个例子有两个橙色的带子,这两个带子都意味着数值为 “3”,总共为 “33”。如果你的电阻有四个分组带,而不是三个,也要记下第三个带的值(关于五/六带的电阻,见:rpf.io/5-6band)。
转到最后一个环——第三或第四环——在“乘数”栏中查看其颜色。这将告诉你,你需要用你目前的数字乘以什么,才能得到该电阻的实际值。这个例子有一个棕色的带子,这意味着”×101”。这可能看起来令人困惑,但这只是科学符号:“×101”只是意味着“在你的数字后面加一个零”。如果它是蓝色的,表示“×106”,那就意味着“在你的数字后面加上六个零”。
橙色带的33,加上棕色带的加零,就得到了330——这是电阻的值,以欧姆为单位测量。最后一条带子,在右边,是电阻的误差率。这只是它可能接近其额定值的程度。廉价的电阻可能有一条银色的带子,表示它可以比其额定值高或低10%,或者根本没有最后一条带子,表示它可以高或低20%;最昂贵的电阻有一条灰色的带子,表示它的误差将在0.05%以内。对于业余项目来说,精度并不那么重要:任何公差通常都能正常工作。
如果你的电阻值超过1000欧姆(1000Ω),它的额定值通常是千欧姆(kΩ);如果它超过一百万欧姆,那些是兆欧姆(MΩ)。一个2200Ω的电阻会被写成2.2kΩ;一个2200000Ω的电阻会被写成2.2MΩ。
你能算出来吗?
一个100Ω的电阻会有什么色带?一个2.2MΩ的电阻会有什么色带?如果你想找到最便宜的电阻,你会寻找什么颜色的公差带?
你的第一个硬件交互程序:Hello, LED!
就像在屏幕上打印“Hello world!”是学习编程语言的第一步,让LED发光是学习硬件交互的传统入门。在这个项目中,你需要一个LED和一个330欧姆(330Ω)的电阻,或者你能找到的最接近330Ω的电阻,再加上母线对母线(F2F)的跳线。
电阻是至关重要的 电阻器是这个电路中的一个重要组成部分:它通过限制 LED 的电流量来保护树莓派和LED。没有它,LED可能会拉动太多的电流,并烧毁自己或树莓派。当这样使用时,该电阻被称为限流电阻。你需要的电阻器的确切值取决于你使用的LED,但330Ω适用于大多数常见的LED。该值越高,LED越暗;该值越低,LED越亮。除非你知道LED有一个适当值的内置电阻,否则不要在没有限流电阻的情况下将LED连接到树莓派上。
首先检查你的LED是否工作。转动你的树莓派,使GPIO接头在右侧的两个垂直条中。用一根母线对母线的跳线将330Ω电阻的一端连接到第一个3.3V引脚(下图标有3V3),然后用另一根母线对母线的跳线将另一端连接到LED的长脚(正极或阳极)。用最后一根母线对母线的跳线,将LED的短脚(负极或阴极),连接到第一个接地引脚(下图标有GND)。
将你的LED连接到这些引脚上——不要忘了电阻!
只要你的树莓派打开了,LED就应该亮起来。如果它不亮,请仔细检查你的电路:确保你没有使用过高的电阻值,所有的线都连接正确,而且你肯定选择了正确的GPIO引脚来配合图示。还要检查LED的脚,因为LED只能以一种方式工作:长脚连接到电路的正极,短脚连接到负极。
一旦你的LED开始工作,就是对它进行编程的时候了。将跳线从3.3V引脚(上图标有3V3)上断开,并将其连接到GPIO 25引脚(下图标有GP25)。LED将关闭,但不要担心 - 这是正常的。
断开3V3的电线,将其连接到GPIO 25针脚上。
现在你已经准备好创建一个Scratch或Python程序来开启和关闭你的LED。
编码知识
本章的项目需要你能熟练使用 Scratch 3 和 Thonny Python 集成开发环境(IDE)。如果你还没有这样做,请翻开第4章《 用Scratch 3 编程》和第5章《用 Python 编程》,先完成这些项目。
在Scratch中控制LED
加载Scratch 3,点击“添加扩展”图标。向下滚动,找到 “Raspberry Pi GPIO”扩展(见下图),然后点击它。这将加载你需要的块,以便从 Scratch 3 控制树莓派的 GPIO 头。你会看到新的块出现在块调色板中;当你需要它们时,它们就可以在 Raspberry Pi GPIO 类别中找到。
在 Scratch 3 中添加 Raspberry Pi GPIO 扩展。
首先,将一个 块拖到代码区,然后在它下面放置一个 “设置 gpio 0 为高电平输出”块。你需要选择你所使用的引脚的编号:点击小箭头打开下拉选择,点击“25”,告诉 Scratch 你要控制 GPIO 25 引脚。
当“绿旗”被点击的时候
设置 gpio 25 输出为高电平
点击绿色标志来运行你的程序。你会看到你的 LED 灯亮起:你已经为你的第一个硬件交互项目编程了 点击红色的八角形来停止你的程序:注意到 LED 灯一直亮着吗?这是因为你的程序只告诉树莓派打开 LED ——这就是你设置 GPIO 25 “输出为高电平”部分的意思。要想再次关闭它,请点击该块末尾的向下箭头,并从列表中选择“low(低)”。
当“绿旗”被点击的时候
设置 gpio 25 输出为低电平
再次点击绿色标志,这一次你的程序将 LED 关闭。为了使事情更有趣,添加一个永远控制块和几个等待 1 秒的块来创建一个程序,每秒钟闪烁一次 LED。
点击绿旗,观察你的LED:它将开启一秒钟,关闭一秒钟,再开启一秒钟,一直重复这个模式,直到你点击红色八角形停止。看看当LED处于开启或关闭状态时,你点击八角形会发生什么。
挑战:你能改变它吗?
你将如何改变程序以使LED保持更长的时间?保持关闭的时间更长呢?你能使用的最小延迟是什么,同时还能看到LED的开关状态?
Python中的LED控制
从树莓菜单的编程部分加载 Thonny,然后点击新建按钮开始一个新的项目,保存为 Hello LED。要从 Python 中使用 GPIO 引脚,你需要一个叫作 GPIO Zero 的库。在这个项目中,你只需要库中用于处理LED的部分。通过在Python 外壳区键入以下内容,只导入库的这一部分。
from gpiozero import LED
接下来,你需要让 GPIO Zero 知道 LED 被连接到哪个 GPIO 引脚。输入以下内容:
led = LED(25)
这两行一起给了 Python 控制连接到树莓派的 GPIO 引脚的 LED 的能力,并告诉它连接哪一个引脚或哪几个引脚(如果你的电路中有多个LED要控制)。要实际控制LED,请输入以下内容。
led.on()
要再次关闭LED,请输入
led.off()
恭喜你,你现在可以在Python中控制你的树莓派的 GPIO 引脚了! 再试着输入这两条指令。如果 LED 已经关闭,led.off()
不会做任何事情;如果LED已经打开,你输入led.on()
也是如此。
要制作一个真正的程序,请在脚本区输入以下内容:
from gpiozero import LED
from time import sleep
led = LED(25)
while True:
led.on()
sleep(1)
led.off()
sleep(1)
这个程序从 gpiozero(GPIO Zero)库中导入了 LE 函数,从时间库中导入了 sleep 函数,然后构建了一个无限循环,将LED打开一秒,关闭一秒,然后重复。点击“Run”图标可以看到它在运行:你的LED将开始闪烁。与Scratch 程序一样,记录下当你在 LED 开启时与 关闭时,点击“Stop”按钮会发生什么。
挑战:延长亮灯时间
你将如何改变程序以使LED的亮灯时间更长?保持关闭的时间更长呢?在看到LED开启和关闭时,你能使用的最小延迟是什么?
使用面包板
如果你用面包板来放置元件和进行电子连接,本章的下一个项目将更容易完成。
面包板上布满了孔——为了配合元件,间隔为 2.54 毫米。这些孔下面是金属条,就像你到现在为止一直在使用的跳线。这些金属条在板子上排成一排,大多数板子的中间有一个空隙,把它们分成两半。许多面包板的顶部还有字母,两侧有数字。这些可以让你找到一个特定的孔。A1 是左上角,B1 是紧靠在其右边的孔,而 B2 则是在B1下面的一个孔。A1 通过隐藏的金属条与 B1 相连,但在这三个孔当中,除非你自己加一根跳线,否则没有一个孔与任何两个孔相连。
较大的面包板两侧也有条状的孔,通常用红黑或红蓝条纹标记。这些是电源轨,旨在使布线更容易:你可以从树莓派的地线连接到其中一个电源轨,通常标有蓝色或黑色条纹和负号,这为面包板上的许多元件提供一个公共地线。如果你的电路需要 3.3V 或 5V 的电源,你也可以这样做。在面包板上添加电子元件很简单:只需将其引线(粘在外面的金属部分)与孔对齐,然后轻轻推动,直到元件就位。对于你需要在面包板之外进行的连接,你可以使用公对公(M2M)跳线;对于从面包板到树莓派的连接,使用公对母(M2F)跳线。
千万不要试图在面包板上的一个孔里塞进一个以上的元件导线或跳线。记住:除了中间的分叉外,孔是成列连接的,所以 A1 的元件引线与你添加到 B1、C1、D1 和 E1 的任何东西都是电连接的。
下一步:读取一个按钮
像LED这样的输出是一回事,但 GPIO 的 “输入/输出 ”部分意味着你也可以将引脚用作输入。在这个项目中,你需要一块面包板、公对公(M2M)和公对母(M2F)跳线,以及一个按钮开关。如果你没有面包板,你可以使用母对母(F2F)的跳线,但为了不意外破坏电路,按钮将更难被按下。
首先,将按钮添加到你的面包板上。如果你的按钮只有两条腿,确保它们插在面包板的不同编号的“行”中;如果它有四条腿,把它转过来, 使四条腿伸出来的侧面沿着面包板的“行”,没有腿的平坦侧面在顶部和底部。 将面包板的地线与树莓派的地线(下图标有 GND)连接起来,然后将按钮的一条腿与地线连接起来,用一根公母跳线。最后,如果使用四条腿的开关,将另一条腿与你刚才连接的腿在同一侧的那条腿,用一根公母跳线连接到树莓派的 GPIO 2 针脚(下图标有 GP2)。
将一个按钮连接到GPIO引脚上。
在Scratch中读取一个按钮
启动一个新的Scratch程序,并将一个当点击的块拖到代码区。连接“设置 GPIO 0 输入为高电平”“ 设置 GPIO 0 为输入高电平”,并从下拉菜单中选择数字 2,以匹配你用于 GPIO 的引脚。
你用于按钮的GPIO引脚。
如果你现在点击绿旗,什么也不会发生。这是因为你已经告诉 Scratch 把这个引脚作为一个输入,但没有告诉他如何处理这个输入。把一个块拖到你的序列的末尾,然后在里面拖一个块。找到块,把它拖到块的菱形空间里,用下拉菜单选择数字 2,告诉它要检查哪个 GPIO 引脚。将一个块拖入该块的部分,并将其编辑为“按钮被按下了!”块,部分暂时留空。
这里有很多事情要做,但先从测试开始:点击绿旗,然后按下面包板上的按钮。你的精灵应该告诉你,按钮已经被按下了:你已经成功地从 GPIO 引脚读取了一个输入信号。
你可能已经注意到,如果 GPIO 2 为高电平,那么这个块的一部分是空的。当按钮真的被按下时,运行的代码是在该块的 else 部分。这似乎令人困惑,因为按下按钮肯定会使它变成高电平?事实上,情况恰恰相反。树莓派的GPIO引脚在被设置为输入时通常是高电平,或者说通电,而按下按钮会把它们拉到低电平。
再看看你的电路:看看按钮是如何连接到 GPIO 2 引脚的,它提供了电路的正极部分,以及接地引脚。当按钮被按下时,GPIO 引脚上的电压通过接地引脚被拉低,你的 Scratch 程序停止运行块中的代码,转而运行该块的 else 部分的代码。
如果这一切听起来令人费解,请记住:Raspberry Pi GPIO 引脚上的按钮是在该引脚变为低电平时被按下的,而不是在它变为高电平时被按下的。
为了进一步扩展你的程序,把 LED 和电阻加回到电路中:记得把电阻连接到 GPIO 25 引脚和 LED 的长脚,把 LED 的短脚连接到面包板的地线上。
把这个代码区的块拖到块调色板上删除,然后用一个设置 GPIO 25 为高电平输出的块代替它——记住你必须用下拉箭头改变 GPIO 的编号。添加一个块(注意要改变数值)到目前空的这一部分。
点击绿色旗帜并按下按钮。只要你按住按钮,LED就会亮起来;放手,它又会变暗。恭喜:你是根据另一个输入来控制一个 GPIO 引脚的。
挑战:让它一直亮着
你将如何改变程序,使LED在你松开按钮后还能保持几秒钟?要想让LED在你不按下按钮时亮起,在你按下时熄灭,你需要做哪些改变?
在Python中读取一个按钮
点击Thonny中的“New”图标开始一个新的项目,点击“Save”图标将其保存为 “按钮输入”。使用 GPIO 引脚作为按钮的输入,与使用引脚作为 LED 的输出非常相似,但你需要导入 GPIO Zero 库的不同部分。在脚本区键入以下内容。
from gpiozero import Button
button = Button(2)
为了让代码在按钮被按下时运行,GPIO Zero 提供了wait_for_press
函数。输入以下内容:
button.wait_for_press()
print("你推了我!")
点击 “Run”图标,然后按下按钮开关。你的信息将打印到 Thonny 窗口底部的 Python shell 中:你已经成功地从 GPIO 引脚中读取了一个输入! 如果你想再次尝试你的程序,你需要再次点击运行按钮;因为程序中没有循环,它在完成向 shell 打印信息后就会退出。
为了进一步扩展你的程序,请将 LED 和电阻重新添加到电路中:记得将电阻连接到 GPIO 25 引脚和 LED 的长腿上,将 LED 的短腿连接到面包板的地轨上。
为了控制一个 LED 以及读取一个按钮,你需要从 GPIO Zero 库中导入 Button 和 LED 函数。你还需要从时间库中导入睡眠函数。回到你程序的顶部,输入以下内容作为新的前两行。
from gpiozero import LED
from time import sleep
在button = Button(2)
这一行下面,输入。
led = LED(25)
删除 print("You pushed me!")
这一行,并将其替换为。
led.on()
sleep(3)
led.off()
你完成的程序应该看起来像这样。
from gpiozero import LED
from time import sleep
from gpiozero import Button
button = Button(2)
led = LED(25)
button.wait_for_press()
led.on()
sleep(3)
led.off()
点击 “Run”图标,然后按下按钮开关:LED将亮起三秒钟,然后再次关闭,程序退出。祝贺你:你可以在Python 中使用按钮输入来控制一个 LED!
挑战:添加一个循环
你将如何添加一个循环来使程序重复而不是在按下一个按钮后退出?你需要改变什么来使LED在你不按下按钮时亮起,在你按下时熄灭?
制造一些噪音:控制一个蜂鸣器
LED 是一个很好的输出设备,但如果你看向另一个方向就没有什么用处了。解决办法是:蜂鸣器,它可以在房间的任何地方发出声音。在这个项目中,你需要一块面包板,公对母(M2F)跳线,以及一个有源蜂鸣器。如果你没有面包板,你可以用母对母(F2F)的跳线来代替连接蜂鸣器。
在电路和编程方面,一个有源蜂鸣器可以完全像 LED 一样处理。重复你为 LED 制作的电路,但是用有源蜂鸣器代替 LED,并且不使用电阻,因为蜂鸣器需要更多的电流来工作。用你的面包板和公对母跳线将蜂鸣器的负极一条引脚连接到 GPIO 15 引脚(下图标有 GP15),另一条引脚(一般标有正号“+”)连接到接地引脚(下图中有GND)。
如果你的蜂鸣器有三条引脚,确保标有负号“-”的引脚连接到接地引脚,标有“S”或 “SIGNAL”的引脚连接到 GPIO 15,然后将剩下的引脚(通常在中间)连接到3.3V引脚(下图标有3V3)。
将一个蜂鸣器连接到 GPIO 引脚上
在Scratch中控制一个蜂鸣器
重新创建与制作 LED 闪光灯相同的程序,如果你在创建按钮项目之前保存了这个程序,则可以加载它。使用块中的下拉菜单,选择 15 号,这样 Scratch 就可以控制蜂鸣器。
点击绿色旗帜,你的蜂鸣器将开始嗡嗡作响:一秒钟开,一秒钟关。如果你只听到蜂鸣器一秒钟响一次,你使用的是无源蜂鸣器而不是有源蜂鸣器。有源蜂鸣器产生快速变化的信号,即所谓的振荡,使金属板自身振动,而无缘蜂鸣器需要振荡信号。当你用 Scratch 简单地打开它时,金属板只移动一次就停止了——发出“咔哒”的声音,直到你的程序下次打开或关闭这个金属板。
点击红色的八角形来停止你的蜂鸣器,但要确保在它不发声的时候这样做,否则蜂鸣器会继续嗡嗡作响,直到你再次运行你的程序为止。
挑战:改变嗡嗡声 你可以如何改变程序,使蜂鸣器鸣叫的时间更短?你能不能建立一个电路,让蜂鸣器由一个按钮控制?蜂鸣器由一个按钮来控制?
用Python控制一个蜂鸣器
通过 GPIO Zero 库控制一个有源蜂鸣器与控制一个 LED 几乎是一样的,因为它有开和关的状态。不过你需要一个不同的,功能:蜂鸣器。在 Thonny 中启动一个新项目,并将其保存为“蜂鸣器”,然后输入以下内容。
from gpiozero import Buzzer
from time import sleep
和 LED 一样,GPIO Zero 需要知道你的蜂鸣器连接到哪个引脚,以便控制它。输入以下内容。
buzzer = Buzzer(15)
从这里开始,你的程序与你写的控制 LED 的程序几乎相同;唯一的区别(除了不同的 GPIO 引脚号码)是你用buzzer 代替了 led。输入以下内容。
while True:
buzzer.on()
sleep(1)
buzzer.off()
sleep(1)
点击“Run”图标,你的蜂鸣器将开始嗡嗡作响:一秒钟开,一秒钟关。如果你使用的是无源蜂鸣器而不是有源蜂鸣器,你将只听到每秒短暂的咔哒声,而不是连续的嗡嗡声:这是因为无源蜂鸣器缺少一个振荡器来产生快速变化的信号,从而使蜂鸣器内部的板块振动。
点击“Stop”图标退出程序,但要确保蜂鸣器当时没有发出声音,否则它将继续嗡嗡作响,直到你再次运行你的程序为止。
挑战:更好的嗡嗡声
你怎样才能改变程序,使蜂鸣器响的时间更短?你能建立一个电路,使蜂鸣器由一个按钮控制吗?
Scratch项目:交通灯
现在你知道了如何使用按钮、蜂鸣器和LED作为输入和输出,你可以准备建立一个真实世界的计算实例:交通灯,用一个可以按动的按钮来过马路。在这个项目中,你需要一块面包板;一个红色、一个黄色和一个绿色的 LED;三个 330Ω 的电阻;一个蜂鸣器;一个按钮开关;以及一些公对公(M2M)和公对母(M2F)的跳线。
首先建立电路(如下图),将蜂鸣器连接到GPIO 15针脚(下图中标有GP15),红色LED连接到GPIO 25针脚(标为GP25),黄色LED连接到GPIO 8(GP8),绿色LED连接到GPIO 7(GP7),开关连接到GPIO 2(GP2)。记得在 GPIO 引脚和LED的长腿之间连接 330Ω 的电阻,并将所有元件的第二条腿连接到面包板的地线。最后,将地线连接到树莓派上的一个接地引脚(标有GND),以完成电路。
交通灯项目的接线图
启动一个新的 Scratch 3 项目,然后把一个块拖到代码区。接下来,你需要告诉 Scratch,在你的电路中连接到按钮开关的 GPIO 2 引脚是一个输入,而不是输出:在 块下面,从Raspberry Pi GPIO类别中拖出一个设置 GPIO 为输入拉高的块。点击“0”旁边的向下箭头,从下拉列表中选择数字 2。
接下来,你需要创建你的交通灯序列。把一个永远的块拖到你的程序中,然后用块来填充它,使交通灯的 LED 灯以一种模式打开和关闭。记住哪个 GPIO 引脚连接了哪个元件:当你使用 25 号引脚时,你使用的是红色 LED,8 号引脚是黄色 LED,7 号引脚是绿色 LED。
点击绿色旗帜,观察你的 LED:首先红色会亮,然后红色和黄色都会亮,然后是绿色,然后是黄色,最后顺序重复,红色再亮一次。
这个模式与英国的交通灯相匹配;如果你愿意,你可以编辑这个序列以匹配其他国家的模式。为了模拟行人过马路,你需要你的程序来观察按钮被按下的情况。如果你的程序正在运行,点击红色的八角形来停止它。拖动一个块到你的脚本区,并把它连接到你的块的正下方,把你的交通灯序列放在部分。暂时将菱形缺口留空。
真正的人行横道并不是一按下按钮就把灯换成红灯,而是要等待序列中的下一个红灯。要在你自己的程序中建立这一点,把“当 GPIO 0 是低电平时”块拖到代码区,并从其下拉列表中选择“2”。然后在它下面拖出一个块。
这个块堆栈检测按钮是否被按下,然后将变量“按下”设置为1。以这种方式设置变量让你存储按钮被按下的事实,尽管你不会马上对它采取行动。
回到你的原始块堆栈,找到块。将一个运算符块拖入块的菱形空白处,然后将一个被按下的记者块拖入第一个空白处。在该块右侧的“50”上输入“0”。
点击绿旗,观察交通灯的运行顺序。按下按钮开关:起初看起来什么都没有发生,但一旦序列达到终点,只有黄色LED灯亮起,交通灯就会熄灭并保持关闭,这要感谢你的 “按下”变量。
剩下要做的就是让你的行人过马路按钮除了关灯之外还能做些什么。在主堆栈块中,找到块,并将一个设置 GPIO 25 的高电平输出块拖入其中(记得改变默认的 GPIO 引脚编号),以匹配你的红色 LED 所连接的引脚。
在这下面,仍然是块,为蜂鸣器创建一个模式:拖动一个块,然后用填充,改变 GPIO 引脚值以匹配蜂鸣器元件的引脚。最后,在你的块的下面,但仍然在块中,添加一个,最后一个块重置了存储按钮按下的变量,这样蜂鸣器序列就不会永远重复。
点击绿旗,然后按下你面包板上的开关。序列完成后,你会看到红灯亮起,蜂鸣器响起,让行人知道可以安全通过。几秒钟后,蜂鸣器将停止,交通灯序列将再次启动,并持续到你下次按下按钮。
祝贺你:你已经为自己的一套功能齐全的交通灯进行了编程,并配有行人过马路的功能!
挑战:你能改进它吗?
你能改变程序,让行人有更长的时间过马路吗?你能找到其他国家的红绿灯模式的信息,并重新编程使之与之匹配吗?你怎么能让LED灯的亮度降低?
Python项目:快速反应游戏
现在你知道了如何使用按钮和 LED 作为输入和输出,你已经准备好建立一个真实世界的计算实例:一个双人快速反应游戏,目的是看谁的反应速度最快 在这个项目中,你需要一块面包板、一个 LED 和一个 330Ω 的电阻、两个按钮开关、一些公对母(M2F)跳线和一些公对公(M2M)跳线。
首先构建电路(如下图):将面包板左侧的第一个开关连接到 GPIO 14 针脚(下图标有 GP14),将面包板右侧的第二个开关连接到 GPIO 15 针脚(标为 GP15),将 LED 的长脚连接到 330Ω 电阻,然后连接到树莓派的 GPIO 4 针脚(标为 GP4),将所有部件的第二条腿连接到面包板的地轨。最后,将地轨连接到树莓派的接地引脚(标有 GND)。
快速反应游戏的接线图
在 Thonny 中开始一个新项目,并将其保存为“快速反应游戏”。你将会使用 GPIO Zero 库中的 LED 和按钮功能,以及时间库中的睡眠功能。不过,你可以节省时间,用逗号“,”将它们分开,而不是将两个 GPIO Zero 函数分别导入两行。在脚本区输入以下内容。
from gpiozero import LED, Button
from time import sleep
和以前一样,你需要告诉 GPIO Zero 这两个按钮和 LED 连接到哪个引脚。输入以下内容。
led = LED(4)
right_button = Button(15)
left_button = Button(14)
现在添加指令来打开和关闭 LED,这样你就可以检查它是否正常工作。
led.on()
sleep(5)
led.off()
点击运行按钮:LED 将开启五秒钟,然后关闭,程序将退出。不过,对于反应游戏的目的来说,每次让 LED 在 5 秒后准时熄灭是有点可预测的。在from time import slee
p这行下面添加以下内容 :
from random import uniform
随机库(random),顾名思义,让你生成随机数(这里用的是“uniform”均匀分布——详见rpf.io/uniform)。找到 sleep(5)
这一行,把它改成读取 uniform。
sleep(uniform(5, 10))
再次点击“Run”图标:这次 LED 会在 5 到 10 之间的随机秒数里保持亮光。数数看 LED 灯要多久才会熄灭,然后再点击“Run”图标几次:你会发现每次运行的时间都不一样,使程序的可预测性降低。
为了把按钮变成每个玩家的触发器,你需要添加一个函数。在你的程序的最下面,输入以下内容。
def pressed(button):
print(str(button.pin.number) + " won the game")
记住,Python 使用缩进来知道哪些行是你的函数的一部分;Thonny 会自动为你缩进第二行。最后,添加以下两行来检测玩家是否按下按钮——记住它们不能缩进,否则 Python 会把它们当作你函数的一部分。
right_button.when_pressed = pressed
left_button.when_pressed = pressed
运行你的程序,这次尝试在 LED 灯熄灭后立即按下其中一个按钮。你会看到第一个按钮被按下的信息被打印到 Thonny 窗口底部的 Python shell 中。不幸的是,你也会看到每次按下任何一个按钮的信息——它们使用针脚号码而不是按钮的名称。要解决这个问题,首先要向球员询问他们的名字。在从随机导入 uniform 的那一行下面,输入以下内容。
left_name = input("左方选手的名字是")
right_name = input("右方选手的名字是")
回到你的函数中,将 print(str(button.pin.number) + " won the game")
一行替换为。
if button.pin.number == 14:
print(left_name + "赢了!")
else:
print(right_name + " 赢了!")
点击“Run”图标,然后在 shell 区 输入两个玩家的名字。当你这次按下按钮时,记住要在 LED 熄灭后尽快完成,你会看到打印的是玩家的名字而不是针脚号。
为了解决所有按钮被报告为获胜的问题,你需要从 sys(系统的简称)库中添加一个新函数:exit。在最后一个导入行下,输入以下内容。
from os import _exit
然后在函数的结尾处,在 print(right_name + " 赢了!")
一行下,输入以下内容。
_exit(0)
这里的缩进很重要。_exit(0)
只缩进四个空格,与“if、else”对齐。这条指令告诉 Python 在第一个按钮被按下后就停止程序,这意味着玩家第二个按下按钮的玩家因为输了,所以得不到任何奖励。
你完成的程序应该看起来像这样。
from gpiozero import LED, Button
from time import sleep
from random import uniform
from os import _exit
left_name = input("左方选手的名字是")
right_name = input("右方选手的名字是")
led = LED(4)
right_button = Button(15)
left_button = Button(14)
led.on()
sleep(uniform(5, 10))
led.off()
def pressed(button):
if button.pin.number == 14:
print(left_name + "赢了!")
else:
print(right_name + " 赢了!")
_exit(0)
right_button.when_pressed = pressed
left_button.when_pressed = pressed
点击运行按钮,输入玩家的名字,等待 LED 灯熄灭,你会看到获胜玩家的名字。你还会看到来自 Python 本身的一条信息。后台被终止或断开连接。使用Stop/Restart
来重新启动。这只是意味着 Python 收到了你的 _exit(0)
命令并停止了程序,但你需要点击 “Stop”图标来完全退出,并为你的程序准备好另一个回合(见下图)。
一旦决定了胜负,你就需要停止该程序。
恭喜你:你已经制作了自己的物理游戏!
挑战:改进游戏
你能添加一个循环,使游戏连续运行吗?记住要先删除 _exit(0) 指令 你能不能增加一个分数计数器,这样你就可以看到谁在多轮比赛中获胜?加一个计时器呢?这样你就可以看到你花了多长时间对灯的熄灭作出反应?