2023寒假在家一起练
基于Step Pico的嵌入式学习平台
项目名称:制作一个交通灯控制器
平台: 基于Step Pico的嵌入式平台
姓名: 赵鸿烨
1 项目要求
- 完成交通灯的模拟:能够控制扩展版上12个WS2818B RGB三色灯实现自动定时切换颜色,并且在程序中设置接口以便于改变每种信号灯的持续时间。
- 完成行人按钮功能:当K1或K2任意按键按下时,将交通灯立即切换为红灯,且该红灯的持续时间较默认时间更长。
- 时间显示:通过SSD1306 OLED显示屏显示交通灯的倒计时。
2 完成的功能
2.1 交通灯模拟以及时间显示
连接电源,即实现交通灯的模拟。
默认设置红灯持续时间为10s,绿灯为7s,黄灯为2s。当绿灯进入最后2s时,LED灯会闪烁。
2.2 行人按钮功能
按下任意按键后,指示灯会立即切换为红灯,且红灯持续时间设置为20s。之后若不再按下按钮,则红灯持续时间会恢复至10s。
3 硬件介绍
3.1 Step Pico嵌入式开发板
Step Pico是一种小型、便携式的计算机,由Raspberry Pi和Step社区开发。它是一种基于ARM架构的计算机,可运行Linux操作系统。Step Pico具有小巧,功耗低,价格低,可扩展性强的特点。Step Pico也是一种开放源代码平台,可以通过MicroPython,C/C++等编程语言进行编程。由于它的小巧便携和低功耗特性,Step Pico被广泛用于物联网、嵌入式系统、机器人、教育和科研等领域。
Step Pico采用Raspberry Pi官方自主研发的RP2040微控制器芯片,运行频率高达133MHz,内置264KB SRAM和2MB flash,还提供多大26个多功能的GPIO引脚。通过将Step Pico平台与硬禾学堂设计的扩展版相结合,能够极大程度的扩展芯片的外部电路,简化编程,以便实现更多的功能。
3.2 WS2818B
WS2812B是三通道LED驱动控制专用电路,可支持多个芯片级联。芯片在上电复位后,DIN端接受从Step Pico微控制器传输的数据。首先传入的24bit数据被第一个芯片提取后,送入芯片内部的数据锁存器,剩余数据经内部整形处理电路通过Dout端口输出至下一个像素点。芯片内部的数据锁存器会根据所接收到的24bit数据,向所控制的三颗LED灯珠发送不同占空比的PWM信号,通过更改24bit的数据,可以控制每颗LED灯珠所显示的颜色。
WS2812B的数据协议采用单线归零码的通讯方式。其中0码,1码和RES码的波形如下:
T0H |
0码高电平时间 |
220ns-380ns |
T0L |
0码低电平时间 |
580ns-1.6us |
T1H |
1码高电平时间 |
580ns-1.6us |
T1L |
1码低电平时间 |
220ns-380ns |
RES |
帧单位 |
280us以上 |
24bit数据帧结构:
采用MSB形式,以RGB顺序发送数据。
3.3 SSD1306
SSD1306是一款单片CMOS OLED驱动芯片,常用来驱动128*64分辨率的OLED显示屏。它由128个段和64个公共部分组成。
SSD1306内置对比度控制,显示RAM以及振荡器,减少了外部组件和功耗,可通过SPI或I2C总线与微处理器进行通信。SSD1306的内部结构图如下图所示:
拓展版上的显示屏使用SPI总线与Raspberry Pi进行连接。数据的写入时序如下图所示:
在SCL的每个上升沿,SDA上的数据以MSB的形式以为到八位寄存器中,每八个时钟周期对D/C#进行一次采样,根据结果将图形写入GDDRAM中。之后SDA的数据将被写入GDDRAM,控制OLED显示屏。
4 实现思路
- 使用定时器,通过中断控制LED灯的切换和OLED显示屏的数字切换;
- 通过中断实现按键控制指示灯;
- 使用ws2812B库函数对LED灯的颜色进行控制
- 使用SSD1306库函数对OLED屏幕进行显示控制,并编写字库脚本以提升显示效果。
5 程序框图
6 主要代码片段:
6.1 模n计数器
def timer_mod_n(n):
global TIMER
if TIMER != n:
TIMER = TIMER + 1
else:
TIMER = 0
输入参数n为计数器的模。
在主定时器发送中断请求时执行,其中全局变量TIMER用来控制LED灯的颜色和OLED的显示。
该函数通过MicroPython提供的软件定时器回调,软件定时器周期设置为0.5s,即该函数每0.5s调用一次。因此,TIMER变量的周期为0.5n秒。
6.2 颜色控制
def Color_Ctrl(red_t,green_t,yellow_t):
global color
global TIMER
red_t = red_t*2
green_t = green_t*2
yellow_t = yellow_t*2
if TIMER == red_t:
ws2812b.on_all(green)
color = "green"
elif TIMER == red_t+green_t-4:
ws2812b.off_all()
color = "green"
elif TIMER == red_t+green_t-3:
ws2812b.on_all(green)
elif TIMER == red_t+green_t-2:
ws2812b.off_all()
elif TIMER == red_t+green_t-1:
ws2812b.on_all(green)
elif TIMER == red_t+green_t:
ws2812b.on_all(yellow)
color = "yellow"
elif TIMER <= 0:
ws2812b.on_all(red)
color = "red"
输入red_t,green_t,yellow_t为红,绿,黄灯的持续时间,单位为秒,默认设置为红灯10s,绿灯7s,黄灯2s。
根据全局变量TIMER的值控制交通灯的颜色。
6.3 显示控制
def disp_time(timer):
if timer%2 != 0:
timer = timer + 1
else:
pass
if color == "red":
time = -timer + 20
elif color == "green":
time = -timer + 34
elif color == "yellow":
time = -timer + 38
time = time / 2
timer_bit = time % 10
timer_dic = time // 10
screen1 = screen(timer_dic,timer_bit)
screen1.display()
控制OLED屏幕显示当前交通灯的倒计时。其中screen为文件“image.py”中定义的类,用来控制OLED屏幕显示。字库存放于“ziku.py”文件中。
6.4 按键中断函数
def button_callback():
global TIMER
global color
TIMER = -21 #红灯延长10s
color = "red"
ws2812b.on_all(red)
screen1 = screen(2,0)
screen1.display()
在按键按下时触发,作用为:
- 立即将LED切换为红色
- 在默认红灯持续时间的基础上,延长10s
- 显示屏显示数值“20”
注:由于红灯默认时长为10s,按下按键后又延长10s,因此设置显示屏显示20s,当修改红灯默认持续时间和红灯延长时间后,应对应修改显示数值。
7 遇到的困难及解决方法
最初的程序架构为双线程架构,其中一个线程控制交通灯,另一线程控制OLED刷新。在烧录后发现,两线程的时钟不能严格对齐,容易出现OLED显示屏和LED灯变化不同步的问题,且由于单片机执行LED刷新或OLED显示屏刷新函数的时间未知,使用sleep函数不能精确定时。随时间推移,会产生计时不准确的问题。为解决这一问题,之后在程序中引入定时器,通过定时器发送定时信号,实现每隔0.5s刷新一次显示屏和LED灯,从而实现两者的同步。