基于树莓派RP2040嵌入式学习平台制作的俄罗斯方块(Tetris)
通过学习树莓派RP2040嵌入式学习平台,通过micropython进行编程,使用240*240的彩色IPS LCD,蜂鸣器,四向摇杆,两个轻触按键等制作一个小游戏----俄罗斯方块
标签
嵌入式系统
RP2040
俄罗斯方块
2022寒假在家练
Windy-sha
更新2022-03-07
华东理工大学
807

一.项目介绍

  选择的项目为项目3-复古游戏移植

1.设计一款经典的俄罗斯方块游戏,通过LCD屏显示,通过按键和四向摇杆来控制

2.在游戏中要通过蜂鸣器播放游戏背景音乐

  本次设计的游戏为俄罗斯方块游戏,通过蜂鸣器播放背景音乐,将各种俄罗斯方块显示在LCD屏幕上,并且通过消除满一行的俄罗斯方块来得分,若方块超出屏幕范围则GAME OVER。

消除分数=50*(2**(行数-1))

二.硬件介绍

Fl2ZvKo0nE-l41x7tFOWLZn2rhv6

  • 采用树莓派Pico核心芯片RP2040:

    • 双核Arm Cortex M0+内核,可以运行到133MHz

    • 264KB内存
    • 性能强大、高度灵活的可编程IO可用于高速数字接口

    • 片内温度传感器、并支持外部4路模拟信号输入,内部ADC采样率高达500Ksps、12位精度

    • 支持MicroPython、C、C++编程

  • 板上功能:

    • 240*240分辨率的彩色IPS LCD,SPI接口,控制器为ST7789

    • 四向摇杆 + 2个轻触按键 + 一个三轴姿态传感器MMA7660用做输入控制

    • 板上外扩2MB Flash,预刷MicroPython的UF2固件

    • 一个红外接收管 + 一个红外发射管

    • 一个三轴姿态传感器MMA7660
    • 一个蜂鸣器

    • 双排16Pin连接器,有SPI、I2C以及2路模拟信号输入

    • 可以使用MicroPython、C、C++编程

    • USB Type C连接器用于供电、程序下载

三.设计思路

  • 先规划好游戏范围,我使用的是12*16个方格用来确定俄罗斯方块的大小
  • 因为需要旋转,所以每种不同的方块实际生成所占的要是一个正方形然后再显示哪里有方块
  • 每种方块的定位都由方块的左上角的坐标来确定
  • 每次移动俄罗斯方块时都需要检测移动方向是否存在其他方块
  • 移动方块使用左上角横纵坐标加减1来改变方块位置
  • 顺逆旋转:
  • A-------      B-------
    |1|2|3|     |7|4|1|
    ---------     ---------
    |4|5|6|     |8|5|2|
    ---------     ---------
    |7|8|9|     |9|6|3|
    ---------     ---------
    b[N-j-1][i] = a[i][j]顺时针
    b[j][N-i-1] = a[i][j]逆时针
  • 设置初始化界面
  • B键控制旋转,A键控制进入游戏,四向摇杆控制左右和下落
  • 方块要自动下落

四.软件流程图

FtmFfhlmpkm1D9CMEBs5nC2eFAbo

五.主要代码片段及说明:

1.代码的一些初始化:

引入所需的库

初始化接口

import machine
import st7789 as st7789
import time
from machine import Pin, ADC
import random
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
from buzzer_music import music
from time import sleep

st7789_res = 0
st7789_dc  = 1
disp_width = 240
disp_height = 240
spi_sck=machine.Pin(2)
spi_tx=machine.Pin(3)
spi0=machine.SPI(0,baudrate=900000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
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)
song = '0 A#4 1 1;2 F5 1 1;4 D#5 1 1;8 D5 1 1;11 D5 1 1;6 A#4 1 1;14 D#5 1 1;18 A#4 1 1;20 D#5 1 1;22 A#4 1 1;24 D5 1 1;27 D5 1 1;30 D#5 1 1;32 A#4 1 1;34 F5 1 1;36 D#5 1 1;38 A#4 1 1;40 D5 1 1;43 D5 1 1;46 D#5 1 1;50 A#4 1 1;52 D#5 1 1;54 G5 1 1;56 F5 1 1;59 D#5 1 1;62 F5 1 1;64 A#4 1 1;66 F5 1 1;68 D#5 1 1;70 A#4 1 1;72 D5 1 1;75 D5 1 1;78 D#5 1 1;82 A#4 1 1;84 D#5 1 1;86 A#4 1 1;88 D5 1 1;91 D5 1 1;94 D#5 1 1;96 A#4 1 1;100 D#5 1 1;102 A#4 1 1;104 D5 1 1;107 D5 1 1;110 D#5 1 1;114 A#4 1 1;116 D#5 1 1;118 G5 1 1;120 F5 1 1;123 D#5 1 1;126 F5 1 1;98 F5 1 1'
xAxis = ADC(Pin(28))
yAxis = ADC(Pin(29))
display.fill(st7789.BLACK)
buttonB = Pin(5,Pin.IN, Pin.PULL_UP) #B
buttonA = Pin(6,Pin.IN, Pin.PULL_UP) #A

