基于STEP-PICO实现一个交通灯控制器
1. 背景介绍
本人于2023年寒假购入了硬禾公司的2023寒假一起练平台(1)- 基于STEP Pico的嵌入式系统学习平台,并完成了其中的项目2——基于STEP-PICO实现一个交通灯控制器。现对其内容及原理进行解释和说明。
交通灯控制器的算法原理是根据交通流量和道路情况,通过计算机程序控制交通灯的开关,以达到交通流畅和安全的目的。具体来说,交通灯控制器的算法原理包括以下几个方面:
1. 信号配时算法:根据道路交通流量和行车速度等因素,计算出每个方向的信号配时,以确保交通流畅和安全。
2. 车辆检测算法:通过车辆检测器等设备,实时监测道路上的车辆流量和车速,以便及时调整信号配时。
3. 优先通行算法:对于公交车、救护车等特殊车辆,通过优先通行算法,确保它们能够快速通过交通路口。
4. 交通流量控制算法:当交通流量过大时,通过控制信号配时和车辆通行速度等方式,控制交通流量,避免交通拥堵和事故发生。
5. 紧急情况处理算法:当发生紧急情况时,如交通事故、火灾等,交通灯控制器会立即切换到紧急模式,以便及时疏散人员和车辆。总之,交通灯控制器的算法原理是通过计算机程序控制交通灯的开关,以达到交通流畅和安全的目的。
当然,如此复杂的情况运用一小块开发板并不足以实现,因此我将会对其功能进行简化。
如下图所示是本人所用的开发板的引脚图,其上连接的扩展板上拥有以下功能:
2个按键输入
4个单色LED
12个WS2812B RGB三色灯
1个姿态传感器
1个128*64 OLED显示屏
1个蜂鸣器
1个可调电位计(用于电压表)
1路音频信号输入(用于示波器)
8位R-2R电阻网络构成的DAC(用于DDS信号发生器)
功能及管脚映射图如上图所示。
STEP PICO是一款低成本,高性能的微控制器开发板,具有灵活的数字接口(完全兼容Raspberry Piano Pico)在硬件上,采用树莓派官网自主研发的RP2040微控制器芯片,搭载了ARM Cortex M0+双核处理器,高达133MHz的运行频率,内置了264KB sram和2MB山村,还板载有多大26个多功能的GPIO引脚,软件上可选择树莓派提供的C语言或C++SDK,或者使用MicroPython进行开发,本人在此次实践中使用的是MicroPython语言。
2.基本功能及设计思路
2.1基本功能
本次实践主要实现的功能是,通过在oled显示屏上打印出预设好的图案来模拟小人在交通灯控制器下通行。
预设时间为,红灯亮五秒,黄灯亮两秒,绿灯亮五秒,黄灯亮两秒,如此循环往复。小人在绿灯亮起或绿灯后的黄灯亮起时可以通过,其余时间不可通过。
同时,本次实践拓展了以下功能:
在上述的一个循环内,若按下k1键,则可以在下次循环中延长五秒红灯的时间,并且再延长的时间内启动蜂鸣器,提醒行人快速通过。
2.2具体设计思路及注释:
第一部分代码:按下k1后在下一循环中延长五秒红灯时间并在延长的时间内启动蜂鸣器
#进行库的引入
import time
import ws2812b
from button import k1
from machine import PWM,Pin
from board import pin_cfg
pwm = PWM(Pin(pin_cfg.buzzer))
#定义启动蜂鸣器的函数
def pitch(frequency, duration=0):
pwm.freq(frequency)
pwm.duty_u16(3000)
time.sleep_ms(duration)
#定义红灯亮起的函数
def red_light():
for i in range(1,13):
ws2812b.on(i, "#ff0000")
#定义黄灯亮起的函数
def yellow_light():
for i in range(1,13):
ws2812b.on(i, "#ffff00")
#定义绿灯亮起的函数
def green_light():
for i in range(1,13):
ws2812b.on(i, "#00ff00")
#主循环
while True:
#当按下k1时,红灯增加五秒时间
if k1.value() == True:
red_light()
print("+5 start")
for i in range(10):
for freq in range(880, 1760, 16):
pitch(freq, 6)
for freq in range(1760, 880, -16):
pitch(freq, 6)
print("+5 end")
pwm.deinit()
#若没有按下k1,则正常循环红五秒,黄两秒,绿五秒,黄两秒
red_light()
time.sleep(5)
yellow_light()
time.sleep(2)
green_light()
time.sleep(5)
第二部分代码:小人在绿灯及绿灯后的黄灯可以通行,其余时间不可通行
#引入库函数
import ws2812b
import utime, time
import _thread
from machine import SPI, Pin
from ssd1306 import SSD1306_SPI
import framebuf
from astronaut import frames
from board import pin_cfg
import gc
spi = SPI(1, 100000, mosi=Pin(pin_cfg.spi1_mosi), sck=Pin(pin_cfg.spi1_sck))
oled = SSD1306_SPI(128, 64, spi, Pin(pin_cfg.spi1_dc),Pin(pin_cfg.spi1_rstn), Pin(pin_cfg.spi1_cs))
# 清空oled显示屏
oled.fill(0)
state = 1
#定义小人移动的函数
def the_second_loop():
global state
x = -64
fb = [framebuf.FrameBuffer(frames[fr], 64, 64, framebuf.MONO_HLSB)
for fr in range(0, 48)]
while True:
for fr in range(0, 48):
if state == 0: continue
oled.blit(fb[fr], x, 0)
gc.collect()
utime.sleep_ms(40)
if x < 128:
x = x + 1
else:
x = -64
print(x)
oled.show()
#多线程运行
_thread.start_new_thread(the_second_loop, ())
green = "#00ff00"
red = "#ff0000"
yellow = "#fff00"
#主循环
while True:
ws2812b.on_all(red)
state = 0
time.sleep(4)
ws2812b.on_all(yellow)
time.sleep(2)
ws2812b.on_all(green)
state = 1
time.sleep(4)
ws2812b.on_all(yellow)
time.sleep(4)
设计思路:
- 在引入库函数之后对蜂鸣器函数进行引用,并且在主循环中持续监测是否按下k1键,若按下k1键,则触发以下程序:延长五秒红灯亮的时间并在此期间启动蜂鸣器,同时在下一循环开始前清空内存以保证不会影响下一循环的运行结果。
- 通过文件导入一个小人的oled动画来模拟小人的通行。首先清空oled显示屏并将动画文件读入内存中,同时定义小人行走的程序,即设定标志位从-64到128,循环显示小人的行走动画。设定状态位state,来关联当前灯亮的情况,若为红色,则状态位设为0,此时小人不动,当绿灯亮起时,状态位更新为1,此时运行小人行走程序,当红色再次亮起时更新状态位为0,由此来保证小人在绿灯亮起以及绿灯后的黄灯亮起时可以通行,其余时间不可通行。
3. 软件框图
4. 问题总结
在进行编程的过程中,我发现了以下几个问题,并逐一进行了解决。
其一,虽然MicroPython 和 CPython 在 Python3 语法上保持高度的一致性,常用的标准语法命令都已经支持。同时,Micro Python中已经包含了很多库函数。这使得在调用的时候很方便。例如程序中用到的延时函数就是在原本库中的,相比于c语言的开发,显然Python的开发方式更为友好。然而,自己写的一部分库函数和关于接口的定义也是必不可少的。
其二,该程序中关于多线程执行的内容我认为有些没有必要,因为该开发板是单核工作的,因此在运行较大程序的时候有可能会造成内存的拥堵,导致程序出错或崩溃。不过从教学的角度来讲还是可以了解一些这方面的知识。
其三,我想谈谈今后我的发展规划,我的专业是集成电路工程,因此将来的发展方向可能主要与硬件的开发有关,而这次实践所用的Micro Python语言是我从未有过的经历,我想,这次实践可能为我打开了一个新的思路,不再局限于Verilog语言和FPGA开发,毕竟树莓派的语言对于开发者来说是更为友好的。