2023寒假一起练平台-基于STEP Pico的制作一个交通灯控制器
基于STEP Pico的制作一个交通灯控制器,通过thonny软件使用microPython编程,使用到了三个单色LED,蜂鸣器,两个按键
标签
嵌入式系统
FPGA
2023寒假在家练
学好再玩
更新2023-03-29
北京理工大学
317

一、任务要求

1.仿真马路上的交通灯的工作状态切换,利用板上的红、黄、绿三种颜色的LED显示道路状态的切换

2.利用板上的按键模拟行人按键,改变红绿灯时间,行人按键具有优先功能

二、环境配置

1、thonny:

硬禾课堂老师在直播中推荐的开发软件,thonny页面简洁,基础功能齐全,简单易上手,非常适合初学者(比如我)。安装的教程网上比较多,可以参考https://class.eetree.cn/live_pc/l_63acf922e4b07b05582a719d

2、硬禾学堂树莓派pico平台:

硬禾学堂为“寒假在家一起练”制作了一个平台,这平台正是我视频中演示用到的板子他的原理图如下

FuH5-IFpfIl8mT3BWKiI1NgwHqwe

功能和引脚见下

FtmyQ-Gzvau_Ho0MBNxFmpnDTV6s

三、程序实现

程序均使用micropython编写,在thonny上编译通过并烧录到板卡上。

1. 模块介绍

1.1 board

查阅管脚图,配置各管脚信息

class pin_cfg:
    yellow_led = 20
    blue_led = 21
    green_led = 22
    red_led = 26
    
    buzzer = 19
    mic = 27
    
    i2c0_scl = 17
    i2c0_sda = 16
    
    i2c1_scl = 15
    i2c1_sda = 14

    spi1_mosi = 11
    spi1_sck = 10
    spi1_dc = 9
    spi1_rstn = 8
    spi1_cs = 29

    adc0 = 26
    adc1 = 27

    k1 = 12
    k2 = 13

    pot = 28

1.2 button

配置k1和k2按键信息,并且进行按键消抖。

import time
from board import pin_cfg
from machine import Pin

class button:
    def __init__(self, pin, callback=None, trigger=Pin.IRQ_RISING, min_ago=200):
        #print("button init")
        self.callback = callback
            
        self.min_ago = min_ago
        self._next_call = time.ticks_add(time.ticks_ms(), self.min_ago)

        self.pin = Pin(pin, Pin.IN, Pin.PULL_UP)

        self.pin.irq(trigger=trigger, handler=self.debounce_handler)

        self._is_pressed = False

    def call_callback(self, pin):
        #print("call_callback")
        self._is_pressed = True
        if self.callback is not None:
            self.callback(pin)

    def debounce_handler(self, pin):
        #print("debounce")
        if time.ticks_diff(time.ticks_ms(), self._next_call) > 0:
            self._next_call = time.ticks_add(time.ticks_ms(), self.min_ago)
            self.call_callback(pin)

    def value(self):
        p = self._is_pressed
        self._is_pressed = False
        return p

k1 = button(pin_cfg.k1)
k2 = button(pin_cfg.k2)

1.3 ws2812b

控制灯的颜色,并且确保12个LED灯可以同时亮或灭。

import array, time, math
from machine import Pin
import rp2

LED_COUNT = 12 # number of LEDs in ring light
PIN_NUM = 18 # pin connected to ring light
brightness = 1.0 # 0.1 = darker, 1.0 = brightest

@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT,
             autopull=True, pull_thresh=24) # PIO configuration
def ws2812():
    T1 = 2
    T2 = 5
    T3 = 3
    wrap_target()
    label("bitloop")
    out(x, 1)               .side(0)    [T3 - 1]
    jmp(not_x, "do_zero")   .side(1)    [T1 - 1]
    jmp("bitloop")          .side(1)    [T2 - 1]
    label("do_zero")
    nop()                   .side(0)    [T2 - 1]
    wrap()

state_mach = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(PIN_NUM))
state_mach.active(1)

pixel_array = array.array("I", [0 for _ in range(LED_COUNT)])

