差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
mp_traffic_light [2022/02/24 16:06] gongyusu |
mp_traffic_light [2022/02/24 21:26] (当前版本) gongyusu |
||
---|---|---|---|
行 1: | 行 1: | ||
## 交通灯的控制 | ## 交通灯的控制 | ||
- | 微控制器在日常所有的电子产品中用到,包括交通灯。交通灯控制器是一种特殊的系统,它可以定时改变信号灯,观察行人是否要过马路,以及根据交通流量调整信号灯的时间,与附近的交通灯系统通话,以确保整个交通网络保持顺畅运行。虽然构建一个大规模的交通管理系统是一个相当先进的项目,但构建一个由树莓派[[rpi_pico|Pico]]驱动的微型模拟器就比较简单。在这个项目中,你将看到如何控制多个[[LED]],设置不同的时间,以及当程序的其余部分使用一种称为线程的技术继续运行时如何监测一个按键的输入。 | + | 微控制器在日常所有的电子产品中用到,包括交通灯。交通灯控制器是一种特殊的系统,它可以定时改变信号灯,观察行人是否要过马路,以及根据交通流量调整信号灯的时间,与附近的交通灯系统通话,以确保整个交通网络保持顺畅运行。虽然构建一个大规模的交通管理系统是一个相当先进的项目,但构建一个由树莓派[[rpi_pico|Pico]]驱动的微型模拟器就比较简单。在这个项目中,你将看到如何控制多个[[LED]],设置不同的时间,以及当程序的其余部分使用一种称为“线程”的技术继续运行时如何监测一个按键的输入。 |
+ | {{ :traffic_light.jpg |}}<WRAP centeralign> 交通灯的控制 </WRAP> | ||
- | ### 1. 一个简单的交通灯 | + | ### 1. 实现一个简单的交通灯 |
在我们设计的树莓派[[rpi_pico|Pico]]的学习板上,有4个LED,分别为R(红色)、G(绿色)、B(蓝色)、Y(黄色),我们可以使用其中的三个来仿真现实中的交通灯的工作状态: | 在我们设计的树莓派[[rpi_pico|Pico]]的学习板上,有4个LED,分别为R(红色)、G(绿色)、B(蓝色)、Y(黄色),我们可以使用其中的三个来仿真现实中的交通灯的工作状态: | ||
* 红色LED亮表示禁止通行 | * 红色LED亮表示禁止通行 | ||
行 8: | 行 9: | ||
* 绿色LED亮表示可以再次通行 | * 绿色LED亮表示可以再次通行 | ||
- | 要给你的交通灯编程,把你的Pico连接到你的树莓派或其它电脑,并加载[[thonny_ide|Thonny]]。创建一个新程序,并从导入machine库开始,这样你就可以控制你的Pico的GPIO引脚: | + | 要给我们的交通灯编程,先把我们的Pico连接到树莓派或其它电脑,并加载[[thonny_ide|Thonny]]。创建一个新程序,并从导入machine库开始,这样我们就可以控制[[rpi_pico|Pico]]的[[GPIO]]引脚: |
<code python> | <code python> | ||
行 54: | 行 55: | ||
单击Run图标并将程序以TrafficLights.py的形式保存到[[rpi_pico|Pico]]中。 | 单击Run图标并将程序以TrafficLights.py的形式保存到[[rpi_pico|Pico]]中。 | ||
- | \\ | ||
看[[LED]]的状态变化: | 看[[LED]]的状态变化: | ||
行 76: | 行 76: | ||
</code> | </code> | ||
- | 此设置引脚GP12上的按钮用作行人的按键输入,引脚GP22上的蜂鸣器作为声音输出。因为树莓派Pico有内置的可编程输入电阻,我们将其设置为上拉模式。这意味着引脚的电压被拉到3.3V(它的逻辑电平是1),除非它连接到GND(在这种情况下,它的逻辑电平将是0)。 | + | 此设置引脚GP12上的按钮用作行人的按键输入,引脚GP22上的[[buzzer|蜂鸣器]]作为声音输出。因为树莓派[[rpi_pico|Pico]]有内置的可编程输入电阻,我们将其设置为上拉模式。这意味着引脚的电压被拉到3.3V(它的逻辑电平是1),除非它连接到GND(在这种情况下,它的逻辑电平将是0)。 |
接下来,您需要一种方法让您的程序不断监视按键的值。以前,你所有的程序都是通过一系列的指令一步一步地工作的,一次只做一件事。红绿灯程序也没有什么不同:当它运行时,[[MicroPython]]会一步一步地完成指令,打开和关闭led。 | 接下来,您需要一种方法让您的程序不断监视按键的值。以前,你所有的程序都是通过一系列的指令一步一步地工作的,一次只做一件事。红绿灯程序也没有什么不同:当它运行时,[[MicroPython]]会一步一步地完成指令,打开和关闭led。 | ||
行 110: | 行 110: | ||
</code> | </code> | ||
- | 你添加的第一行定义了你的线程并给它起了一个描述性的名字, 它是一个读取按钮输入的线程。与编写循环时一样,MicroPython需要线程中包含的所有内容缩进四个空格, 这样它就知道线程的开始和结束位置。 | + | 你添加的第一行定义了你的线程并给它起了一个描述性的名字, 它是一个读取按钮输入的线程。与编写循环时一样,MicroPython需要线程中包含的所有内容缩进4个空格, 这样它就知道线程的开始和结束位置。 |
- | 下一行让[[MicroPython]]知道您将更改全局button_pressed变量的值。如果你只是想检查值,你不需要这一行, 但没有它,你不能对变量做任何改变。 | + | 下一行让[[MicroPython]]知道我们将更改全局button_pressed变量的值。如果我们只是想检查值,就不需要这一行, 但没有它,我们是不能对变量做任何改变的。 |
- | 接下来,您已经设置了一个新的循环, 这意味着需要遵循一个新的四格缩进, | + | 接下来,我们设置一个新的循环, 同时也遵循一个新的4格缩进,总共8格,所以[[MicroPython]]知道循环是线程的一部分,下面的代码也是循环的一部分。这个多级缩进的嵌套代码在[[MicroPython]]中是很常见的, [[thonny_ide|Thonny]]也会尽最大的努力在每次需要的时候来帮助你自动添加一个新的层级, 但我们要记得完成一个特定的层级以后删除多余的空格。 |
- | 总共8个,所以MicroPython知道循环是线程的一部分,下面的代码也是循环的一部分。这个多级锁进的嵌套代码在MicroPython是很常见的, Thonny也会尽最大的努力在每次需要的时候来帮助你自动添加一个新的等级,但你要记得完成一个特定的等级以后删除多余的空格。 | + | 下一行是一个条件语句,用于检查按键的值是否为1, 我们的[[rpi_pico|Pico]]使用一个内部的上拉电阻,当按键没有被按下时,读取的值是1,这意味着在此条件下代码永远不会运行, 只有当按键被按下时,读取的值才为0,线程的最后一行才会运行,这一行将button_pressed变量设置为True,让程序的其余部分知道按键已经被按下。最后,我们添加了一个非常短(0.01秒)的延迟,以防止while循环运行过快。 |
- | 下一行是一个条件语句,用于检查按键的值是否为1。因为你的Pico使用一个内部的上拉电阻,当按键没有被按下时,读取的值是1,这意味着在此条件下代码永远不会运行。只有当按键被按下时,读取的值为0,线程的最后一行才会运行,这一行将button_pressed变量设置为True,让程序的其余部分知道按键已经被按下。最后,我们添加了一个非常短(0.01秒)的延迟,以防止while循环运行过快。 | + | 我们注意到,当按键被按下后再释放时,线程中没有任何东西可以将button_pressed变量重置为False。这是有原因的,虽然我们可以在红绿灯周期的任何时候按路口的按键,但它只在红灯亮起、我们可以安全过马路时才生效。新线程需要做的就是在按键被按下时更改变量,当行人安全地过马路时,主线程会将其重置为False。 |
- | 你可能会注意到,当按键被按下后被释放时,线程中没有任何东西可以将button_pressed变量重置为False。这是有原因的,虽然你可以在红绿灯周期的任何时候按路口的按键,但它只在红灯亮起、你可以安全过马路时才生效。新线程需要做的就是在按键被按下时更改变量;当行人安全地过马路时,主线程会将其重置为False。 | + | 定义一个线程并不会设置它运行,在我们的程序中可以在任何时候启动一个线程,我们需要明确地告诉_thread库想要启动线程的时间。与运行正常的代码行不同,运行线程不会停止程序的其余部分,当线程启动时,[[MicroPython]]将继续运行程序的下一行,即使它运行新线程的第一行。 |
- | 定义一个线程并不会设置它运行,在你的程序中可以在任何时候启动一个线程,你需要明确地告诉_thread库你想要启动线程的时间。与运行正常的代码行不同,运行线程不会停止程序的其余部分,当线程启动时,MicroPython将继续运行程序的下一行,即使它运行新线程的第一行。 | + | 在我们的线程下面创建一个新行,删除所有[[Thonny_ide|Thonny]]自动为我们添加的缩进,如下所示: |
- | + | ||
- | 在你的线程下面创建一个新行,删除所有Thonny自动为你添加的缩进,如下所示: | + | |
<code python> | <code python> | ||
行 132: | 行 130: | ||
这告诉_thread库启动前面定义的线程。在这一点上,线程将开始运行并快速进入它的循环, 每秒检查按键数千次,看看它是否被按下。与此同时,主线程将继续执行程序的主要部分。 | 这告诉_thread库启动前面定义的线程。在这一点上,线程将开始运行并快速进入它的循环, 每秒检查按键数千次,看看它是否被按下。与此同时,主线程将继续执行程序的主要部分。 | ||
- | 现在单击Run按钮。你会看到交通灯和以前一模一样,没有任何延误或停顿。但是,如果您按下按钮,什么也不会发生,因为您还没有添加对按钮作出实际反应的代码。 | + | 现在单击Run按钮,我们会看到交通灯和以前一模一样,没有任何延误或停顿,这时候如果我们按下按钮,什么也不会发生,因为我们还没有添加对按钮作出实际反应的代码。 |
- | 到主循环的开始部分,就在一行的正下方(while True:),并添加以下代码,记住要注意嵌套缩进,并在不再需要时删除Thonny添加的缩进: | + | 到主循环的开始部分,就在(while True:)这一行的后面添加以下代码,记住要注意嵌套缩进,并在不再需要时删除[[Thonny_ide|Thonny]]添加的缩进: |
<code python> | <code python> | ||
if button_pressed == True: | if button_pressed == True: | ||
行 147: | 行 145: | ||
</code> | </code> | ||
- | 这段代码检查button_pressed全局变量,以查看自循环最后一次运行以来,按键是否在任何时候被按下。如果有,就像你之前做的按键阅读线程报告的那样,它开始运行一段代码,首先打开红色的LED灯来停止交通,然后按下蜂鸣器十次,让行人知道时间到了可以过马路了。 | + | 这段代码检查button_pressed全局变量,以查看自循环最后一次运行以来,按键是否在任何时候被按下。如果有,就像我们之前做的按键阅读线程报告的那样,它开始运行一段代码,首先打开红色的LED灯来停止交通,然后按下蜂鸣器十次,让行人知道时间到了可以过马路了。 |
最后两行将button_pressed变量重置为False, 所以下一次环路运行它不会触发行人通行规则,除非再次按下按钮。 | 最后两行将button_pressed变量重置为False, 所以下一次环路运行它不会触发行人通行规则,除非再次按下按钮。 | ||
- | 你会发现你不需要global button_pressed行来检查条件变量的状态, 只有当你想要更改变量并使该更改影响程序的其它部分时,才需要使用它。 | + | 我们会发现不需要global button_pressed行来检查条件变量的状态, 只有当我们想要更改变量并使该更改影响程序的其它部分时,才需要使用它。 |
- | 你的程序应该是这样的: | + | 最终我们的程序应该是这样的: |
<code python> | <code python> | ||
import machine | import machine | ||
行 173: | 行 171: | ||
button_pressed = True | button_pressed = True | ||
utime.sleep(0.01) | utime.sleep(0.01) | ||
+ | |||
_thread.start_new_thread(button_reader_thread, ()) | _thread.start_new_thread(button_reader_thread, ()) | ||
行 200: | 行 199: | ||
单击Run图标。 | 单击Run图标。 | ||
- | 首先,程序将正常运行: 交通灯将以通常的模式开或关。 | ||
- | 按下按钮开关,如果程序目前处于循环的中间,什么也不会发生,直到它到达终点并再次循环, 这时红灯会变红,蜂鸣器会发出哔哔声,让你知道可以安全通过马路了。 | ||
- | 过马路的条件部分代码运行在前面编写的代码的灯打开和关闭在循环模式: | ||
- | 完成之后, 模式将开始像往常一样的红色LED点亮待进一步点燃了五秒的时间时,蜂鸣器(after it’s finished, the pattern will begin as usual with the red LED staying lit for a further five seconds on top of the time it was lit while the buzzer was going)。 | + | 一开始的时候,程序按照正常模式运行 - 交通灯将以通常的模式开或关。 |
+ | |||
+ | 按下按钮开关,如果程序目前处于循环的中间,什么也不会发生,直到它到达终点并再次循环, 这时红灯会变红,[[buzzer|蜂鸣器]]会发出哔哔声,让你知道可以安全通过马路了。 | ||
+ | |||
+ | 检测过马路的条件部分的代码,是在我们刚才编写的在循环模式中打开/关闭灯的代码之前运行,它完成之后, 模式将恢复到像往常一样 - 红色LED在原来点亮的基础上再亮五秒的时间,蜂鸣器也一直在响。 | ||
- | 这是在模拟真正的过马路的方式,即使蜂鸣器停止鸣叫,红灯仍然亮着,所以在蜂鸣器响着的时候开始过马路的人有时间在车辆允许通行之前到达另一边。 | + | 这是在模拟真正的过马路的方式, 即使蜂鸣器停止鸣叫,红灯仍然亮着,所以在蜂鸣器响着的时候开始过马路的人有时间在车辆允许通行之前到达另一边。 |
让交通灯再循环几次,然后再次按下按键触发另一个路口。 | 让交通灯再循环几次,然后再次按下按键触发另一个路口。 | ||
- | 祝贺你:你已经建造了你自己的十字路口交通控制系统! | + | 祝贺你:你已经实现了你自己的十字路口交通控制系统! |
<WRAP center round tip 60%> | <WRAP center round tip 60%> | ||
**挑战:你能改进它吗? ** | **挑战:你能改进它吗? ** | ||
- | 你能改变程序,延长行人过马路的时间吗? 你能找到其他国家交通灯模式的信息,并重新编程你的交通灯以匹配吗?你能不能再加一个按钮,让对面的行人也能发出想要过马路的信号? | + | 你能改变程序,延长行人过马路的时间吗? 你能找到其他国家交通灯模式的信息,并重新编程我们的交通灯以匹配吗? 你能不能再加一个按钮,让对面的行人也能发出想要过马路的信号? |
</WRAP> | </WRAP> | ||