一、项目介绍
交通灯在日常生活中随处可见,尤其是在一线城市,一个交通灯的损坏,或会引发一场交通事故,或会造成交通堵成长龙,上班族迟到的后果。之前有采用51单片机制作过交通灯控制面板,使用C语言开发。此次在硬禾的Step Pico板子上,有4颗LED灯,分别为R(红色),G(绿色),B(蓝色),Y(黄色),由于交通灯为三色,故使用其中的R(红色),G(绿色),Y(黄色)灯来仿真实际生活中交通灯的工作状态。注:由于板上蜂鸣器声音过小,所以笔者外接自购的蜂鸣器。
项目中指示灯无人控制下,三盏灯的运作状态如下:
①、开始红色LED点亮,OLED屏显示行人图样。此时车辆禁止通行,行人可顺利通过。
②、红色LED与黄色LED同时闪烁3秒,提醒行人即将禁止通过,司机做好开车准备。
③、绿色LED点亮,OLED熄屏,此时车辆可顺利通行,行人禁止通过马路。
④、绿色LED与黄色LED同时闪烁3秒,提醒司机即将禁止通行,行人做好通过马路的准备。
二、设计思路
由于只是借助pico板上集成的3盏LED灯,模拟单条马路上,车辆与行人有序安全通过,行人观察LED灯状态可能需要斜眼查看,因此引入按钮+OLED屏+蜂鸣器来提醒行人高效出行。
为了确保行人更安全通过,这里引入按钮控制交通灯状态,当有行人按了按钮后,产生一个标识状态,当运行的线程到下一周期的行人可通过状态,响起蜂鸣器提示行人,并等蜂鸣器停止鸣叫之后,继续进行默认的红灯点亮时长,再加上黄灯的闪烁环节,这样更有保障。
设计框图如下:
三、硬件资源
Step Pico板上资源丰富,交通灯项目运用到硬件资源有:RP2040微控制器,按键K1,OLED屏,蜂鸣器,R、G、Y三颗指示灯,这里着重介绍一下RP2040微控制器。
◆ 双核Arm Cortex M0 +处理器,时钟频率高达133 MHz
◆ 264KB的SRAM和2MB的板载闪存
◆ 通过USB使用大容量存储进行拖放编程
◆ 26个多功能GPIO引脚
◆ 2个SPI,2个I2C,2个UART,3个12位ADC,16个可控PWM通道
◆ 片上准确的时钟和计时器
RP2040通过焊接在板上的排针与底板相连接,使用的硬件连线示意图如下:
电路原理图如下:
四、实现功能
引入线程,行人通过按钮来开启全局标志变量button_pressed,当主线程执行到检测标志变量button_pressed时,为真时则触发蜂鸣器,并将标志变量button_pressed关闭;为假时则不用跳转到另一线程。按键稍加延时,用以消抖。为了确保每次OLED屏周期性从初始位显屏,将在子线程中设置X坐标为-64;为确保每次熄屏后不残留图样信息,稍加延时消隐处理。
软件流程图如下:
五、图片展示
六、主要代码片段展示
import machine #导入machine库
import utime #导入utime库
import _thread #导入thread库
from machine import SPI, Pin
from ssd1306 import SSD1306_SPI
import framebuf
from astronaut import frames #从astronaut图形库中引入frames变量,需要在同一目录下加载astronaut.py文件
from board import pin_cfg
import gc
global button_pressed #定义按键是否按下的全局变量
button_pressed = False
led_red = machine.Pin(26,machine.Pin.OUT) #配置红灯的管脚属性
led_yellow = machine.Pin(20,machine.Pin.OUT) #配置黄灯的管脚属性
led_green = machine.Pin(22,machine.Pin.OUT) #配置绿灯的管脚属性
button = machine.Pin(12,machine.Pin.IN,machine.Pin.PULL_UP) #配置按键的管脚属性
buzzer = machine.Pin(19,machine.Pin.OUT) #配置蜂鸣器的管脚属性
spi = SPI(1, 100000, mosi=Pin(pin_cfg.spi1_mosi), sck=Pin(pin_cfg.spi1_sck)) #配置SPI的管脚及波特率等属性
oled = SSD1306_SPI(128, 64, spi, Pin(pin_cfg.spi1_dc),Pin(pin_cfg.spi1_rstn), Pin(pin_cfg.spi1_cs)) #定义OLED采用SPI驱动的管脚配置
oled.fill(0) #初始化清屏
state = 1 #点亮OLED屏
def oled_display_loop():
global state #点亮屏的全局标志位
global button_pressed #按键被触发的全局标志位
x = -64 #每次刷图的X坐标的起始点
fb = [framebuf.FrameBuffer(frames[fr], 64, 64, framebuf.MONO_HLSB) #调用astronaut图形库中的每一帧数组文件
for fr in range(0, 48)]
while True:
if button.value() == 0: #读取按键IO口是否为低电平,为低则触发了按键,标志位置true
button_pressed = True
utime.sleep(0.02) #按键消抖延时处理
for fr in range(0, 48):
if state == 0: #确保每次熄屏再次亮屏时,图样从固定起始位刷出,故对X坐标赋固定值
x = -64
continue
oled.blit(fb[fr], x, 0) #OLED读取fb数组文件
gc.collect()
utime.sleep_ms(1)
if x < 128: #每隔1ms则X左移一个坐标
x = x + 1
else:
x = -64
print(x)
oled.show() #OLED显示
_thread.start_new_thread(oled_display_loop, ()) #启动OLED显示线程
while True:
if button_pressed == True:
state = 1
led_red.value(1) #有按键被按下触发过,说明有行人需要通过马路
led_green.value(0) #只亮红灯,禁止车辆通行
led_yellow.value(0)
for i in range(20): #蜂鸣器鸣叫20次,提醒行人可放心通过马路
buzzer.value(1)
utime.sleep(0.2)
buzzer.value(0)
utime.sleep(0.2)
global button_pressed
button_pressed = False #释放按键被触发的状态标志位
state = 1 #点亮OLED屏,显示Astronaut图样
led_red.value(1) #红灯常亮5秒,行人可通过马路,车辆禁止同行
led_green.value(0)
led_yellow.value(0)
utime.sleep(5)
led_red.value(1) #红灯与黄灯同时闪烁3s,提醒行人将要禁止通过状态
led_yellow.value(1) #此时提醒开车司机即将通行,做好驾车准备
utime.sleep(0.5)
led_red.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_red.value(1)
led_yellow.value(1)
utime.sleep(0.5)
led_red.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_red.value(1)
led_yellow.value(1)
utime.sleep(0.5)
led_red.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_red.value(1)
led_yellow.value(1)
state = 0 #熄灭OLED屏
oled.fill(0) #清屏处理
utime.sleep(0.5)
oled.fill(0) #确保无残留图样
utime.sleep(0.5)
led_red.value(0)
led_green.value(1) #绿灯常亮5秒,车辆可同行,行人禁止通过马路
led_yellow.value(0)
utime.sleep(5)
led_yellow.value(1) #绿灯与黄灯同时闪烁3s,提醒司机即将禁止通行
utime.sleep(0.5)
led_green.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_green.value(1)
led_yellow.value(1)
utime.sleep(0.5)
led_green.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_green.value(1)
led_yellow.value(1)
utime.sleep(0.5)
led_green.value(0)
led_yellow.value(0)
utime.sleep(0.5)
led_green.value(1)
led_yellow.value(1)
值得注意的是,正如视频https://v.youku.com/v_show/id_XNTk0NjYzMzE0MA==.html中介绍的内容一样,加载上述功能代码时,需要通过“Thonny”工具将必备的bsp文件拷贝到pico板中,才能正常调用封装好的库,否则运行时会报找不到相应的库。
七、遇到的主要难题
经过多次调试、检测,发现虽然通过增加延时时长、熄屏次数可以改善OLED残留图像的现象,但这种消隐的做法以牺牲时长、刷屏频率为代价,影响代码的高效执行。
八、未来的计划
经过此次的项目实践,了解了MicroPython的基本语法,以及Thonny工具的简要运用。对比C语言的开发,MicroPython语言显得很简易,无需详细得了解底层驱动过程。对于bsp板级支持py文件的实现,还有很多不解的地方,后续计划对MicroPython语言更深入理解,由于手头没有多余的LED模块,加上时间不是很充裕,所以只用到板上的单色LED指示灯作为模拟的单路交通灯,如果后续条件允许,可以考虑做多路十字交通灯控制程序。