1.项目介绍
本文展示的是一个基于RP2040芯片作为核心实现的一个反应测试器。
在系统启动之后,OLED会显示3秒倒计时,倒计时结束之后,会随机选择12个RGB灯中的一个使用随机颜色点亮,此时用户需要按下K1、K2中的一个,0~5个LED点亮时,需要按下右边的按键K2;当6~12个LED点亮时,需要按下左边的按键K1。当按下对应的按键之后,单片机会停止计时,如果从亮灯到按下正确按键的时间间隔小于500ms时,OLED屏幕上会提示GOOD;如果按下按键的时间超过500ms则OLED会显示:emmm...
2.硬件连接
由于RP2040核心板有对应的扩展地板,所以硬件链接比较简单,如下为硬件链接示意图。RP2040作为主控制器链接128*64的OLED、12个RGB WS2812、2个独立按键。
128*64的OLED与RP2040之间通过SPI进行通信;WS2812通过一个GPIO与RP2040进行通信;独立按键分别链接RP2040的两个端口,通过读取这两个端口的电平值来获取当前按键是按下还是非按下。
3.代码说明
整个代码使用MicroPython进行编写,使用Thonny进行文件上传以及测试。
3.1.初始化
如下代码为初始化部分代码,首先加载需要用到的库,库的相关用途如下
- neopixel:用于控制WS2812
- random:用于生成随机数
- time:用于延时以及获取当前启动时间
- machine:PICO外设相关,这里主要使用到了Pin、SPI
- ssd1306:用于驱动OLED 12864的库
初始化完毕之后,分别针对OLED 12864、WS2812、按键1、按键2创建对应的变量,便于后续进行操作。
import neopixel
import random
import time
from machine import Pin, SPI
from ssd1306 import SSD1306_SPI
# 创建oled对象,并清空显示
oled = SSD1306_SPI(128, 64, SPI(1, 100000, mosi=Pin(11), sck=Pin(10)), Pin(9), Pin(8), Pin(29))
oled.rotate(1)
oled.fill(0)
oled.show()
# 创建环形LED对象,并关闭所有显示
n = neopixel.NeoPixel(Pin(18), 12)
n.fill((0, 0, 0))
n.write()
# 创建按键对象
k1 = Pin(13, Pin.IN, Pin.PULL_UP)
k2 = Pin(12, Pin.IN, Pin.PULL_UP)
3.2.准备界面
如下所示,为准备界面,该部分流程的主要逻辑是:每隔一秒钟在OLED上显示剩余等待秒数,在倒计时完成时,在屏幕上显示START字样,表示游戏开始。
# 启动倒计时
for count in range(3, 0, -1):
oled.fill(0)
oled.text('Countdown: %d' % (count), 0, 0)
oled.show()
time.sleep(1)
else:
oled.fill(0)
oled.text('Start', 0, 0)
oled.show()
3.3.点亮LED
如下代码的主要目的是,从0~12中任意选择一个LED进行点亮,点亮的颜色也是随机的,R、G、B分别从0~60中选择一个数进行组合。在设置完毕之后,使用write方法进行显示
light_led = random.randrange(12)
n[light_led] = (random.randrange(60), random.randrange(60), random.randrange(60))
n.write()
3.4.检测按键
如下代码的目的为:记录LED点亮的时间戳,使用系统启动时间,单位为毫秒,然后等待对应的按键被按下,如果是0~6LED被点亮,则检查K1按键是否被按下;如果是7~12 LED被点亮,则检查K2按键是否被按下。如果按下,则记录了当前系统启动时间,通过结束时间减去起始时间以此来获取玩家的反应速度。
# 获取当前时间作为起始时间
time_begin = time.ticks_ms()
while True:
if 0 <= light_led and light_led < 6 and 0 == k1.value():
break
elif 6 <= light_led and light_led < 12 and 0 == k2.value():
break
# 获取当前时间作为结束时间
time_end = time.ticks_ms()
time_interval = time_end - time_begin
3.5.结果展示
如代码主要逻辑为关闭LED、在OLED上展示游戏结果,展示数据包含:起始时间戳、结束时间戳、间隔时间。当间隔时间小于500ms时提示:Very Good、当间隔时间大于500ms小于1000ms时提示:Good,当间隔时间大于1000ms时,提示:emmm....
# 关闭LED
n[light_led] = (0, 0, 0)
n.write()
# 显示结果
# 显示起始时间
oled.text('B: %d ms' % (time_begin), 0, 0)
# 显示结束时间
oled.text('E: %d ms' % (time_end), 0, 12)
# 显示间隔时间
oled.text('I: %d ms'% (time_interval), 0, 24)
if time_interval <= 500:
oled.text('Very Good', 0, 36)
elif time_interval <= 1000:
oled.text('Good', 0, 36)
else:
oled.text('emmm...', 0, 36)
oled.show()
4.功能展示
系统启动之后或者在游戏结束以后按下任意键都会自动进入到准备阶段,该界面如下图所示,会在OLED上进行倒计时,从3倒计时到0,此时用于给玩家集中注意力以及做好准备。
在倒计时结束之后,游戏开始,会在OLED屏幕上面显示:START字样,同时会使用随机的颜色点亮12个LED中的一个。此时玩家需要快速按键LED对应的按键,在游戏设计中,以蜂鸣器、OLED所在的中线作为分界线,左边6个OLED对应的按键是左边的按键K1;右边6个OLED对应的按键是右边的按键K2。
在玩家拿下正确按键之后,屏幕上就会展示出对应的状态,B表示游戏开始的时间,单位是毫秒,E表示按下正确按键的时间,单位是毫秒,I表示从游戏开始到按下正确按键的间隔时间,单位是毫秒。
当按下的间隔时间少于600ms时,认为反应速度比较快,会提示Good;反之会显示:emmm....
玩家在结果显示页面任意按下一个按键,游戏会再次开始,进入到如下的倒数页面。
5.心得
通过这次活动,接触到了树莓派基金会推出的RP2040这款双核MCU,其使用MicroPython的方式直接进行开发,操作简单,省去了传统单片机开发的复杂IDE以及很长的编译时间,大大提升了开发速度,并且该单片机集成了树莓派一贯生态丰富,参考资源众多的特点,特别适合进行快速原型开发。
但是使用Micropython以外的开发方式就比较复杂,通过Micropython如果需要添加复杂外设编程就比较复杂。
后续可以尝试复刻一下网上的一些基于树莓派的有意思的项目,例如示波器、逻辑分析仪等。