def update_pix(brightness_input=brightness): # dimming colors and updating state machine (state_mach)
    dimmer_array = array.array("I", [0 for _ in range(LED_COUNT)])
    for ii,cc in enumerate(pixel_array):
        r = int(((cc >> 8) & 0xFF) * brightness_input) # 8-bit red dimmed to brightness
        g = int(((cc >> 16) & 0xFF) * brightness_input) # 8-bit green dimmed to brightness
        b = int((cc & 0xFF) * brightness_input) # 8-bit blue dimmed to brightness
        dimmer_array[ii] = (g<<16) + (r<<8) + b # 24-bit color dimmed to brightness
    state_mach.put(dimmer_array, 8) # update the state machine with new colors
    time.sleep_ms(10)

def set_24bit(ii, color): # set colors to 24-bit format inside pixel_array
    color = hex_to_rgb(color)
    pixel_array[ii] = (color[1]<<16) + (color[0]<<8) + color[2] # set 24-bit color
    
def hex_to_rgb(hex_val):
    return tuple(int(hex_val.lstrip('#')[ii:ii+2],16) for ii in (0,2,4))

def on(n, color = "#ffffff"):
    if not ((n >= 1 and n <= 12) and isinstance(n, int)):
        print("arg error")
        return
    set_24bit((n - 1) % 12, color)
    update_pix()
    
def off(n, color = "#000000"):
    if not ((n >= 1 and n <= 12) and isinstance(n, int)):
        print("arg error")
        return
    set_24bit((n - 1) % 12, color)
    update_pix()

def on_all(color = "#ffffff"):
    for i in range(0,12):
        set_24bit(i, color)
    update_pix()

def off_all(color = "#000000"):
    for i in range(0,12):
        set_24bit(i, color)
    update_pix()

def light_value(l):
    if l > 255: l = 255
    elif l < 0: l = 0
    return "#{0:02x}{1:02x}{2:02x}".format(l, l, l)

class PixelDisplay():
    def __init__(self):
        self.pixel_array = array.array("I", [0 for _ in range(12)])

    def set_color(self, n, color):
        """set the color of pixel 'n
        n - 1...12
        color - color tuple"""
        self.pixel_array[(n - 1) % LED_COUNT] = (color[1]<<16) + (color[0]<<8) + color[2]

    def get_color(self, n):
        v = self.pixel_array[(n - 1) % LED_COUNT]
        return ((v >> 8) & 0xff, (v >> 16) & 0xff, v & 0xff)
    
    def fill(self, c):
        for i in range(1, LED_COUNT + 1):
            self.set_color(i, c)

    def dim(self, brightness_input = 1, n = None):
        if n is not None:
            cc = self.pixel_array[n - 1]
            r = int(((cc >> 8) & 0xFF) * brightness_input) # 8-bit red dimmed to brightness
            g = int(((cc >> 16) & 0xFF) * brightness_input) # 8-bit green dimmed to brightness
            b = int((cc & 0xFF) * brightness_input) # 8-bit blue dimmed to brightness
            self.pixel_array[n - 1] = (g<<16) + (r<<8) + b # 24-bit color dimmed to brightness
        else:
            for ii,cc in enumerate(self.pixel_array):
                r = int(((cc >> 8) & 0xFF) * brightness_input) # 8-bit red dimmed to brightness
                g = int(((cc >> 16) & 0xFF) * brightness_input) # 8-bit green dimmed to brightness
                b = int((cc & 0xFF) * brightness_input) # 8-bit blue dimmed to brightness
                self.pixel_array[ii] = (g<<16) + (r<<8) + b # 24-bit color dimmed to brightness
            
    def rainbow(self, offset = 0):
        for i in range(1, LED_COUNT + 1):
            rc_index = (i * 256 // LED_COUNT) + offset
            self.set_color(i, wheel(rc_index & 255))

    def render(self):
        state_mach.put(self.pixel_array, 8)

def wheel(pos):
  """Input a value 0 to 255 to get a color value.
  The colours are a transition r - g - b - back to r."""
  if pos < 0 or pos > 255:
    return (0, 0, 0)
  if pos < 85:
    return (255 - pos * 3, pos * 3, 0)
  if pos < 170:
    pos -= 85
    return (0, 255 - pos * 3, pos * 3)
  pos -= 170
  return (pos * 3, 0, 255 - pos * 3)

2. 整体实现

FkdLYqxw1ymNs761u_3jOEqNk_zw

整体见上图,其中traffic-lights为主程序,各模块协调工作实现了交通控制灯的功能。button、board、ws2812b为副程序,用于节省主程序代码,并且实现多次调用。程序直接保存在了Raspberry Pi Pico开发板上。

主程序traffic-lights各模块见下所示:

import time
import ws2812b
from button import k1, k2
from machine import PWM, Pin
from board import pin_cfg

硬件初始化,将之前的辅助程序带入主程序。调用time库通过machine调用PWM和pin。

pwm = PWM(Pin(pin_cfg.buzzer))

定义pwm,确保蜂鸣器正常使用。

def pitch(frequency, duration=0):
    pwm.freq(frequency)
    pwm.duty_u16(3000)
    time.sleep_ms(duration)

定义蜂鸣器频率信息,控制其工作时间

def red_light():
    for i in range(1, 13):
        ws2812b.on(i, "#ff0000")    
def yellow_light():
    for i in range(1, 13):
        ws2812b.on(i, "#ffff00")
def green_light():
    for i in range(1, 13):
        ws2812b.on(i, "#00ff00")

定义三个灯颜色的函数,实现三色灯的控制,确保ws2812b控制红色、黄色、绿色准确无误,同色灯同时闪烁,LED可以显示相应的颜色。括号里的"#ff0000"、"#ff0000"、"#ffff00"依次为红、黄、绿。

while True:
    if k1.value() == True:
        red_light()
        print("+6 start")
        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)
        print("+6 end")
        pwm.deinit()
    elif k2.value() == True:
        green_light()
        print("+6 start")
        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)
        print("+6 end")
        pwm.deinit()

