基于树莓派RP2040设计模拟鼠标功能
本项目使用树莓派RP2040板卡实现模拟鼠标功能,并实现在PC端和RP2040端的任意切换,以及参数调整。并且加入点击动态反馈效果和模式选择状态反馈效果。
标签
显示
USB
2022寒假在家练
树莓派RP2040
LIUZHIQIANG123
更新2022-05-16
天津市职业大学
1143

内容介绍

本项目使用由硬禾学堂针对“2022年寒假在家一起练”开发的“基于树莓派RP2040的嵌入式系统学习平台”,通过该平台上的ST7789屏幕以及摇杆和按键设计一款模拟鼠标的功能,并且带有人机交互页面以及参数选择功能。通过Thonny(使用MicroPython)进行编写。

1、图标绘制

本项目使用由硬禾学堂官方提供的固件库‘BreakoutColourLCD240x240库‘进行绘图,所绘图标包括“鼠标箭头”、“功能按键图标”、“灵敏度数值”、“模式选择图标”、“按键说明”,其中“功能按键图标”和“模式选择图标”分别包括“点击”、“未点击”状态和当前模式状态,从而以实现动态效果,可以给用户反馈按键状态,并且以函数形式封装,方便程序实时调用。

2、摇杆和按键

摇杆使用RP2040的外部ADC引脚pin(28)、pin(29),并将x、y轴分别转换为0-65535的数值,本项目中,首先将ADC转换后的数值进行取整:摇杆居中时,x、y轴数值都为3,(本项目未将数值转为向量代表,而是直接使用数值代表摇杆的方向)x>3时为下移,x<3时为上移,y>3时为右移,y<3时为左移。在RP模式下通过countY、countX参数加减sensitivity来实现鼠标箭头的移动及灵敏度快慢(countY、countX为鼠标箭头的位置坐标,初始为0,sensitivity为灵敏度参数),箭头刷新方面首先判断摇杆是否移动,只有摇杆移动才会对应刷新箭头位置,摇杆没有移动则表示页面为静止状态,则无需刷新。在PC模式下,同样通过countY、countX以及sensitivity_PC灵敏度参数控制鼠标箭头的移动,同时增加摇杆轻推和重推不同状态,轻推鼠标缓慢移动,重推鼠标快速移动,可以应对不同场景的需求。(详细代码请参考附件)

RP模式下程序

'鼠标箭头显示'
def arrow_on(x,y):
    
'鼠标箭头清除'
def arrow_off(x,y):

'初始化设置'
sensitivity=5  #RP2040灵敏度设置
sensitivity_PC=2 #PC灵敏度
countX=0
countY=0

while True:
     xValue = xAxis.read_u16()
     yValue = yAxis.read_u16()
     X=xValue//10000  #取万位值
     Y=yValue//10000
     if(X!=3 or Y!=3):
         arrow_off(countY,countX)
         if(X>3):
             countX=countX+1*sensitivity   #4-6时,方向为上移,X坐标每次+2
         if(X<3):
             countX=countX-1*sensitivity   #0-2时,方向为下移,X坐标每次-2
         if(Y>3):
             countY=countY+1*sensitivity   #4-6时,方向为左移,Y坐标每次+2
         if(Y<3):
             countY=countY-1*sensitivity   #0-2时,方向为右移,Y坐标每次+2
     #设置坐标边界
         if(countX>=235):
             countX=235
         if(countY>=235):
             countY=235
         if(countX<=0):
             countX=0
         if(countY<=0):
             countY=0
     arrow_on(countY,countX)

PC模式下程序:

if(X==3 and Y==3):
            countX=0
            countY=0 
        if(X==4 or X==5):
            countX=1*sensitivity_PC
        if(X==6):
            countX=3*sensitivity_PC
        if(X==2 or X==1):
            countX=-1*sensitivity_PC
        if(X==0):
            countX=-3*sensitivity_PC   

        if(Y==4 or Y==5):
            countY=1*sensitivity_PC
        if(Y==6):
            countY=3*sensitivity_PC
        if(Y==2 or Y==1):
            countY=-1*sensitivity_PC
        if(Y==0):
            countY=-3*sensitivity_PC
        m.move(countY+countY,countX+countX)   #输出箭头位置,相对位置

按键为读取引脚电平的方式,根据原理图可以看出按键未按下为高电平状态,所以判断按键点位为低电平时则为按下。RP模式下,按键定义了B键为‘确认’,A键为‘重置’。PC模式下,按键定义了B键为‘鼠标左键’,A键为‘鼠标右键’,SELECT键为‘PC转RP模式按键’。通过箭头在指定的功能图标区按下,来达到指定的功能效果。

