差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
mp_traffic_light [2021/10/04 18:16]
gongyusu [1. 一个简单的交通灯]
mp_traffic_light [2022/02/24 21:26] (当前版本)
gongyusu
行 1: 行 1:
 ## 交通灯的控制 ## 交通灯的控制
-微控制器在日常所有的电子产品中用到,包括交通灯。交通灯控制器是一种特殊的系统,它可以定时改变信号灯,观察行人是否要过马路,以及根据交通流量调整信号灯的时间,与附近的交通灯系统通话,以确保整个交通网络保持顺畅运行。+微控制器在日常所有的电子产品中用到,包括交通灯。交通灯控制器是一种特殊的系统,它可以定时改变信号灯,观察行人是否要过马路,以及根据交通流量调整信号灯的时间,与附近的交通灯系统通话,以确保整个交通网络保持顺畅运行。虽然构建一个大规模的交通管理系统是一个相当先进的项目,但构建一个由树莓派[[rpi_pico|Pico]]驱动的微型模拟器就比较简单。在这个项目中,你将看到如何控制多个[[LED]],设置不同的时间,以及当程序的其余部分使用一种称为“线程”的技术继续运行时如何监测一个按键的输入。 
 +{{ :​traffic_light.jpg |}}<WRAP centeralign>​ 交通灯的控制 </​WRAP>​
  
-虽然构建一个大规模的交通管理系统是一个相当先进的项目,但构建一个由树莓派Pico驱动的微型模拟器就比较简单。在这个项目中,你将看到如何控制多个LED,设置不同的时间,以及当程序的其余部分使用一种称为线程的技术继续运行时如何监测一个按键的输入。 +### 1. 实现一个简单的交通灯 
- +在我们设计的树莓派[[rpi_pico|Pico]]的学习板上,有4个LED,分别为R(红色)、G(绿色)、B(蓝色)、Y(黄色),我们可以使用其中的三个来仿真现实中的交通灯的工作状态:
-### 1. 一个简单的交通灯 +
-在我们设计的树莓派Pico的学习板上,有4个LED,分别为R(红色)、G(绿色)、B(蓝色)、Y(黄色),我们可以使用其中的三个来仿真现实中的交通灯的工作状态:+
   * 红色LED亮表示禁止通行   * 红色LED亮表示禁止通行
   * 黄色LED亮表示交通状态就要改变   * 黄色LED亮表示交通状态就要改变
   * 绿色LED亮表示可以再次通行   * 绿色LED亮表示可以再次通行
  
-要给的交通灯编程,把的Pico连接到你的树莓派或其它电脑,并加载Thonny。创建一个新程序,并从导入machine库开始,这样就可以控制你的Pico的GPIO引脚:​+要给我们的交通灯编程,我们的Pico连接到树莓派或其它电脑,并加载[[thonny_ide|Thonny]]。创建一个新程序,并从导入machine库开始,这样我们就可以控制[[rpi_pico|Pico]][[GPIO]]引脚:
  
 <code python> <code python>
行 38: 行 37:
 </​code>​ </​code>​
  
-下面的每一行都需要缩进四个空格,这样MicroPython就知道它们是循环的一部分,​ 当你按下回车键时,Thonny会自动为你缩进。+下面的每一行都需要缩进四个空格,这样[[MicroPython]]就知道它们是循环的一部分,​ 当你按下回车键时,Thonny会自动为你缩进。
 <code python> <code python>
     led_red.value(1) ​     led_red.value(1) ​
行 54: 行 53:
 </​code>​ </​code>​
  
-单击Run图标并将程序以Traffic_Lights.py的形式保存到Pico中。+单击Run图标并将程序以TrafficLights.py的形式保存到[[rpi_pico|Pico]]中。
  
-看LED的状态变化:​+ 
 +[[LED]]的状态变化:​
   - 首先红色的LED会亮起来 - 禁止通行;​   - 首先红色的LED会亮起来 - 禁止通行;​
   - 接下来,黄色的LED灯会亮起来,意味着交通灯即将改变;​   - 接下来,黄色的LED灯会亮起来,意味着交通灯即将改变;​
行 63: 行 63:
   - 最后,黄色的LED熄灭,循环重新开始,红色的LED亮起。   - 最后,黄色的LED熄灭,循环重新开始,红色的LED亮起。
  