2.游戏的主函数:

通过循环控制方块下落,读取ADC的值,并且用范围来定义左右上下的按键,up()为旋转俄罗斯方块,并且再游戏循环间隙来播放背景音乐,若失败则返回GAME OVER

def game():
    global gameover,song
    mySong = music(song, pins=[Pin(23)])
    while True:
        playmusic(mySong,400)
        xValue = xAxis.read_u16()
        yValue = yAxis.read_u16()
        f=4
        if buttonB.value()==0:
            up()
            print("up")
        elif xValue in range(50000,70000):
            down()
            print("down")
        elif yValue in range(0,10000):
            left()
            print("left")
        elif yValue in range(50000,70000):
            right()
            print("right")
        else:
            print("stop")
        drawbox()
        display.fill_rect(181,0,4,240,color[1])
        display.fill_rect(236,0,4,240,color[1])
        display.text(font1,"Score",185,20)
        display.text(font2,str(score),185,60)
        display.text(font1,"Micro",185,120,color[2])
        display.text(font1,"Python",185,150,color[2])
        display.text(font1,"Tetris",185,200,color[3])
        autodown()
        if gameover==True:
            mySong.stop()
            display.fill(0x0000)
            display.text(font2,"===GAME===",45,60)
            display.text(font2,"===OVER===",45,130)
            time.sleep(1)
            restart()

3.自动下落的函数:

才用先清楚方块的数据,将纵坐标增加一,若下一排存在方块,则返回将纵坐标减一以确保方块堆叠在其他方块上,若不存在方块则继续正常刷新方块,使方块下落

def leave():
    j=box_row
    for r in box:
        i=box_column
        for d in r:
            if d==1:
                g[j][i]=0
            i+=1
        j+=1
def enter():
    global box_row
    global box_column
    j=box_row
    for r in box:
        i=box_column
        for d in r:
            if d==1:
                g[j][i]=1
            i+=1
        j+=1
def checkvalid():
    global box_row
    global box_column
    j=box_row
    for r in box:
        i=box_column
        for d in r:
            if d==1:
                if i<0 or i>= 12:
                    return False
                
                if j<0 or j>= 16:
                    return False
                
                if g[j][i]==1:
                    return False
                
            i+=1
        j+=1
    return True
def autodown():
    global box_row,box_column,box,box_id 
    leave()
    box_row+=1
    if checkvalid() == False:
        box_row-=1
        enter()
        #消除
        clear()
        
        box=newbox()
        box_column = 4
        box_row = 0
        
    enter()

4.旋转:

先旋转,检查旋转过后方块是否有重合,若重合则反转,将其归位,具体旋转方式参考游戏的设计环节

def clockwise():
    N = len(box)
    a = []
    for i in range(N):
        a.append(box[i][:])
    for i in range(N):
        for j in range(N):
            box[N-j-1][i]=a[i][j]

def counter_clockwise():
    """
    反转
    """
    N = len(box)
    a = []
    for i in range(N):
        a.append(box[i][:])
    for i in range(N):
        for j in range(N):
            box[j][N-i-1] = a[i][j]

5.盒子的设计及绘制:

规定1是方块,0是空,设计7种俄罗斯方块,并随机产生7种方块中的1种

g = []
for i in range(16):
    g.append([0]*12)    