定义一个主循环。循环中,判断k1和k2的值,来决定如何执行之后的操作。按下k1,表示要增加红灯的时间,延长时间为6s左右,即循环10次蜂鸣器的时间,并通过蜂鸣器示警,表示红灯此时是特殊情况,与以往不同,达到警示路口车辆的作用。而按下k2,表示要增加绿灯的时间,延长时间同上,同时在下次绿灯时通过蜂鸣器示警,表示绿灯此时是特殊情况。

pwm.deinit()用来关闭蜂鸣器工作,结束示警。

    red_light()
    time.sleep(6)
    yellow_light()
    time.sleep(2)
    green_light()
    time.sleep(6)
    yellow_light()
    time.sleep(2)

定义每个灯的亮灭时间red_light控制红灯亮,time.sleep(6)表示时间为6s,同理控制黄灯亮2s,绿灯亮6s。

四、问题与解决

设计中遇到的主要问题是如何准确的通过按键来改变交通信号灯的状态,每次键入的按键会执行几次,是否可以多次执行。当然,通过学习,摸清的按键消抖可以避免命令多次输入影响结果,而且大循环保证了一次按键命令生效后,不会改变之后交通信号灯正常的运行。按键改变交通灯状态,则是行人按下控制键后会反馈给k1和k2,判断输入k1还是k2并执行相应的代码即可。

五、未来设计

本次设计只实现了简单的循环控制交通信号灯,并且行人可以改变下一次交通信号灯的状态,对于一些特殊情况没有考虑,比如行人紧急需要在红的或者绿灯期间直接延长一定的时间。可以通过异步程序来实现,碍于时间问题本次没有解决,希望在后续优化程序达到更加的高效完善。

六、学习心得

本次学习,接触了MicroPython语言,并使用该语言完成了交通信号灯项目的实现,之前的FPGA实现都是通过C语言和Verilog实现的。总体比较而言,python的编码更为简单快捷,使用简单、功能强大、容易扩展,有非常多的库可以使用。同时网络功能和计算功能也很强,方便的和其他语言配合使用。

课程中,老师还展示了如何用c语言扩展micropython的接口,可以参考https://class.eetree.cn/live_pc/l_63acf9cde4b02685a42f21ec

可以不使用thonny就实现程序的烧录,节省硬盘空间,我按照教程完成了安装,但是为了便于管理所有文件还是使用了thonny。开发板上已经提前完成了microPython的配置,所以节省了很大的时间。

本次课程的参考代码也给了我很多的帮助,想学习的可以看https://gitee.com/picospuch/eetree-mpy-lecture-code

 

附件下载
traffic-lights.py
主程序
程序.zip
库函数代码
团队介绍
北京理工大学
团队成员
学好再玩
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号