一、项目背景
硬禾课堂开展了“2021暑假练活动”,完成项目就能免费获得板卡,这让更多的同学们参与进来,让同学们学有成效,真正将所学知识用于实践。
二、项目要求
仿真马路上的交通灯的工作状态切换,利用板上的红、黄、绿三种颜色的LED显示道路状态的切换,行人按键时,具有优先功能
三、项目功能
利用板上的3个不同颜色的LED模拟交通灯,程序会轮流切换三个灯的状态,并根据按键的输入(中断或查询机制)调整交通灯的切换
四、项目实现
拿到pico拓展功能板的第一件事肯定就是了解设备的具体信息,熟悉电路和引脚位置。我这里放上几张原理图,方便大家查阅。
PICO官方支持MicroPython和C/C++ 两种编程语言。考虑到为了使用现有的ST7789驱动显示屏,作者选择了使用MicroPython进行编程。
关于IDE的配置,MicroPython拥有很多不错的IDE,在这里给大家推荐Thonny,由爱沙尼亚的 Tartu 大学开发,轻量化的同时,兼具了很多必要的功能。具体的安装方法可以参见https://www.eetree.cn/project/detail/257。
为了往PICO中拷贝程序,我们还需要下载一个UF2文件,链接https://pico.org.cn/。将文件复制到PICO中之后,我们就可以开始对它进行编程了,准备好开始了吗
设计思路:
import ws2812b;导入ws2812b模块,用于控制WS2812B彩灯
import utime, time;导入utime和time模块,用于获取和操作时间
import _thread;导入_thread模块,用于创建和管理线程
from machine import SPI, Pin从machine模块中导入SPI和Pin类,用于创建和操作SPI总线和引脚对象;
from ssd1306 import SSD1306_SPI;从ssd1306模块中导入SSD1306_SPI类,用于创建和操作OLED显示屏对象
import framebuf;导入framebuf模块,用于操作帧缓冲区
from astronaut import frames;从astronaut模块中导入frames变量,它是一个列表,包含了48个字节数组对象,每个字节数组对象表示一帧图像
from board import pin_cfg;从board模块中导入pin_cfg变量,它是一个字典,包含了开发板上各个引脚的编号
import gc;导入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))
# Clear the oled display in case it has junk on it.
oled.fill(0)
state = 1
def the_second_loop():;这是定义函数的语法。冒号表示函数体的开始
global state;这是声明state是一个全局变量,可以在函数外部访问或修改
x = -64;这是给x赋值为-64。x是一个变量,用来表示动画在屏幕上的位置
fb = [framebuf.FrameBuffer(frames[fr], 64, 64, framebuf.MONO_HLSB)
for fr in range(0, 48)]
while True:;这是创建了一个无限循环。冒号表示循环体的开始
for fr in range(0, 48):;这是创建了一个嵌套循环。range(0, 48)表示从0到47的整数序列;fr表示每次循环时取出序列中的一个元素。冒号表示循环体的开始
if state == 0: continue;
oled.blit(fb[fr], x, 0)
gc.collect();这是调用gc模块(也就是垃圾回收器)的collect方法。collect方法可以清理内存中不再使用的对象,释放空间
utime.sleep_ms(40);这是调用utime模块(也就是微秒级时间)的sleep_ms方法。sleep_ms方法可以让程序暂停一段时间,单位是毫秒。sleep_ms(40)表示暂停40毫秒。
这样,这个函数就结束了。它会不断地在OLED显示屏上显示宇航员动画的每一帧,直到state变为0
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))
# Clear the oled display in case it has junk on it.
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:这是一个条件判断语句。如果x小于128,就执行x = x + 1;否则,就执行x = -64。这样可以让动画在屏幕上移动。
for fr in range(0, 48):
if state == 0: continue
oled.blit(fb[fr], x, 0)
if x < 128:
x = x + 1
else:
x = -64
print(x);这是调用print函数打印x的值。print函数可以在控制台或终端显示输出
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(10)
ws2812b.on_all(yellow)
time.sleep(3)
ws2812b.on_all(green)
state = 1
time.sleep(10)
ws2812b.on_all(yellow)
time.sleep(3)
这段代码是用Python语言写的,用于控制一个OLED显示屏和一串WS2812B彩灯。WS2812B是一种智能控制的LED灯源,每个灯珠内部集成了控制电路和RGB芯片。
• import ws2812b:导入ws2812b模块,用于控制WS2812B彩灯。WS2812B是一种智能控制LED光源,它将控制电路和RGB芯片集成在一个5050封装的元件中 (digikey.com) (blog.csdn.net) (jianshu.com)。
• import utime, time:导入utime和time模块,用于获取和操作时间。
• import _thread:导入_thread模块,用于创建和管理线程。
• from machine import SPI, Pin:从machine模块中导入SPI和Pin类,用于创建和操作SPI总线和引脚对象。
• from ssd1306 import SSD1306_SPI:从ssd1306模块中导入SSD1306_SPI类,用于创建和操作OLED显示屏对象。
• import framebuf:导入framebuf模块,用于操作帧缓冲区。
• from astronaut import frames:从astronaut模块中导入frames变量,它是一个列表,包含了48个字节数组对象,每个字节数组对象表示一帧图像。
• from board import pin_cfg:从board模块中导入pin_cfg变量,它是一个字典,包含了开发板上各个引脚的编号。
• import gc:导入gc模块(垃圾回收器),用于清理内存中无用的对象。
代码是用MicroPython写的,这是一种运行在微控制器上的Python版本 (translate.google.com)。代码使用了几个模块和库来控制一个OLED显示屏和一个WS2812B LED灯带。
OLED显示屏是一种使用有机发光二极管来显示图像或文字的屏幕 (bing.com)。WS2812B LED灯带是一种光源,它有多个LED,每个LED可以单独控制颜色和亮度 (support.microsoft.com) 。
代码有两个主要部分:一个部分创建了一个循环,使用帧缓冲区在OLED屏幕上显示一个宇航员的动画;另一个部分创建了一个循环,使用ws2812b库改变LED灯带的颜色,从红色到黄色到绿色 (support.microsoft.com)。
代码还使用了线程来同时运行两个循环,并且使用了一个叫做state的变量来控制动画什么时候停止或开始
这一段代码是一个函数,叫做the_second_loop。它有一个全局变量state,用来控制动画的开始和停止
第一行是def the_second_loop():,这是定义函数的语法。冒号表示函数体的开始。
第二行是global state,这是声明state是一个全局变量,可以在函数外部访问或修改。
第三行是x = -64,这是给x赋值为-64。x是一个变量,用来表示动画在屏幕上的位置。
第四行是fb = [framebuf.FrameBuffer(frames[fr], 64, 64, framebuf.MONO_HLSB) for fr in range(0, 48)],这是使用列表推导式创建了一个名为fb的列表。列表中的每个元素都是一个framebuf.FrameBuffer对象,它可以存储和操作图像数据。framebuf.FrameBuffer接受四个参数:frames[fr]表示从frames列表中取出第fr个元素,也就是宇航员动画的一帧;64, 64表示图像的宽度和高度;framebuf.MONO_HLSB表示图像使用单色(黑白)和水平最低有效位(HLSB)格式。
第五行是while True:,这是创建了一个无限循环。冒号表示循环体的开始。
第六行是for fr in range(0, 48):,这是创建了一个嵌套循环。range(0, 48)表示从0到47的整数序列;fr表示每次循环时取出序列中的一个元素。冒号表示循环体的开始。
第七行是if state == 0: continue,这是一个条件判断语句。如果state等于0,就执行continue语句;continue语句表示跳过本次循环剩下的部分,直接进入下一次循环。
第八行是oled.blit(fb[fr], x, 0),这是调用oled对象(也就是OLED显示屏)的blit方法。blit方法可以将图像数据复制到显示屏上。blit方法接受三个参数:fb[fr]表示从fb列表中取出第fr个元素,也就是宇航员动画的一帧;x, 0表示图像在显示屏上的位置坐标(横坐标为x,纵坐标为0)。
第九行是gc.collect(),这是调用gc模块(也就是垃圾回收器)的collect方法。collect方法可以清理内存中不再使用的对象,释放空间。
第十行是utime.sleep_ms(40),这是调用utime模块(也就是微秒级时间)的sleep_ms方法。sleep_ms方法可以让程序暂停一段时间,单位是毫秒。sleep_ms(40)表示暂停40毫秒。
这样,这个函数就结束了。它会不断地在OLED显示屏上显示宇航员动画的每一帧,直到state变为0。
接下来定义的循环:
第十一行是if x < 128: x = x + 1 else: x = -64,这是一个条件判断语句。如果x小于128,就执行x = x + 1;否则,就执行x = -64。这样可以让动画在屏幕上移动。
第十二行是print(x),这是调用print函数打印x的值。print函数可以在控制台或终端显示输出。
第十三行是oled.show(),这是调用oled对象(也就是OLED显示屏)的show方法。show方法可以更新显示屏上的内容。
第十四行和第十五行都是空行,没有任何作用。
第十六行是_thread.start_new_thread(the_second_loop, ()),这是调用_thread模块(也就是线程)的start_new_thread方法。start_new_thread方法可以创建一个新的线程,并运行指定的函数。start_new_thread接受两个参数:the_second_loop表示要运行的函数;()表示传递给函数的参数(为空)。
第十七行到第二十行都是定义了三个变量:green, red和yellow,对应LEDs呈现不同的颜色。
之后是一段while循环
这一段代码是一个无限循环,它会不断地改变灯光的颜色和状态。
第一行是while True:,这是创建了一个无限循环。冒号表示循环体的开始。
第二行是ws2812b.on_all(red),这是调用ws2812b模块(也就是LED灯带)的on_all方法。on_all方法可以让所有的LED灯都亮起指定的颜色。red表示红色,也就是之前定义的变量。
第三行是state = 0,这是给state赋值为0。state是一个全局变量,用来控制动画的开始和停止。
第四行是time.sleep(10),这是调用time模块(也就是秒级时间)的sleep方法。sleep方法可以让程序暂停一段时间,单位是秒。sleep(10)表示暂停10秒。
第五行是ws2812b.on_all(yellow),这是调用ws2812b模块(也就是LED灯带)的on_all方法。on_all方法可以让所有的LED灯都亮起指定的颜色。yellow表示黄色,也就是之前定义的变量。
第六行是time.sleep(3),这是调用time模块(也就是秒级时间)的sleep方法。sleep方法可以让程序暂停一段时间,单位是秒。sleep(3)表示暂停3秒。
第七行是ws2812b.on_all(green),这是调用ws2812b模块(也就是LED灯带)的on_all方法。on_all方法可以让所有的LED灯都亮起指定的颜色。green表示绿色,也就是之前定义的变量。
第八行是state = 1,这是给state赋值为1。state是一个全局变量,用来控制动画的开始和停止。
第九行是time.sleep(10),这是调用time模块(也就是秒级时间)的sleep方法。sleep方法可以让程序暂停一段时间,单位是秒。sleep(10)表示暂停10秒。
第十行是ws2812b.on_all(yellow),这是调用ws2812b模块(也就是LED灯带)的on_all方法。on_all方法可以让所有的LED灯都亮起指定的颜色。yellow表示黄色,也就是之前定义的变量。
第十一行是time.sleep(3),这是调用time模块(也就是秒级时间)的sleep方法。sleep方法可以让程序暂停一段时间,单位是秒。sleep(3)表示暂停3秒。
这样,这个循环就结束了。它会不断地重复上面的步骤,让灯光变换颜色和状态。
实物展示:
五、项目总结
本次活动是我第一次尝试单片机编程,勇敢的迈出了第一步,由于本人刚刚大一,受限于专业知识水平,和实践经验的不足。对于很多东西,尤其是硬件方面的逻辑还不甚了解,这是下一步要学习的内容。经验的话就是不要对一些自己不太了解的东西感到害怕,学会在探索中学习。再一点就是模块化编程很重要。
最后感谢电子森林、硬禾学堂提供这次学习交流的机会。