基于树莓派rp2040实现能控制LCD和电脑界面的“鼠标”
利用树莓派rp2040的四向摇杆和按键设计一款“鼠标”,该鼠标可以对240*240的LCD屏进行菜单选择和参数控制,同时,可以利用板子上的摇杆和按钮实现控制PC屏上的光标移动和点击操作,行使电脑鼠标的功能。
标签
嵌入式系统
MicroPython
RP2040
2022寒假在家练
xiaoyu555
更新2022-03-02
华北电力大学
1258

                             基于树莓派rp2040实现能控制LCD和电脑界面的“鼠标”

 

一.项目介绍

  • 利用rp2040的四向摇杆和按键设计一款“鼠标”,该鼠标可以对240*240的LCD屏进行菜单选择和参数控制,同时,可以利用板子上的摇杆和按钮实现控制PC屏上的光标移动和点击操作,行使电脑鼠标的功能。

二.实现的功能:

  • 绘制了一个“鼠标”图案,并且不断移动这个鼠标,当B键按下时,如果鼠标图案在相应的位置,那么会加载出新的界面,如果按下A键,就可以返回最初始的页面。
  • 当按下START按键时,会进入控制PC屏幕光标的操作,通过移动rp2040上的四向摇杆,PC屏幕光标会进行相应的移动,若按下B键,即相当于鼠标左键的功能,按下A键时即相当于鼠标右键的功能,从而实现了用rp2040来充当电脑鼠标的目的

三.设计思路:

  • 第一步绘制鼠标图案,通过st7789c的polygon函数绘画多边形从而绘制出鼠标的形状。
  • 第二步实现鼠标的移动,通过刷新鼠标的位置,在新的位置绘制另一个鼠标图案,并且在这之前先消除掉原来位置鼠标的图案,使其融为背景色,从而最终实现鼠标移动的效果。
  • 第三步实现利用“鼠标”来进行菜单选择和参数控制。通过判断鼠标图案的所属位置,并且当按钮按下时实现对菜单的选择,这时要实现只需要定义一个变量,当相应位置按下按键B时来改变变量,并且当变量变化时执行LCD界面的变换。同理,为了当A按键时按下回到起始页面,也只要当A按键按下时使变量变为初始值即可。
  • 第四步实现通过USB端口来控制PC屏幕上的光标移动和点击操作。这里需要利用到python的usb_hid库,通过该库进行人机的交互,利用里面的函数对屏幕光标进行XY轴位置的变化,和光标左右键点击的判断。当四向摇杆被操控时,利用函数来对光标操作,同理,AB键按下时也执行对应的操作,B键为鼠标左键操作,A键为鼠标右键操作。
  • 上述对LCD屏的鼠标图案和PC屏幕光标的操作完成后,实现按钮对两个模式进行转换,当START按下时用变量进行对操作模式的转换。

四.实现的过程:

  1.程序流程图:

FuzU1KqBzlEwjwLD9FeGOAIC59Hw

  2.定义引脚:

orange=0xfd20
xAxis = ADC(Pin(29))     #x轴
yAxis = ADC(Pin(28))     #Y轴
Bbutton = Pin(5,Pin.IN, Pin.PULL_UP) #B按键
Abutton = Pin(6,Pin.IN, Pin.PULL_UP) #A按键
buttonStart = Pin(7,Pin.IN, Pin.PULL_UP)#START按键

  3.利用st7789c驱动LCD屏幕,并且实例化一个对象方便后期调用:

背景色是白色

#调用ST7789C来驱动LCD屏幕
spi0=machine.SPI(0,baudrate=4000000, phase=1, polarity=1,sck=machine.Pin(game_kit.lcd_sck, machine.Pin.OUT),
                  mosi=machine.Pin(game_kit.lcd_sda, machine.Pin.OUT))