buttonB = Pin(5,Pin.IN, Pin.PULL_UP) #B
buttonA = Pin(6,Pin.IN, Pin.PULL_UP) #A
buttonSelect = Pin(8,Pin.IN, Pin.PULL_UP)#
while True:
    'SELECT键'
    buttonValueSelect = buttonSelect.value()
    if(buttonValueSelect!=1):
            while(buttonValueSelect!=1):
                RP_model_box_off1()
                buttonValueSelect = buttonSelect.value()
                arrow_on(countY,countX)
                if(buttonValueSelect==1):
                    arrow_off(countY,countX)
                    model=RP2040
                    break
 '判断当前模式'
    if(model==RP2040):
    buttonValueA = buttonA.value()
    buttonValueB = buttonB.value()
    if(buttonValueB!=1):
         #RP2040端参数设置
         #右侧
            if(185<=countY<=210 and 190<=countX<=215):
                while(buttonValueB!=1):
                    RP_box1_r()
                    buttonValueB=buttonB.value()
                    arrow_on(countY,countX)
                    if(buttonValueB==1):
                        arrow_off(countY,countX) 
                        display.set_pen(0,0,0)#设置黑色
                        display.rectangle(123, 190 ,50 ,50) 
                        sensitivity+=1
                        break
            #左侧
            if(45<=countY<=70 and 190<=countX<=215):
                while(buttonValueB!=1):
                    RP_box1_l()
                    buttonValueB=buttonB.value()
                    arrow_on(countY,countX)
                    if(buttonValueB==1):
                        arrow_off(countY,countX)
                        display.set_pen(0,0,0)#设置黑色
                        display.rectangle(123, 190 ,50  ,50) 
                        sensitivity-=1
                        if(sensitivity<=1):
                            sensitivity=1
                        break
                    
            #PC端参数设置
            #右侧
            if(185<=countY<=210 and 190-75<=countX<=215-75):
                while(buttonValueB!=1):
                    PC_box1_r()
                    buttonValueB=buttonB.value()
                    arrow_on(countY,countX)
                    if(buttonValueB==1):
                        arrow_off(countY,countX) 
                        display.set_pen(0,0,0)#设置黑色
                        display.rectangle(123, 190-75 ,50 ,50) 
                        sensitivity_PC+=1
                        break
            #左侧
            if(45<=countY<=70 and 190-75<=countX<=215-75):
                while(buttonValueB!=1):
                    PC_box1_l()
                    buttonValueB=buttonB.value()
                    arrow_on(countY,countX)
                    if(buttonValueB==1):
                        arrow_off(countY,countX)
                        display.set_pen(0,0,0)#设置黑色
                        display.rectangle(123, 190-75 ,50  ,50) 
                        sensitivity_PC-=1
                        if(sensitivity_PC<=1):
                            sensitivity_PC=1
                        break
            #PC_model   
            if(15<=countY<=70 and 10<=countX<=40):
                while(buttonValueB!=1):
                    PC_model_box_off1()
                    buttonValueB=buttonB.value()
                    arrow_on(countY,countX)
                    if(buttonValueB==1):
                        arrow_off(countY,countX)
                        PC_model_box_on()
                        RP_model_box_off2()
                        countX=0
                        countY=0
                        model=PC
                        break
        'A键'
        if(buttonValueA!=1):
            while(buttonValueA!=1):
                buttonValueA=buttonA.value()
                if(buttonValueA==1):
                    display.set_pen(0,0,0)#设置黑色
                    display.rectangle(123, 190-75 ,50  ,50)
                    display.rectangle(123, 190 ,50 ,50) 
                    sensitivity=5  #RP2040灵敏度设置
                    sensitivity_PC=2 #PC灵敏度
                    break
 'PC模式'
    if(model==PC):
        buttonValueA = buttonA.value()
        buttonValueB = buttonB.value()
 #'''左键定义'''
        if(buttonValueB!=1):
            m.press(True)  #按下     
        if(buttonValueB==1):
            m.release(True)  #释放
    #'''右键定义'''
        if(buttonValueA!=1):
            m.press(0x02)  #按下
        if(buttonValueA==1):
            m.release(0x02)  #释放

3、设计流程

首先,单独建立函数绘制所需要的所有图标,包括箭头图标,不同状态的功能按键图标,方便后续程序直接调用,将所有绘制的初始状态图标放在初始化程序中。其次是图标的刷新,在箭头移动过程中只能固定刷新黑色或者其他一种颜色背景,因此箭头移动会导致途中其他图标会被覆盖,所以当箭头移动或者是按键按下后都需要刷新图标。在主函数中,程序会判断当前模式状态(PC\RP),然后对应执行不同模式下的功能,检测摇杆或按键是否移动或按下,若没有,则页面不刷新,可以大幅减少刷新频率。

Fns7EXF0fvnjMmsJdpCRVaHEBGK6

4、菜单界面

RP240屏幕上呈现有完整功能的菜单界面,包括屏幕左上方的‘模式选择菜单’、右侧的‘按键功能介绍’以及下方的‘PC模式下鼠标灵敏度参数设置’和‘RP模式下箭头灵敏度参数设置’。

FtSnft92sA2D4hNIzTJ-3xDVTMVQ

其中所有可点击的功能图标都带有点击反馈动态效果,可以反馈给用户是否按下。

5、遇到的问题及后续改进

本项目虽然简单易实现,但是想要做到非常流畅的刷新,还是需要一定的努力。目前本项目所遇到的一个问题是在RP模式下,箭头移动是刷新较慢,导致在不为黑色背景下出现残影,在寻找问题后发现是由于在制作初期,没有使用缓冲区画图的方式,所有图标直接绘制到屏幕上,导致绘图量大,刷新不及时出现的问题,还有就是无法直接获取指定坐标像素的颜色,导致箭头刷新只能使用黑色或其他一种颜色,无法动态刷新对应背景的颜色。针对以上发现问题,本人在努力改进中,完成后会及时更新改进后的项目。

附件下载
rp2040project.zip
My_Mouse为主程序,hid为所用到的库,uf2为所用固件,在烧录程序前务必使用压缩包里的uf2固件,因为有一个库uf2固件里内置
团队介绍
由本人独立完成
团队成员
刘志强
天津职业大学
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号