boxO = [[1,1],[1,1]]
boxz_1 = [[1,1,0],[0,1,1],[0,0,0]]#z
boxz_2 = [[0,1,1],[1,1,0],[0,0,0]]#镜像z
box7_1 = [[1,1,0],[0,1,0],[0,1,0]]#7
box7_2 = [[0,1,1],[0,1,0],[0,1,0]]#镜像7
boxT = [[0,1,0],[1,1,1],[0,0,0]]#倒T
boxI = [[0,0,0,0,0],[0,0,0,0,0],[1,1,1,1,0],[0,0,0,0,0],[0,0,0,0,0]]
boxes = [boxO,boxz_1,boxz_2,box7_1,box7_2,boxT,boxI]

def drawbox():
    global color
    display.fill(0x0000)
    y=0
    for r in g:
        x=0
        for d in r:
            if d==1:
                display.rect(x,y,15,15,0x10)
                display.fill_rect(x+1,y+1,13,13,color[0])
                
            x+=cell
        y+=cell

6.初始化界面:

显示编辑好的初始化屏幕,并进入循环,若A键按下则初始化16*12的游戏空间之后再开始游戏

def restart():
    display.fill(0x0000)
    display.fill_rect(4, 0, 4, 240, 0x07FF)
    display.fill_rect(227, 0, 4, 240, 0x07FF)
    display.text(font2, "Micropython", 30, 10, color=0x4416)
    display.text(font2, "Tetris", 70, 60, color=0x471a)
    display.text(font2, "Start-->A", 30, 130, color=0x7e0)
    buttonValueB = buttonB.value()
    while True:
        buttonValueA = buttonA.value()
        if buttonValueA==0:
            print("OK")
            g = []
            for i in range(16):
                g.append([0]*12)  
            enter()
            game()

7.游戏背景音效的设计函数:

在间隔时间内播放背景音效

song = '0 A#4 1 1;2 F5 1 1;4 D#5 1 1;8 D5 1 1;11 D5 1 1;6 A#4 1 1;14 D#5 1 1;18 A#4 1 1;20 D#5 1 1;22 A#4 1 1;24 D5 1 1;27 D5 1 1;30 D#5 1 1;32 A#4 1 1;34 F5 1 1;36 D#5 1 1;38 A#4 1 1;40 D5 1 1;43 D5 1 1;46 D#5 1 1;50 A#4 1 1;52 D#5 1 1;54 G5 1 1;56 F5 1 1;59 D#5 1 1;62 F5 1 1;64 A#4 1 1;66 F5 1 1;68 D#5 1 1;70 A#4 1 1;72 D5 1 1;75 D5 1 1;78 D#5 1 1;82 A#4 1 1;84 D#5 1 1;86 A#4 1 1;88 D5 1 1;91 D5 1 1;94 D#5 1 1;96 A#4 1 1;100 D#5 1 1;102 A#4 1 1;104 D5 1 1;107 D5 1 1;110 D#5 1 1;114 A#4 1 1;116 D#5 1 1;118 G5 1 1;120 F5 1 1;123 D#5 1 1;126 F5 1 1;98 F5 1 1'

def playmusic(mySong,time):
    l = 0
    time0 = time/40
    while l<time0:
        l+=1
        mySong.tick()
        sleep(0.04)   

六.实现结果的展示图:

  1. 消除加分

FkUTCTTMytPySOBJBST5SrMnKDmN

   2.超出边界GAME OVER

licJEY2CYYFNQvbpjtBY2kxr_3Ts

七.遇到的主要难题及解决方法:

  • 设计背景音乐---- 通过在游戏方块下落的sleep时间内循环播放配置好的音乐
  • 方块下落的停止与重新刷新---------需要控制好游戏的边界,检测方块是否可以停住,再重新随机刷新一个新的种类的方块
  • 方块旋转的编程方法--------查询资料查到了方块旋转的算法
  • python编程的不熟悉------在b站粗略学习一下python的语法
  • 遇到自己不会的micropython编程知识------去micropython官网查询,使用翻译软件来阅读
  • 对st7789的不熟悉------看一遍st7789的文件,使用翻译软件翻译函数文档

八.未来的规划及建议:

  1. 准备具体学习一下Arm Cortex的嵌入式系统打算先从stm32f1系列的先学起
  2. 准备买个开发板来了解各种接口和具体的功能
  3. 自学一下python的入门基础
  4. 下个暑假继续参加硬禾学堂的项目,在每个假期都多了解一下单片机知识
附件下载
pico rp2040.rar
项目所有的代码和引用的库
团队介绍
华东理工大学信息科学与工程学院
团队成员
Windy-sha
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号