print(spi0)
display = st7789c.ST7789(spi0, 240, 240,
                        reset=machine.Pin(game_kit.lcd_rst, machine.Pin.OUT),
                        dc=machine.Pin(game_kit.lcd_dc, machine.Pin.OUT),
                        rotation=0)#实例化了一个ST7789
display.init()  #st7789初始化
display.fill(st7789c.WHITE)

  4.定义一个rpMouse类来实现对鼠标图案的初始化,鼠标位置的移动,鼠标位置的绘画,还有更新鼠标位置,使用时定义对象使用函数即可

    4.1总代码:

#设置边界
width =240
height=240
#rp2040鼠标定义
class rpMouse():
        '''
        Poly class to keep track of a polygon based sprite
        '''
        #初始化
        def __init__(
                self,
                # list (x,y) tuples of convex polygon, must be closed
                polygon,
                x=None,             # 鼠标X坐标
                y=None,             # 鼠标Y坐标
                v_x=None,           # X轴的移动速度
                v_y=None,           # Y轴的移动速度
                angle=None,         # 鼠标图案的移动速度
                max_velocity=25,    # 鼠标图案移动的最大速度
                min_velocity=10     # 鼠标图案移动的最小速度
                ):         #这边是传入的参数,对其进行一个初始化赋值

            self.polygon = polygon #赋值元组

            # 如果没有初始化起始位置便随便给个位置
            self.x = random.randint(0, width) if x is None else x
            self.y = random.randint(0, width) if y is None else y

            # 如果角度有给就设置
            self.angle = float(0) if angle is None else angle

            # 除非有给速度参数否则随便给一个
            self.velocity_x = random.uniform(
                0.50, 0.99)*6-3 + 0.75 if v_x is None else v_x
            self.velocity_y = random.uniform(
                0.50, 0.99)*6-3 + 0.75 if v_y is None else v_y

            self.max_velocity = max_velocity  #设置最大值
            self.min_velocity = min_velocity  #设置最小值
  
            #移动
        def moveX(self,moveflag):
                self.draw(st7789c.WHITE)  #使旧的鼠标图案融于背景色,达到消失的效果
                if(moveflag==1) :#向左
                    self.x -= int(self.velocity_x*0.2)  #每次以速度移动
                    if(self.x<0):     #如果到最底端,就更新位置到最上端
                        self.x=240
                elif(moveflag==2) :  #向右  
                    self.x += int(self.velocity_x*0.2)  #每次以速度移动
                    self.x %= width                 #不会超过边界
                self.draw(orange)  #使之移动
        def moveY(self,moveflag):
                self.draw(st7789c.WHITE)
                if moveflag==1:#向上
                    self.y -= int(self.velocity_y*0.2)  #每次以速度移动,乘上倍数,看起来流畅
                    if(self.y<=0):
                        self.y=240
                if moveflag==2:
                    self.y += int(self.velocity_y*0.2)  #每次以速度移动,乘上倍数,看起来流畅
                    self.y %= height
                self.draw(orange)  #使之移动
            #画鼠标
        def draw(self, color):
                display.polygon(self.polygon, self.x, self.y, color, self.angle, 0, 0)
            
            #更新鼠标的移动速度
        def update(self,vflag):  #1为加速,2为减速
            if vflag==2 :
                self.velocity_x -= 1
                self.velocity_y -= 1
            elif vflag==1:
                self.velocity_x += 1
                self.velocity_y += 1

            if self.velocity_x > self.max_velocity:        #使速度不超过最大值
                self.velocity_x = self.max_velocity
            elif self.velocity_x < self.min_velocity:
                self.velocity_x = self.min_velocity
        
