基于STEP PICO设计一个反应测试器
本项目基于STEP PICO嵌入式学习平台开发设计了一个测试反应时间的小游戏,使用micro python语言基于thonny编译器实现。
标签
嵌入式系统
测试
显示
“2023”寒假在家练“
陈嘉奕
更新2023-03-29
北京理工大学
334

基于STEP PICO的嵌入式系统学习平台开发的测量反应时长的小游戏项目总结

北京理工大学 信息与电子学院 陈嘉奕

  • 项目介绍

本项目是基于STEP PICO嵌入式系统学习平台开发的一个测量反应时长的小游戏。主要实现的功能是随机点亮板上的一个LED灯,按下板上的一个按键,在显示屏上显示出从灯亮到按键之间的时间。该项目使用micro python语言实现。

具体要求:随机点亮板上的一个LED,按下板上的一个按键,在显示屏上显示出从灯亮到按键之间的时间,这是心理学上的一个重要实验

实现方式:通过软件产生随机数,程序启动以后在随机数控制的时间下点亮板上的LED,被测试者按下按键以后,处理器计算从点亮灯到接收到按键之间的时间差,并将时间差通过USB显示在PC上,也可以将OLED用起来,在OLED上显示时间信息。

  • 设计思路

程序启动以后通过调用随机整数控制ws2812b灯带中LED灯的随机点亮,被测试者按下按键以后,处理器计算从点亮灯到接收到按键之间的时间差,并将时间差通过USB显示在PC和oled显示屏上。经过短暂显示结果之后随机经过一段时间,再进入下一次游戏。

  • 设计思路框图和软件流程图

FpnqtxZAPMXTJz7ruL4FfV9nL1-f

图1. 程序设计流程图

 

  • 硬件介绍
  • 、RP2040:树莓派基金会推出的双核Arm Cortex M0+微控制器,133MHz时钟速率,264KB SRAM,支持C/C++、Micro Python编程

FgI-7N9nChoX3rK6zcewrNyyQfFn

图2. RP2040

  • 、WS2812B:WS2812B是一款智能控制LED光源,它将控制电路和RGB芯片集成到5050组件包中。

FqYmxT2F1suTeAiHVL5_XtPokNK1

图3. WS2812B

  • 、SSD1306:OLED控制器 - 支持128*32、128*64 OLED显示屏

FrcubQwC8LWqAW7K_mfY8qmmpEq8

图4. SSD1306

 

  • 实现的功能及图片展示
  • 、通过产生1-12之间的随机整数控制WS2812B中的随机一盏LED灯亮起。

 Fpy8MigBsF-klJT4HYGibsdkvfzo

图5. 随机一盏LED灯亮起

 

  • 、按下按键之后,oled屏幕上和PC端显示反应时长(ms)。

FgCBClN7d-47r0F0LZNcUBUfwBef

图6. oled屏幕显示结果

FnqXbt7hh8vE0_tz0ljZizzz7VYK

图7. PC端显示结果

  • 、一轮结束之后的第二轮游戏,再随机亮一盏灯,显示结果。

Fis8et6eIlEHQ5qcSASOtvTYMhkb

图8. 第二次游戏的结果

  • 主要代码片段及说明
    import ws2812b#调用ws2812b灯带
    from oled import oled#调用oled屏幕
    from button import button#调用按键button
    from board import pin_cfg#调用board板组中的pin_cfg变量
    
    from machine import Pin#调用内置machine模块
    import time#调用内置时间模块
    import random#调用内置随机数模块
    此部分主要是调用所需要的各个模块,包括ws2812b灯带、oled屏幕、SSD1306屏幕控制模块、板上按键K1,board板组模块、machine模块、内置时间模块、随机数模块。
def result(msg):
    print(msg)#在PC端打印结果
    oled.text(msg,48,32)
    oled.show()#在oled屏幕居中显示结果

此部分为自定义结果打印函数,print部分是在PC端显示结果,后面两行是用oled模块中定义的oled.text和oled.show函数分别获取结果并在屏幕居中(长48像素宽32像素处)显示结果。