-模式将循环直到你按下停止按钮,因为它形成了一个无限循环。它基于英国和爱尔兰现实世界交通控制系统中使用的交通灯模式,加速 - 只给汽车5秒钟通过交通灯,不会让交通非常自由!+模式将一直无限循环下去,直到你按下停止按钮。它基于英国和爱尔兰现实世界交通控制系统中使用的交通灯模式,在这个模拟试验中,我们只是将状态转换做了加速 - 最长的通行时间5秒钟
  
-### 2. 加入行人的控制 +### 2. 加入行人对灯的控制 
-然而,真正的红绿灯并不仅仅是用于道路车辆的,它们在那里保护行人,让他们有机会安全地过繁忙的道路。在英国这些最常见的类型被称为行人操作用户友好智能十字路口+然而,真正的红绿灯并不仅仅是用于道路车辆的,也要考虑到行人的通行,让他们有机会安全地过繁忙的道路,交通控制系统可以根据行人的按键来决定交通灯状态变化
  
-把你的交通灯变成一个海雀十字路口,你需要两样东西:​一个按开关,这样行人可以要求交通灯让他们过马路;还有蜂鸣器,这样行人就知道什么时候该过马路了。+那我们需交通灯控制系统中增加一个行人可以控制的开关,这样行人可以要求交通灯让他们过马路;再增加一个蜂鸣器,告知行人什么时候该过马路了。
  
-我们可以将学习板上的其中一个按键KEY1用作行人可以操作的按钮,学习板上的蜂鸣器给出提示音, 在Tonny中,回到设置led的行,并在下面添加以下两行:​+我们可以将学习板上的其中一个按键KEY1用作行人可以操作的按钮,学习板上的蜂鸣器给出提示音,在[[Thonny_ide|Thonny]] IDE中,回到设置led的行,并在下面添加以下两行:​
 <code python> <code python>