#定义一个rp2040鼠标
mouse_poly=[(-7,-7),(7,0),(-7,7),(-3,0),(-7,-7)] #鼠标图像元组
mouse =rpMouse(mouse_poly,x=120,y=120,v_x=15,v_y=15,angle=180.0) #类对象
mouse.draw(orange)

    4.2初始化:

        #初始化
        def __init__(
                self,
                # list (x,y) tuples of convex polygon, must be closed
                polygon,
                x=None,             # 鼠标X坐标
                y=None,             # 鼠标Y坐标
                v_x=None,           # X轴的移动速度
                v_y=None,           # Y轴的移动速度
                angle=None,         # 鼠标图案的移动速度
                max_velocity=25,    # 鼠标图案移动的最大速度
                min_velocity=10     # 鼠标图案移动的最小速度
                ):         #这边是传入的参数,对其进行一个初始化赋值

            self.polygon = polygon #赋值元组

            # 如果没有初始化起始位置便随便给个位置
            self.x = random.randint(0, width) if x is None else x
            self.y = random.randint(0, width) if y is None else y

            # 如果角度有给就设置
            self.angle = float(0) if angle is None else angle

            # 除非有给速度参数否则随便给一个
            self.velocity_x = random.uniform(
                0.50, 0.99)*6-3 + 0.75 if v_x is None else v_x
            self.velocity_y = random.uniform(
                0.50, 0.99)*6-3 + 0.75 if v_y is None else v_y

            self.max_velocity = max_velocity  #设置最大值
            self.min_velocity = min_velocity  #设置最小值

解释:

  • polygon是绘制鼠标位置的元组,里面的元素是鼠标图案各个顶点与中心坐标(x,y)的相对位置,而且元素的顺序要按照绘画各个点的顺序来,最后一个元素要回到第一个点。
  • 定义完后,当想绘制鼠标时,要先进行初始化,将鼠标的基础信息传入,后面才得以绘制。

    4.3绘制鼠标的函数:

            #画鼠标
        def draw(self, color):
                display.polygon(self.polygon, self.x, self.y, color, self.angle, 0, 0)
            

解释:

  • 每次绘制时只需要传入想要的鼠标的颜色,然后进行绘制就行。

    4.4移动鼠标的函数:

            #移动
        def moveX(self,moveflag):
                self.draw(st7789c.WHITE)  #使旧的鼠标图案融于背景色,达到消失的效果
                if(moveflag==1) :#向左
                    self.x -= int(self.velocity_x*0.2)  #每次以速度移动
                    if(self.x<0):     #如果到最底端,就更新位置到最上端
                        self.x=240
                elif(moveflag==2) :  #向右  
                    self.x += int(self.velocity_x*0.2)  #每次以速度移动
                    self.x %= width                 #不会超过边界
                self.draw(orange)  #使之移动
        def moveY(self,moveflag):
                self.draw(st7789c.WHITE)
                if moveflag==1:#向上
                    self.y -= int(self.velocity_y*0.2)  #每次以速度移动,乘上倍数,看起来流畅
                    if(self.y<=0):
                        self.y=240
                if moveflag==2:
                    self.y += int(self.velocity_y*0.2)  #每次以速度移动,乘上倍数,看起来流畅
                    self.y %= height
                self.draw(orange)  #使之移动

解释:以在x轴移动为例,y轴是同样的道理

  • 首先先使原来的鼠标融于背景色中达到消失的效果,然后判断是要正半轴还是负半轴移动,最后更新rpMouse对象的X,Y的位置,然后在新位置绘画另一个鼠标。

  5.定时器扫描按键:

#定时器检索按键,并且给显示对应画面
def timercb(n):
    global rporcomflag  #判断是操控rp2040的鼠标还是PC屏幕的光标,0为RP2040的,1是PC屏幕光标
    global faceflag     #判断是进入初始页面还是其他页面的变量
    if(Bbutton.value()==0 and rporcomflag==1): #相当于电脑鼠标左键按下
        hid_mouse.press(hid_mouse.BUTTON_LEFT)
    if(Bbutton.value()==1 and rporcomflag==1): #相当于电脑鼠标左键松开
        hid_mouse.release(hid_mouse.BUTTON_LEFT)
    if(Abutton.value()==0 and rporcomflag==1): #相当于电脑右键按下一次
        hid_mouse.click(hid_mouse.BUTTON_RIGHT)
    if(Bbutton.value()==0 and rporcomflag==0): #如果是rp2040按下B键,为确定键
        if(mouse.y>70 and mouse.y<98 and mouse.x>=84 and mouse.x<=163): #移动到冰墩墩的位置
            faceflag=1  #冰墩墩
        elif(mouse.y>98 and mouse.y<128 and mouse.x>=84 and mouse.x<=163):
            faceflag=2  #TEXT
        elif(mouse.y>220 and mouse.y<240 and mouse.x>=110 and mouse.x<=240):
            mouse.update(1)   #更新鼠标的速度
            utime.sleep(0.05)
        elif(mouse.y>220 and mouse.y<240 and mouse.x>=0 and mouse.x<=100):
            mouse.update(2)   #更新鼠标的速度
            utime.sleep(0.05)
        
        
tim=Timer(period=100, mode=Timer.PERIODIC, callback=lambda t:timercb(1))

解释:

  • 利用定时器不断地扫描按键,减少主循环中对摇杆判断的影响。
  • 利用rporcomflag来判断是操控哪个,从而判断B,A键按下时的效果。
  • 当操控RP2040鼠标图案时,判断鼠标位置,从而当B键按下时,进入不同的页面。

  6.主循环,扫描摇杆进行对应的操作:

#定义hid鼠标
hid_mouse=Mouse()
rporcomflag=0
enterflag=0  #每次进入图像后置1,不让再次刷新图像,返回后再置0,使得下一次得以进入图像。
             #这样子可以避免影响返回键A的判断
while True:
    if (rporcomflag==1):                    #1为电脑鼠标控制
        xValue = xAxis.read_u16()    #读取摇杆位移
        yValue = yAxis.read_u16()
        if buttonStart.value()==0:   #当按下START时,使rporcomflag置0
            rporcomflag-=1
            utime.sleep(0.2)
        if  xValue <=600:            #当摇杆向左
            hid_mouse.move(-1,0)     #进行对应的位移
        elif xValue >= 60000:        #当摇杆向右
            hid_mouse.move(1,0)
        if yValue <= 600:            #当摇杆向上
            hid_mouse.move(0,-1)
        elif yValue >= 60000:        #当摇杆向下
            hid_mouse.move(0,1)
    elif rporcomflag==0 and faceflag==0:      #0为rp2040鼠标控制
        xValue = xAxis.read_u16()
        yValue = yAxis.read_u16()
        if buttonStart.value()==0:
            rporcomflag+=1
            utime.sleep(0.2)
        if xValue <= 600:#左
            mouse.moveX(1)
            utime.sleep(0.00835)
        elif xValue >= 60000:
            mouse.moveX(2)
            utime.sleep(0.0083)
        if yValue <= 600:
            mouse.moveY(1)
            utime.sleep(0.0083)
        elif yValue >= 60000:
            mouse.moveY(2)
            utime.sleep(0.0083)
        interface1()                  #每次移动后要再进行一次页面的刷新,防止鼠标图像清空时使背景色消失
        mouse.draw(orange)
    if(Abutton.value()==0 and rporcomflag==0):#返回操作
        display.fill(st7789c.WHITE)
        interface1()
        faceflag=0
        enterflag=0
    elif(faceflag==1 and enterflag==0):  # 冰墩墩界面
        display.jpg("bingdundun.jpg", 0, 0, st7789c.SLOW)
        enterflag=1   #每次进入图像后置1,不让再次刷新图像,返回后再置0,使得下一次得以进入图像。
                    #这样子可以避免影响返回键A的判断
    elif(faceflag==2 and enterflag==0):  #text界面
        display.jpg("text.jpg", 0, 0, st7789c.SLOW)
        enterflag=1

