2023寒假在家一起练-基于step pico设计的交通灯
仿真马路上的交通灯的工作状态切换,利用板上的红、黄、绿三种颜色的LED显示道路状态的切换,行人按键时,具有优先功能。利用板上的3个不同颜色的LED模拟交通灯,程序会轮流切换三个灯的状态,并根据按键的输入(中断或查询机制)调整交通灯的切换
标签
嵌入式系统
数字逻辑
2023寒假在家练
tinytuarn
更新2023-03-29
314

一、项目背景

硬禾课堂开展了“2021暑假练活动”,完成项目就能免费获得板卡,这让更多的同学们参与进来,让同学们学有成效,真正将所学知识用于实践。

二、项目要求

仿真马路上的交通灯的工作状态切换,利用板上的红、黄、绿三种颜色的LED显示道路状态的切换,行人按键时,具有优先功能

三、项目功能

利用板上的3个不同颜色的LED模拟交通灯,程序会轮流切换三个灯的状态,并根据按键的输入(中断或查询机制)调整交通灯的切换

四、项目实现

拿到pico拓展功能板的第一件事肯定就是了解设备的具体信息,熟悉电路和引脚位置。我这里放上几张原理图,方便大家查阅。FpHeq4UpR1jWO1us9vul2yQAnCp_Fg9iiiNvXcsA0u6SfchQzU4fPDLRFny7hzNScXCOrdM37MhPUinMFjcs

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中之后,我们就可以开始对它进行编程了,准备好开始了吗

设计思路:

FnkEqQ4LEQdXzObHhWJrZswDP7JD

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秒。

这样,这个循环就结束了。它会不断地重复上面的步骤,让灯光变换颜色和状态。

实物展示:FqlduvyMjWFWw3vUiCIm0VJn2f9oFvVGTGUWXze9LplOLucD8rCQQdx8FhOV4VhSLt8sMDuucS3FPsYyy5iD

 

五、项目总结

本次活动是我第一次尝试单片机编程,勇敢的迈出了第一步,由于本人刚刚大一,受限于专业知识水平,和实践经验的不足。对于很多东西,尤其是硬件方面的逻辑还不甚了解,这是下一步要学习的内容。经验的话就是不要对一些自己不太了解的东西感到害怕,学会在探索中学习。再一点就是模块化编程很重要。

最后感谢电子森林、硬禾学堂提供这次学习交流的机会。

 

附件下载
traffic-light-oled.py
团队介绍
李泽
团队成员
tinytuarn
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号