-button = machine.Pin(12,​ machine.Pin.IN,​ machine.Pin.PULL_DOWN+button = machine.Pin(12,​ machine.Pin.IN,​ machine.Pin.PULL_UP
 buzzer = machine.Pin(22,​ machine.Pin.OUT) buzzer = machine.Pin(22,​ machine.Pin.OUT)
 </​code>​ </​code>​
  
-设置引脚GP12上的按钮作输入,和在引脚GP22上的蜂鸣器作为输出。记住,你的Raspberry Pi Pico有内置的可编程输入电阻,我们将其设置为拉模式。这意味着引脚的电压被拉到0V(它的逻辑电平是0),除非它连接到3.3V电源(在这种情况下,它的逻辑电平将是1直到断开)。+设置引脚GP12上的按钮行人的按键输入,引脚GP22上的[[buzzer|蜂鸣器]]作为声音输出。因为树莓派[[rpi_pico|Pico]]有内置的可编程输入电阻,我们将其设置为拉模式。这意味着引脚的电压被拉到3.3V(它的逻辑电平是1),除非它连接到GND(在这种情况下,它的逻辑电平将是0)。
  
-接下来,您需要一种方法让您的程序不断监视按键的值。以前,你所有的程序都是通过一系列的指令一步一步地工作的,一次只做一件事。红绿灯程序也没有什么不同:​当它运行时,MicroPython会一步一步地完成指令,打开和关闭led。+接下来,您需要一种方法让您的程序不断监视按键的值。以前,你所有的程序都是通过一系列的指令一步一步地工作的,一次只做一件事。红绿灯程序也没有什么不同:​当它运行时,[[MicroPython]]会一步一步地完成指令,打开和关闭led。
  
-对于一套基本的交通灯来说,这就足够了;然而,对于海雀穿越的程序需要能够记录按钮是否按下时不会干扰交通灯。要实现此功能,需要一个新的库:​_thread。回到程序中导入machine和utime库的部分,并导入_thread库:​+对于一套基本的交通灯来说,这就足够了;然而,对于我们目前设定的交通灯控制模式我们的程序需要能够记录按钮是否按下时不会干扰交通灯。要实现此功能,我们需要一个新的库:​ _thread。回到程序中导入machine和utime库的部分,并导入_thread库:​
  
 <code python> <code python>
行 88: 行 88:
 一个或多个执行线程实际上是一个小的、部分独立的程序。您可以将前面编写的控制灯光的循环视为程序的主线程,并且使用_thread库可以创建一个同时运行的附加线程。 一个或多个执行线程实际上是一个小的、部分独立的程序。您可以将前面编写的控制灯光的循环视为程序的主线程,并且使用_thread库可以创建一个同时运行的附加线程。
  
-想象这些线程的一个简单方法是把它们想象成厨房里一个独立的工人:当厨师在准备主菜时,另一个人在做酱料。目前,的程序只有一个线程 - 控制交通灯的线程。然而,驱动Pico的RP2040微控制器有两个处理核心, 这意味着,就像厨房里的厨师和副厨师一样,可以同时运行两个线程来完成更多的工作。+想象这些线程的一个简单方法是把它们想象成厨房里一个独立的工人:当厨师在准备主菜时,另一个人在做酱料。目前,我们的程序只有一个线程 - 控制交通灯的线程。然而,驱动Pico的[[RP2040]]微控制器有两个处理核心, 这意味着,就像厨房里的厨师和副厨师一样,我们可以同时运行两个线程来完成更多的工作。
  
-在创建另一个线程之前,需要一种方法让新线程将信息传递回主线程, ​可以使用全局变量来实现这一点。在此之前使用的变量称为局部变量,只在程序的一个部分中有效;全局变量在任何地方都可以工作,这意味着一个线程可以更改值,另一个线程可以检查它是否被更改。+在创建另一个线程之前,我们需要一种方法让新线程将信息传递回主线程,我们可以使用全局变量来实现这一点。在此之前使用的变量称为局部变量,只在程序的一个部分中有效;全局变量在任何地方都可以工作,这意味着一个线程可以更改值,另一个线程可以检查它是否被更改。
  
-首先,需要创建一个全局变量。在你的buzzer = 行下面,添加以下内容:+首先,我们需要创建一个全局变量。在buzzer = 行下面,添加以下内容:
  
 <code python> <code python>
行 105: 行 105:
      ​global button_pressed ​      ​global button_pressed ​
      while True:      while True:
-        if button.value() == 1+        if button.value() == 0
             button_pressed = True             button_pressed = True
         utime.sleep(0.01)         utime.sleep(0.01)
 </​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使用一个内部的下拉电阻当按键没有被按下时,读取的值是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:),并添加以下代码——记住要注意嵌套缩进,并在不再需要时删除tonny添加的缩进:​+到主循环的开始部分,就在(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 ​
 import utime  import utime 
 import _thread import _thread
-led_red = machine.Pin(15, machine.Pin.OUT)  +led_red = machine.Pin(19, machine.Pin.OUT)  
-led_amber ​= machine.Pin(14, machine.Pin.OUT) +led_yellow ​= machine.Pin(16, machine.Pin.OUT) 
-led_green = machine.Pin(13, machine.Pin.OUT) +led_green = machine.Pin(18, machine.Pin.OUT) 
-button = machine.Pin(16, machine.Pin.IN,​ machine.Pin.PULL_DOWN)  +button = machine.Pin(12, machine.Pin.IN,​ machine.Pin.PULL_UP)  
-buzzer = machine.Pin(12, machine.Pin.OUT)+buzzer = machine.Pin(22, machine.Pin.OUT)
  
 global button_pressed ​ global button_pressed ​
行 170: 行 168:
     global button_pressed ​     global button_pressed ​
     while True:     while True:
-        if button.value() == 1+        if button.value() == 0
             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,​ ())
  
 while True: while True:
     if button_pressed == True:     if button_pressed == True:
-        led_red.value(1+        led_red.value(0
         for i in range(10): ​         for i in range(10): ​
             buzzer.value(1)             buzzer.value(1)
行 185: 行 184:
         global button_pressed         global button_pressed
         button_pressed = False          button_pressed = False 
-    led_red.value(1) ​ 
-    utime.sleep(5) ​ 
-    led_amber.value(1) ​ 
-    utime.sleep(2) ​ 
     led_red.value(0) ​     led_red.value(0) ​
-    led_amber.value(0) ​ 
-    led_green.value(1) ​ 
     utime.sleep(5) ​     utime.sleep(5) ​
 +    led_yellow.value(0) ​
 +    utime.sleep(2) ​
 +    led_red.value(1) ​
 +    led_yellow.value(1) ​
     led_green.value(0) ​     led_green.value(0) ​
-    led_amber.value(1) ​ 
     utime.sleep(5) ​     utime.sleep(5) ​
-    ​led_amber.value(0)+    ​led_green.value(1)  
 +    led_yellow.value(0)  
 +    utime.sleep(5)  
 +    led_yellow.value(1)
 </​code>​ </​code>​
  
 单击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>​