一、项目需求
制作一个交通灯控制器
具体要求:仿真马路上的交通灯的工作状态切换,利用板上的红、黄、绿三种颜色的LED显示道路状态的切换,行人按键时,具有优先功能
实现方式:利用板上的3个不同颜色的LED模拟交通灯,程序会轮流切换三个灯的状态,并根据按键的输入(中断或查询机制)调整交通灯的切换
二、完成的功能及达到的性能
2.1正常交通灯颜色交替变换
程序烧录进板子后,通过代码控制板子上的12个WS2812B RGB三色灯来模拟马路上真实的交通信号灯实现红、黄、绿三种颜色的交替变换。首先红灯亮起,时间持续5秒钟,之后变成黄灯警示,时间持续2秒钟,之后变为绿灯,时间持续5秒钟,再次变为黄灯,提示可通行时间即将结束,时间持续5秒钟,最后再转变为红灯,循环往复持续进行,实现一个最基本的交通信号灯模拟。
图 1 红点亮
图 2 黄灯点亮
图 3 绿灯点亮
2.2 OLED屏幕显示
在实现了基本的红绿灯功能之后,通过板子上的128*64 OLED显示屏,显示小人在绿灯时自左向右通行的效果。当红灯亮起时,小人禁止通行,红灯与绿灯之间的黄灯警示同样禁止通行。小人只有在绿灯和即将变为红灯的黄灯警示期间才可以正常通行。
图 4 OLED显示
2.3 按键控制延长绿灯时间并用蜂鸣器提示
通过板子上的K1按键控制来延长绿灯的时间,当K1键被按下时,会在下一次循环中对绿灯的持续时间延长5秒钟,并在延长的五秒钟时间里用蜂鸣器报警,模拟提示行人抓紧时间通过并警示来往车辆绿灯时间延长。
这段代码是用来控制树莓派Pico上的LED灯,蜂鸣器和OLED显示屏的,实现一个交通灯系统和一个动画效果。
三、代码及解释
3.1代码
import ws2812b
import utime, time
import _thread
from machine import SPI, Pin,PWM
from ssd1306 import SSD1306_SPI
from button import k1
import framebuf
from astronaut import frames
from board import pin_cfg
import gc
首先,导入一些需要用到的模块,如ws2812b(LED灯控制模块),utime(时间模块),_thread(多线程模块),machine(硬件控制模块),ssd1306(OLED显示屏控制模块),button(按钮模块),framebuf(帧缓冲模块),astronaut(动画帧数据模块)和board(引脚配置模块)
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))
pwm = PWM(Pin(pin_cfg.buzzer))
# Clear the oled display in case it has junk on it.
oled.fill(0)
def pitch(frequency, duration=0):
pwm.freq(frequency)
pwm.duty_u16(3000)
time.sleep_ms(duration)
然后,创建一个SPI对象,用来与OLED显示屏通信。SPI是一种串行总线协议,可以实现高速数据传输。接着,创建一个SSD1306_SPI对象,用来控制OLED显示屏的参数和功能。SSD1306是一种OLED显示屏的驱动芯片。
接着,创建一个PWM对象,用来控制蜂鸣器的频率和占空比。PWM是一种调制方式,可以用来产生不同音调的声音。接着,清空OLED显示屏上的内容,以防有杂乱的数据。然后,定义一个pitch函数,用来设置蜂鸣器的频率和持续时间,并让它发出声音。
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, ())
然后,定义一个全局变量state,用来表示是否播放动画效果。初始值为1表示播放。然后,定义一个the_second_loop函数,在另一个线程中运行。
在这个循环中首先定义一个局部变量x,表示动画帧在OLED显示屏上的横向位置。初始值为-64表示在左边界外。
最后,创建一个fb列表,存储48个framebuf对象。每个framebuf对象包含了64x64像素的单色图像数据。这些图像数据来源于astronaut模块中的frames列表。
使用一个无限循环来不断重复以下步骤:
遍历fb列表中的每个framebuf对象,并将其绘制到OLED显示屏上。使用oled.blit方法来将图像数据复制到指定位置上。使用gc.collect方法来回收内存空间。暂停40毫秒以控制动画速度。如果x小于128,则将其加1以向右移动一像素;否则将其重置为-64以从左边界重新开始移动。打印x的值以便调试。使用oled.show方法来更新OLED显示屏上的内容。使用_thread.start_new_thread方法来启动the_second_loop函数在另一个线程中运行。这样可以避免阻塞主线程中的其他代码。
green = "#00ff00"
red = "#ff0000"
yellow = "#fff00"
while True:
ws2812b.on_all(red)
state = 0
time.sleep(5)
ws2812b.on_all(yellow)
time.sleep(2)
ws2812b.on_all(green)
state = 1
time.sleep(5)
if k1.value() == True:
ws2812b.on_all(green)
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)
pwm.deinit()
state = 1
ws2812b.on_all(yellow)
time.sleep(5)
最后,在主线程中使用一个while循环来不断重复以下步骤:
将所有LED灯点亮为红色,并将state设为0表示暂停播放动画效果。等待五秒钟。将所有LED灯点亮为黄色,并等待两秒钟。将所有LED灯点亮为绿色,并将state设为1表示恢复播放动画效果。等待五秒钟。如果检测到按钮被按下,则保持所有LED灯为绿色,并使用两个for循环来产生一个上升和下降的音阶效果。关闭蜂鸣器并将state设为1表示恢复播放动画效果。
3.3 流程图
图 5 程序流程图
四、感受以及心得体会
本次硬禾学堂的2023寒假在家一起练的活动收获颇丰,基于树莓派Pico的嵌入式系统学习平台专为嵌入式系统学习而设计,其可以通过C/C++以及MicroPython编程来学习嵌入式系统的工作原理和应用,对新手非常友好,上手容易,适合没有编程基础的同学进行学习,老师讲解细致,手把手带着我们写代码并且解释意义,通俗易懂,本次设计交通信号灯的项目并不是很难,在老师的带领下非常容易就可以完成,我学到了很多,本项目也存在着一些需要完善的地方,比如按键控制后绿灯时间延长,但是OLED显示屏上的小人未能及时通过就变为红灯,会造成小人不能完全通过,后续可以优化代码实现当小人完全通过时再变为红灯。