基于RP2040 Game Kit 制作电子沙漏
通过使用硬禾学堂提供的RP2040 Game Kit 完成可以感应沙漏方向,以及调节计时时间的电子沙漏。
标签
显示
RP2040
2022暑假在家练
电子沙漏
RP2040 Game Kit
一颗星
更新2022-09-02
沈阳理工大学
824

一、项目介绍

本项目通过使用硬禾学堂提供的RP2040 Game Kit完成基于RP2040的电子沙漏任务,该项目作品通过部分工具完成了两个LED 灯板和RP2040 Game Kit的固定,通过RP2040 Game Kit上的按键和屏幕完成电子沙漏的一个周期的计时,并且通过上边的姿态传感器来感知沙漏方向的变化,并开始新的沙漏操作。该项目使用到了两块8*8 LED灯板,一根数据线,多根杜邦线,以及一个RP2040 Game Kit扳机。下边是连接后的实物

FkkwxclI5qDgeJpa20vt5jIaYpZP

二、硬件介绍

RP2040 Game Kit是基于树莓派RP2040的嵌入式系统学习平台,通过USB Type-C连接器供电,采用RP2040作为主控,具有双核Arm Cortex M0+内核和264KB内存,可通过MicroPython或C/C++编程,性能强大。板上四向摇杆 + 2个轻触按键 + 一个三轴姿态传感器MMA7660用做输入控制,并有240*240分辨率的彩色LCD屏显示,片内温度传感器、并支持外部4路模拟信号输入,内部ADC采样率高达500Ksps。可玩性极高,可移植多款复古游戏,还可作为电赛的控制、显示接口平台,搭配传感器、模拟电路外还可以完成更多创意项目。

采用树莓派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连接器用于供电、程序下载

原理图

FuHlkSwJnpyrXRyj2SYMnKTc62n-

三、设计思路

首先应用取模软件对两块8*8的LED灯进行流沙图像的取模,这些取模的数据保存在程序数组之中,通过将数据按一定顺序依次取出并且在LED灯板进行横向刷新,利用人眼的视觉停留来实现电子沙漏流沙的效果。LED彩色屏幕上主要显示感应到的沙漏的变化方向以及沙漏的定时总时间。彩屏为SPI接口,控制器为ST7789。通过调用函数库来完成屏幕中内容的显示。调节时间的设计使用到了芯片内置的定时器,通过增加或减少两次流沙之间的间隔来完成总的计时时间的调节。沙漏方向的感知重要通过板上的三轴姿态传感器来实现,通过传感器感受到的方向来决定流沙的方向。

软件流程图

Ft8YnvJKrYzYAcNCY16yfJqCmOHG

四、实现的功能

可以通过RP2040 Game Kit上的按钮完成计时时间的调节,首先按下按钮B,进入计时时间调节的中断,然后通过四向摇杆来进行计时时间的调节,遥杆向上,计时时间增加,流沙变慢,摇杆向下,计时时间减少,流沙变快。通过三轴姿态传感器完成沙漏方向的感知,当屏幕上的方向为UP时,沙漏向下流沙,当屏幕上方向为DOWN时,流沙方向反向。图片展示

DOWN 方向时

FspEXsvDX4m0Utpo0hKOaRN2GgIB

UP方向时

Ft7pnpGvrmrOjwI_ixZ6H4h4_o5v

五、部分代码片段展示

1 导入的模块文件

import machine
import st7789 as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
import random
import framebuf
from machine import I2C, Pin
from time import sleep
from mma7660 import MMA7660
from machine import Pin
from utime import sleep
from machine import Timer
from machine import Pin,PWM
import time

2 方向感知部分代码

i2c1 = I2C(1, scl=Pin(11), sda=Pin(10))

acc = MMA7660(i2c1)
acc.on(True)

d = bytearray(3)

r = [0 for x in range(3)]

def twos_compliment(n, nbits):
    sign_bit = 1 << nbits - 1
    sign = 1 if n & sign_bit == 0 else -1
    val = n & ~sign_bit if sign > 0 else sign * ((sign_bit << 1) - n)
    return val


def Azhongduan(buttion):
    
    global judge1
    print("in")
    judgedirect()
    judge1=0
    tim1.init(period=int(per),mode=Timer.PERIODIC,callback=gaijudge)

3 改变计时时间中断

def Bzhongduan(buttion):
    time.sleep(2)
    tim1.deinit()
    global per
    for i in range(0,100):
        adc=control.read_u16()
        duty=int(adc*(6553-3276)/0xffff)+3276
        pwm.duty_u16(duty)
        if duty <= 4000:
            per=per+156.25
            p=str(int(per*64)/1000)
            display.text(font2, p, 80, 150)
            time.sleep(0.5)
        elif duty >= 6000:
            per=per-156.25
            p=str(int(per*64)/1000)
            display.text(font2, p, 80, 150)
            time.sleep(0.5)
        tim1.init(period=int(per),mode=Timer.PERIODIC,callback=gaijudge)

4 LCD彩屏显示函数库调用

st7789_res = 0
st7789_dc  = 1
disp_width = 240
disp_height = 240
CENTER_Y = int(disp_width/2)
CENTER_X = int(disp_height/2)
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)
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.GREEN)
display.text(font2, "DIRECTION:", 0, 10)
display.text(font2, "Timing time", 50, 90)
display.text(font2, "(S)", 170, 150)

