内容介绍
本项目使用由硬禾学堂针对“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),然后对应执行不同模式下的功能,检测摇杆或按键是否移动或按下,若没有,则页面不刷新,可以大幅减少刷新频率。
4、菜单界面
RP240屏幕上呈现有完整功能的菜单界面,包括屏幕左上方的‘模式选择菜单’、右侧的‘按键功能介绍’以及下方的‘PC模式下鼠标灵敏度参数设置’和‘RP模式下箭头灵敏度参数设置’。
其中所有可点击的功能图标都带有点击反馈动态效果,可以反馈给用户是否按下。
5、遇到的问题及后续改进
本项目虽然简单易实现,但是想要做到非常流畅的刷新,还是需要一定的努力。目前本项目所遇到的一个问题是在RP模式下,箭头移动是刷新较慢,导致在不为黑色背景下出现残影,在寻找问题后发现是由于在制作初期,没有使用缓冲区画图的方式,所有图标直接绘制到屏幕上,导致绘图量大,刷新不及时出现的问题,还有就是无法直接获取指定坐标像素的颜色,导致箭头刷新只能使用黑色或其他一种颜色,无法动态刷新对应背景的颜色。针对以上发现问题,本人在努力改进中,完成后会及时更新改进后的项目。