解释:

  • 利用循环不断地扫描摇杆,并且判断是操作rp2040鼠标图案还是屏幕光标,当摇杆移动时,对rp2040和屏幕光标进行移动,屏幕光标的移动只需要利用硬禾学堂官方库中的hid的move进行对鼠标的移动即可。rp2040在鼠标移动后还需要再进行背景的刷新,避免背景被鼠标刷新时局部刷新掉。

五.遇见的难题和解决方法:

 1.问题一

1.1问题:LCD屏鼠标闪烁频率过高:当鼠标进行移动时,鼠标会不停的进行闪烁,此时鼠标图案的效果太差。

1.2原因:当进行一个新位置鼠标的绘画,此时再移动时,这个新位置鼠标会被擦除,这时由于擦除旧位置鼠标和绘制新位置鼠标的间隔太短,新的鼠标显示完成后就马上被擦除,这时就会出现闪烁的现象。

1.3解决方法:在每次位移函数后,进行一个延时,从而增加鼠标的存在时长,从而减轻闪烁的问题,经试验,延时的时长在08秒到0.085秒之间效果最好。

 2:问题二

2.1问题:当进入另一个界面时会出现返回键(A键)的检索不灵敏,会出现得按好多次的问题。

2.2原因:当页面进入新的界面后,由于这时faceflag没有进行更改,所以会不断的进入这个if的分支语句里面,从而不断刷新页面,这时会对树莓派加深负担,从而影响对A按键的判断。

    if(Abutton.value()==0 and rporcomflag==0):
        display.fill(st7789c.WHITE)
        interface1()
        faceflag=0
    elif(faceflag==1):  # 冰墩墩界面
        display.jpg("bingdundun.jpg", 0, 0, st7789c.SLOW)

2.3解决方法:定义一个变量,当进入一次界面后,便将其置1,并且只有当其为0时才能再次进入这个界面,这时就能控制其进入一次,并且,当按下返回键时,就会将该变量重新置为0,变成可进入的状态,从而解决问题。

    if(Abutton.value()==0 and rporcomflag==0):
        display.fill(st7789c.WHITE)
        interface1()
        faceflag=0
        enterflag=0
    elif(faceflag==1 and enterflag==0):  # 冰墩墩界面
        display.jpg("bingdundun.jpg", 0, 0, st7789c.SLOW)
        enterflag=1   #每次进入图像后置1,不让再次刷新图像,返回后再置0,使得下一次得以进入图像。
                      #这样子可以避免影响返回键A的判断

 

六.成品展示:

1.初始界面:

Fj0yuPLSjh_9lY_jx_ZM8ZWO1Wg4

2.当位移到冰墩墩的文字并按下B键进入新页面

Fsd8c71RhHfxyradIk1i_ylUWgb0

Fua458_K5l3U_GECwGMAbR7sOYTc

3.当鼠标位移到TEXT并且按下B键后会进入TEXT说明的界面

FqXN40iybGmaqy_RrpVlW11Fkx8cFoWqpYOnoEuoXQ3118yjczzHxuVX

4.当鼠标位移到最下方数字的右边并且按下B键时,鼠标速度会对应增加。

FgcVMdkFJ_kcv8P61gexwIKX49B2

5.当鼠标位移到最下方数字的左边并且按下B键时,鼠标速度会对应减小。

FlsuXrE5RLm8MylA894B0pP0JC1g

6.当按下START按键后,进入对PC屏幕光标的操控,摇杆控制光标移动,B键充当鼠标左键功能,A键充当鼠标右键功能,并且当长按B键并且移动摇杆时,能够进行多选。

FtUXDg0kpTq7OzfNQJmaJIGtpUR0FqblA4kuSp2ek8u4FC9906YTPo5kFigxIVSvFIdS2sJurFC31DO5QM5QFiAKGSAYD4fc_JVu50Mr7pZndypa

附件下载
项目1:“鼠标”.zip
链接:https://pan.baidu.com/s/1w96ahikxAOgv7YJZrb5abQ?pwd=0000 提取码:0000
团队介绍
姓名:肖煜 学校:华北电力大学
团队成员
xiaoyu555
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号