5 LCD灯板显示部分

class SR:
    def __init__(self, ser, srclk, rclk):
        self.ser = ser
        self.srclk = srclk
        self.rclk = rclk
        self.ser.init(ser.OUT, value=0)
        self.srclk.init(srclk.OUT, value=0)
        self.rclk.init(rclk.OUT, value=0)
    def _clock(self):
        self.srclk(1)
        self.srclk(0)
    def bit(self, value, latch=False):
        self.ser(value)
        self._clock()
        if latch:
            self.latch()
    def bits(self, value, num_bits, latch=False):
        for i in range(num_bits):
            self.bit((value >> i) & 1)
        if latch:
            self.latch()
    def latch(self):
        self.rclk(1)
        self.rclk(0)
#定义第一块板子的引脚
ser1 = Pin(18, Pin.OUT)
rclk1 = Pin(20, Pin.OUT)
srclk1 = Pin(19, Pin.OUT)
sr1 = SR(ser1, srclk1, rclk1)
#定义第二块板子的引脚
ser2 = Pin(27, Pin.OUT)
rclk2 = Pin(21, Pin.OUT)
srclk2 = Pin(22, Pin.OUT)
sr2 = SR(ser2, srclk2, rclk2)

def tupian1(value1,value2,value3,value4,value5,value6,value7,value8):
    number1=0x0000
    numberarr=[value8,value7,value6,value5,value4,value3,value2,value1,0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100]
    for i in range(0,8):
        j=i+8
        number1=numberarr[j]+numberarr[i]
        sr1.bits(number1, 16) 
        sr1.latch()
        time.sleep(0.00001)
    sr1.bits(0x0000, 16) 
    sr1.latch()
#第二块灯的显示

def tupian2(value1,value2,value3,value4,value5,value6,value7,value8):
    number1=0x0000
    numberarr=[value8,value7,value6,value5,value4,value3,value2,value1,0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100]
    for i in range(0,8):
        j=i+8
        number1=numberarr[j]+numberarr[i]
        
        sr2.bits(number1, 16) 
        sr2.latch()
        time.sleep(0.00001)
    sr2.bits(0x0000, 16) 
    sr2.latch()

6 采集信息数据判断部分

def donghua():
        global judge1
        global judge2
        global judge3
        if judge1!=judge2:
            i2=2*judge1
            i1=i2+1
            j1=8*judge1+128
            j2=j1+1
            j3=j2+1
            j4=j3+1
            j5=j4+1
            j6=j5+1
            j7=j6+1
            j8=j7+1
            if judge3==0:
                liusha1(i1,i2,j1,j2,j3,j4,j5,j6,j7,j8)
            elif judge3==1:
                liusha2(i1,i2,j1,j2,j3,j4,j5,j6,j7,j8)
            judge2=judge1
        i=2*judge1
        j=8*judge1+135
        if judge3==0:
            tupian1(shuju1[i][7],shuju1[i][6],shuju1[i][5],shuju1[i][4],shuju1[i][3],shuju1[i][2],shuju1[i][1],shuju1[i][0])
            tupian2(shuju1[j][7],shuju1[j][6],shuju1[j][5],shuju1[j][4],shuju1[j][3],shuju1[j][2],shuju1[j][1],shuju1[j][0])
        elif judge3==1:
            tupian2(shuju1[i][0],shuju1[i][1],shuju1[i][2],shuju1[i][3],shuju1[i][4],shuju1[i][5],shuju1[i][6],shuju1[i][7])
            tupian1(shuju1[j][0],shuju1[j][1],shuju1[j][2],shuju1[j][3],shuju1[j][4],shuju1[j][5],shuju1[j][6],shuju1[j][7])


            
            
            
            
for i in range(1,10):
    judgedirect()
    


#定时模块
def gaijudge(timer):
    global judge1
    judge1=judge1+1
    if judge1 >=64:
        judge1=64
        tim1.deinit()
        
tim1=Timer()
tim1.init(period=per,mode=Timer.PERIODIC,callback=gaijudge)

六、遇到的难题及解决办法

在本次项目中遇到的难题主要有两个,一个是LED灯板图片数据的取模,另一个就是遇到LED灯板忽灭的情况。第一个问题的解决办法是通过利用对称的关系,将LED灯板的显示函数输入方式改变,极大地减小了工作量。第二个问题出现的原因是数据之间格式的频繁转换以及在彩屏刷新时占用时间过长导致的,解决办法是不断进行调试,得出数据格式转换使用最少的方案,以及加入用于判断的中断,当需要显示的内容相同时屏幕不再刷新,最后问题得到解决。

七、未来的计划

本次项目中成功的完成了任务的要求,但项目还可以进一步的完善,而且RP2040 Game Kit的好多模块和功能还未用到,所以我会在接下来的时间里继续学习这一个板子,希望以后可以做出更好的作品。同时非常感谢硬禾学堂可以提供这个好的平台,让我学到了很多知识,在以后的日子里我也会积极地支持硬禾学堂的活动。

 

 

 

附件下载
硬禾学堂代码.rar
电子沙漏可以运行的代码
团队介绍
沈阳理工大学 自动化专业大三学生 黄德伟
团队成员
一颗星
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号