硬禾学堂树莓派RP2040实现模拟“鼠标”
一、硬件介绍以及实物展示图
本项目是基于树莓派RP2040芯片的嵌入式开发学习板,其主要功能由树莓派PICO的芯片以及事先预装在板上的功能硬件实现,以下是对于其功能的详细介绍。
-
树莓派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连接器用于供电、程序下载
-
二、项目介绍
本项目实现的是项目1 - 能控制LCD和电脑界面的“鼠标”
项目的详细要求和实现如下
-
在240*240的LCD屏幕内可以生成一个固定式样的鼠标,并且通过该鼠标进行菜单选择和参数控制(本项目实现的是鼠标移动速度的参数选择控制以及菜单选择从LCD屏幕控制进入电脑界面)
-
通过USB端口可以控制PC屏幕上的光标移动和点击操作,行使电脑鼠标的功能
三、项目设计思路以及软件流程框图
总体而言,进行该项目的设计需要预先对micro python有所了解,并且对于芯片的交互引脚的基本原理有所概念,以下是整体的设计思路。
1.项目的各项内容都离不开四向摇杆的控制,因此首先需要明确四向摇杆对应的引脚以及其控制时参数的总体变化,通过对四向摇杆具体操作参数的读入并寻求其中规律,将其从逻辑上能够对应于鼠标的“上”、“下”、“左”、“右”,同时,对于四向摇杆的操作可以类推至按键的控制,这个较为简单不再赘述。
2.先实现对于PC屏幕上的光标移动和点击操作,从具体的实现细节来看,这一步是更难的,但是从计算机组成原理的角度来看,已经有一些成熟的接口函数库帮助实现设备的I/O交互,预先完成这一步有助于将四向摇杆的控制逻辑引入操作LCD屏幕,并且也对于如何进行硬件控制LCD屏幕会有更深的理解
3.接下来进行LCD屏幕的内容实现,可以拆分成三部分
首先实现在lcd屏幕上显示一个鼠标图形
其次将整个鼠标模块与四向摇杆的操作逻辑进行关联,从而实现鼠标的移动
随后是UI交互界面的编程,显示出对话框
最后实现“鼠标”点击与对话框的交互
四、主要代码片段以及说明
import usb_hid
import hid
from machine import Pin, ADC,SPI
import utime
import st7789c
import st7789
from board import game_kit
import vga1_16x32 as font
import vga2_8x8 as fontt
m = hid.Mouse()
xAxis = ADC(Pin(28))
yAxis = ADC(Pin(29))
buttonB = Pin(5,Pin.IN, Pin.PULL_UP) #B
buttonA = Pin(6,Pin.IN, Pin.PULL_UP) #A
buttonStart = Pin(7,Pin.IN, Pin.PULL_UP)
buttonSelect = Pin(8,Pin.IN, Pin.PULL_UP)
此处是代码的开头部分,主要引入了接下来micro python需要使用的与硬件相关联的库函数。其中usb_hid是通过UF2刷固件的方式配置在树莓派PICO上的,对于电脑鼠标的I/0控制都是基于这一文件,hid.py文件则是实现了电脑上鼠标的点击移动等操作。st7789c的库函数包含了多种与LCD屏幕进行交互的函数,如绘图等,之后的UI界面的实现其实也是在此基础上的进一步封装。board则是存储了引脚对应数字的信息,便于我们进一步的带入和调用。machine则是我们与硬件信息进行读写的交互工具,如果没有其中的Pin(引脚模块)、ADC(模数转换模块)、SPI(总线模块),要实现控制硬件无从谈起。vag1是在st7789的基础上的拓展,具体实现相当于反复调用st7789的“绘制”功能,一次性绘制出了例如8*8个像素点,来便捷的输出其中规定的字典(就像python的字典)在屏幕上的内容。
库函数以外的就是需要从硬件上读入的参数被赋值到变量上的基础代码,主要是为了接下来的调用。
class hardware():
def init():
# screen
spi = SPI(0, baudrate=31250000, polarity=1, phase=1,
sck=Pin(game_kit.lcd_sck, Pin.OUT),
mosi=Pin(game_kit.lcd_sda, Pin.OUT))
tft = st7789c.ST7789(spi,240,240,reset=Pin(game_kit.lcd_rst, Pin.OUT),dc=Pin(game_kit.lcd_dc, Pin.OUT),rotation=0)
tft.offset(0, 0)
tft.init()
tft.fill(color_cfg.back)
#tft.fill_rect(0, 200, 240, 38, color_cfg.blue) #x, y, width, height, color
hardware.tft = tft
pen_color = color_cfg.background
此处用于屏幕的初始化,以及定义了一些能够用于控制显示内容的参数变量。
def outtextxy(x, y, c, pen_color):
hardware.tft.text(font, c, x, y, pen_color, color_cfg.white)
如上所述,利用了已经定义好的参数接口实现的文本UI的输出。
def arrow(x0,y0):
hardware.tft.line(x0,y0,x0+9,y0+9,color_cfg.black)
hardware.tft.line(x0,y0,x0-4,y0+12,color_cfg.black)
hardware.tft.line(x0+9,y0+9,x0+5,y0+9,color_cfg.black)
hardware.tft.line(x0-4,y0+12,x0,y0+10,color_cfg.black)
hardware.tft.line(x0,y0+10,x0+1,y0+19,color_cfg.black)
hardware.tft.line(x0+5,y0+9,x0+6,y0+18,color_cfg.black)
hardware.tft.line(x0+1,y0+19,x0+6,y0+18,color_cfg.black)
def arrowclear(x0,y0):
hardware.tft.line(x0,y0,x0+9,y0+9,color_cfg.back)
hardware.tft.line(x0,y0,x0-4,y0+12,color_cfg.back)
hardware.tft.line(x0+9,y0+9,x0+5,y0+9,color_cfg.back)
hardware.tft.line(x0-4,y0+12,x0,y0+10,color_cfg.back)
hardware.tft.line(x0,y0+10,x0+1,y0+19,color_cfg.back)
hardware.tft.line(x0+5,y0+9,x0+6,y0+18,color_cfg.back)
hardware.tft.line(x0+1,y0+19,x0+6,y0+18,color_cfg.back)
这里是实现鼠标的绘制和擦除,也是在硬件绘图的基础上进行调用的。
def lcdmouse():
global x1
global y1
x1=120
y1=120
rate=3
while True:
xValue = xAxis.read_u16()
yValue = yAxis.read_u16()
buttonvalue = buttonA.value()
if xValue<15000 and y1>=0 and y1<=240+rate:
y1 = y1-rate
elif xValue>45000 and y1>=-rate and y1<=240-rate:
y1 = y1+rate
elif yValue<15000 and x1>=0 and x1<=240+rate:
x1 = x1-rate
elif yValue>45000 and x1>=-rate and x1<=240-rate:
x1 = x1+rate
arrow(x1,y1)
utime.sleep(0.05)
arrowclear(x1,y1)
print(buttonvalue)
if buttonvalue==0 and y1<60 and y1>30:
blink(0,30,'1.change to PC')
break
elif buttonA.value()==0 and y1>60 and y1<90:
blink(0,60,'2.change rate')
rate = (rate+2)%8
这里是LCD屏幕鼠标的控制逻辑,特别附加了函数以避免鼠标跃出屏幕找不到(即到达240*240的边界后不再前进)同时对于按键和菜单的选择控制逻辑也被集成到里面了。
def pcmouse():
rate=3
while True:
xValue = xAxis.read_u16()
yValue = yAxis.read_u16()
x0=y0=0
if xValue<15000:
y0 = -rate
elif xValue>45000:
y0 = +rate
elif yValue<15000:
x0 = -rate
elif yValue>45000:
x0 = +rate
m.move(x0,y0)
buttonValueA = buttonA.value()
buttonValueB = buttonB.value()
if buttonValueB==0:
m.click(5)
elif buttonValueA==0:
m.click(6)
buttonValueA = 1
buttonValueB = 1
这是PC的鼠标控制逻辑,大同小异,不再赘述。
def blink(x0,y0,c):
pen_color = color_cfg.black
outtextxy(x0,y0,c,pen_color)
utime.sleep(0.5)
pen_color = color_cfg.background
outtextxy(x0,y0,c,pen_color)
实现按钮交互时背景色变黑的代码段,增强交互体验。
def main():
hardware.init()
menu()
xAxis = ADC(Pin(28))
yAxis = ADC(Pin(29))
lcdmouse()
pcmouse()
项目的主函数逻辑,其中menu显示了菜单各个选项。
五、实现的功能及图片展示
视频中已经予以展示,此处省略。
六、项目收获
虽然此次项目实现的只是LCD和PC的鼠标控制,但是我实际上听完了硬禾学堂的寒假直播课程,对于复古游戏的移植和实现也有了一定的了解,同时也在授课老师的引导下读完了上一次的实现贪吃蛇代码并在自己的开发板上运行实现了一下。这次项目让我从一个对于嵌入式开发板一窍不通的状态转变变成对micro python和树莓派PICO有所了解,也在一定程度上激发了我对嵌入式学习的乐趣,同时培养了自主解决问题的能力,可以说收获良多。