timer = 0#定义全局变量用于记录获得的精确时间
def k1_callback(pin):#根据button定义k1的中断函数callback
    global timer
    reaction=time.ticks_diff(time.ticks_ms(),timer)#用time.ticks_diff函数求反应时间差
    result(str(reaction)+"ms")#oled屏幕和PC上展示反应时间

k1 = button(pin_cfg.k1, k1_callback,trigger=Pin.IRQ_FALLING)#采用中断方式调用开关k1,下降沿触发

此部分为以中断方式调用按键K1并获取反应时间差。其中timer为自己定义的全局变量,用于记录获得的时间。k1_callback(pin)是根据button模块里面的中断函数变量定义的k1的中断函数。随后用time.ticks_diff函数求灯亮时间与按键时间的时间差,用前面定义的result函数打印结果。最后一行即为开关的中断调用,IRQ_FALLING表示使用下降沿触发。

while True:
    ws2812b.off_all()#初始状态是灯全部熄灭
    ws2812b.on(random.randint(1,12))#用random.randint(a,b)函数随机生成1-12之间的一个整数用来控制ws2812灯带随机一盏LED灯亮
    time.sleep(0.1)#灯短暂亮一下
    
    timer = time.ticks_ms()#用time.ticks函数获得精确时间
    time.sleep(random.uniform(3,5))#结果展示经过3-5秒之后进入下一次游戏
    oled.fill(0)#清屏

此部分为主循环,主要实现对灯带开关和结果显示逻辑的控制。当开关k1被按下后,初试状态是所有灯全灭,然后利用随机整数生成函数random.randint(a,b)随机生成一个1-12之间的随机整数用来控制ws2812b中任意一盏LED灯的亮起。随后用time.ticks函数获取精确的时间。在一次游戏结束之后,结果随机展示3-5秒,随后清屏进入下一次游戏。

· 遇到的问题及解决方法

(1)、不知道该如何调用某一具体范围内的随机整数。视频中的random.uniform()是输入一定范围内的任意实数。

解决方案:通过查阅micro python语法了解到random.randint(a,b)函数的用法,可用于输出[a,b]范围内的随机整数。

(2)、对中断方式调用开关k1的原理不清楚。

解决方法:通过仔细阅读button模块中有关的button函数和callback函数的定义了解开关k1的调用方法。

(3)、在显示一次结果进入下一次游戏之后,第二次oled显示的结果不清晰,甚至与第一次结果又重叠现象。

解决方法:在一次游戏结束之后对oled屏幕清屏,这样每次的结果就会清晰无误地显示在屏幕上。

· 完整代码

#Designed by cjy
import ws2812b#调用ws2812b灯带
from oled import oled#调用oled屏幕
from button import button#调用按键button
from board import pin_cfg#调用board板组中的pin_cfg变量

from machine import Pin#调用内置machine模块
import time#调用内置时间模块
import random#调用内置随机数模块

def result(msg):
    print(msg)#在PC端打印结果
    oled.text(msg,48,32)
    oled.show()#在oled屏幕居中显示结果

timer = 0#定义全局变量用于记录获得的精确时间
def k1_callback(pin):#根据button定义k1的中断函数callback
    global timer
    reaction=time.ticks_diff(time.ticks_ms(),timer)#用time.ticks_diff函数求反应时间差
    result(str(reaction)+"ms")#oled屏幕和PC上展示反应时间

k1 = button(pin_cfg.k1, k1_callback,trigger=Pin.IRQ_FALLING)#采用中断方式调用开关k1,下降沿触发

while True:
    ws2812b.off_all()#初始状态是灯全部熄灭
    ws2812b.on(random.randint(1,12))#用random.randint(a,b)函数随机生成1-12之间的一个整数用来控制ws2812灯带随机一盏LED灯亮
    time.sleep(0.1)#灯短暂亮一下
    
    timer = time.ticks_ms()#用time.ticks函数获得精确时间
    time.sleep(random.uniform(3,5))#结果展示经过3-5秒之后进入下一次游戏
    oled.fill(0)#清屏

1.主程序

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.ws2812b.py

from machine import Pin, SPI
from ssd1306 import SSD1306_SPI
import framebuf
from board import pin_cfg
 
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))



3.oled.py

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)


4.button.py

### board.py --- eetree micropython training board configuration.

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


5.board.py

 

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