一、项目需求和功能实现
-设计或移植一款经典的游戏,通过LCD屏显示,通过按键和四向摇杆控制游戏的动作,-本次选择的游戏是2048,具体需求有:
-驱动LCD屏幕完成图形和数字的绘制
-通过ADC采集摇杆信号,判断摇杆位置
-完成2048游戏的矩阵变换逻辑,以摇杆位置为控制信号
-读取按键状态,完成悔棋机制
在最终的设计中,摇动摇杆即可完成相应方向的数字变换,点击按钮B即可返回上一次操作。
整体实现的流程图如下
二、具体实现代码
2.1 导入python库
import uos
import machine
import time
import st7789 as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
from machine import Pin
import framebuf
import random
将一些MicroPython支持的库导入,其中fonts st7789两个库是参考自板卡自带的例子,用于字体显示和驱动LCD屏幕。machine是MicroPython中一个非常重要的库,内置有操作GPIO、定时器、PIO和各种通信协议的类,可以直接调用。在本次设计中,主要用machine库来设置SPI0、配置、读取ADC的值、操作GPIO,以完成摇杆和按键状态的读取。time库用于完成一些延时,主要有sleep() sleep_ms() sleep_us() 等几个函数。framebuf用于创建显示缓存,将要显示的图形缓存好,通过SPI发送给屏幕控制器。random用于生成随机数。
2.2 初始化屏幕显示
st7789_res = 0
st7789_dc = 1
disp_width = 240
disp_height = 240
CENTER_Y = int(disp_width/2)
CENTER_X = int(disp_height/2)
print(uos.uname())
spi_sck=machine.Pin(2)
spi_tx=machine.Pin(3)
spi0=machine.SPI(0,baudrate=4000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
#
print(spi0)
display = st7789.ST7789(spi0, disp_width, disp_width,
reset=machine.Pin(st7789_res, machine.Pin.OUT),
dc=machine.Pin(st7789_dc, machine.Pin.OUT),
xstart=0, ystart=0, rotation=0)
display.fill(st7789.BLACK)
display.fill_rect(46,46,148,148,st7789.WHITE)
这里定义了一些SPI协议的引脚,比如st7789_res = 0,这里的0指的是RP2040GPIO编号,对应于原理图的框内标识。machine.spi 定义了使用40kHz波特率的SPI0,并将其绑定在所定义的引脚上。st7789.ST7789实例化了一个对象display,方便我们后续调用库中的方法。随后进行了屏幕的初始化,两层for循环内指定了背景方块的位置,具体方法和实际效果如下图所示。
2.3 定义类
放一下类定义的伪代码
class Matrix2048():
def __init__(self, column: int = 4):
def last_step(self):
def generate_number(self):
def init(self):
def move_left(self):
def move_right(self):
def move_up(self):
def move_down(self):
def update_ui(self):
定义的矩阵变换类中有九个函数__init__()是初始化函数,在类被实例化的时候会执行一次。注意该函数中重新定义了一个同名实例display,和此前屏幕初始化部分的实例是不同的,只能用于类的内部。last_step()用于反悔,支持一次撤回操作。generate_number()用于确定新生成基数2的随机位置。函数init()用于初始化屏幕,注意这一部分和此前的操作相同。接下来四个是矩阵变换函数,用于在对应的输入到来时完成显示矩阵的变化。最后是update_ui()函数,用于更新显示矩阵。
2.4 读取四项摇杆方位和按键状态
x_side = machine.ADC(28)
y_side = machine.ADC(29)
button = machine.Pin(buttonB,Pin.IN)
up = 0
down = 0
left = 0
right = 0
regret = 0
g = Matrix2048(4)
machine.ADC()用于读取对应引脚的模拟电压值,括号内的数值同样是GPIO编号。ADC默认16bit精度,即返回的数值范围为(0,65535)。machine.Pin用于指定和配置引脚。此外还定义了一些标志变量,并实例化了上文所描述的类。
2.5 主循环
while True:
updown = x_side.read_u16() #up: 400 down :64000
leftright = y_side.read_u16() #left:256 right:65000
#normal: 33000~34000
time.sleep(0.1)
#====================================================
#--------------button and direction------------------
#====================================================
if button.value() == 0:
regret = 1
if updown <= 400:
up = 1
elif updown >= 64000:
down = 1
elif leftright <= 300:
left = 1
elif leftright >= 65000:
right = 1
#====================================================
#--------------------call update---------------------
#====================================================
if up: # 向上
time.sleep_ms(30)
g.move_up()
g.generate_number()
g.update_ui()
elif down: # 向下
time.sleep_ms(30)
g.move_down()
g.generate_number()
g.update_ui()
elif left: # 向左
time.sleep_ms(30)
g.move_left()
g.generate_number()
g.update_ui()
elif right: # 向右
time.sleep_ms(30)
g.move_right()
g.generate_number()
g.update_ui()
#====================================================
#----------------------regret------------------------
#====================================================
if regret:
display.text(font2, "regret",46,14,st7789.WHITE,st7789.BLACK)
g.last_step()
g.update_ui()
regret_flag = 1
time.sleep_ms(500)
regret = 0
display.text(font2, "regret",46,14,st7789.BLACK,st7789.BLACK)
#====================================================
#--------------------reset flag----------------------
#====================================================
up = 0
down = 0
left = 0
right = 0
regret = 0
主循环中指明了判断摇杆方向的取值范围,这一范围是实际读取大量操作数据后估算的。摇杆判明方向后,将对应的标志变量置1,完成后续的判断操作。同时由于每次循环的周期至少大于100ms,因此无需进行按键的消抖。
三、收获和感想
这是我首次使用MicroPython对嵌入式系统进行编程。不得不感慨一下可以如此方便地调用底层硬件功能,而无需过于繁杂地记忆底层函数和校对语言格式。我在这个项目中投入的时间不长,整体实现方面还有很多可以改进的地方:撤回操作仅支持一步回退,可以更改为多次历史记录;获得的分数可以显示上去;初始化的时候可以放置一些别的屏幕等等。RP2040 Game Kit上也还有很多别的硬件资源(比如PIO和三轴姿态传感器、红外收发),我将会继续探索,期待有一天能赶上群里大佬的步伐。