内容介绍
开发板介绍
本次使用的开发板是树莓派新推出的RP2040 pico 开发板,核心具有双核Arm Cortex M+内核,工作频率可高达133MHz,具有264KB的内存,和高度灵活的可编程IO可用于高速数字接口。
功能实现
项目8 - 利用姿态传感器制作一个水平仪
在LCD屏上设计一款水平仪,通过一个滚动的小球或气泡,来显示当前板子的倾斜度,当板子处于水平位置的时候,小球停在屏幕的正中间,倾斜板子,小球偏移,并能够显示偏移的角度(二维信息)
按键操作
初始状态是水平仪的页面,按下按键B会进入结束页面,页面显示“End !”。再按下A键回到水平仪界面。
代码实现框图
设置
本次使用arduino平台开发树莓派pico,使用万能的编辑器vscode,在板卡管理器中输入pico,选择Raspberry Pi Pico (Arduino Mbed OS RP2040) Boards 选项。
注意
板卡上MMA7660传感器的I2C总线引脚和arduino的rp2040 pico的默认引脚不一致,需稍加改动,操作如下。
由于板卡上MMA7660连接的SDA、SCL分别是10、11脚,
故应转到路径
C:\Users\NSwor\AppData\Local\Arduino15\packages\arduino\hardware\mbed_rp2040\2.6.1\variants\RASPBERRY_PI_PICO
下的 pins_arduino.h 文件修改对应引脚。SDA-10 SCL-11
烧录
按下B键再按下reset键,再松开reset键和B键,进入boot模式,将uf2文件拖入树莓派U盘中,即可执行。
代码分析
在setup()函数中进行串口初始化,MMA7660重力传感器芯片初始化,初始化按键引脚(设置上拉电阻输入),并定义一些Sprite初始化函数。
Serial.begin(250000);
accelemeter.init();
// 初始化按键引脚
pinMode(button_A,INPUT_PULLUP);
pinMode(button_B,INPUT_PULLUP);
tft_init();
spr1_init();
spr_init();
spr2_init();
spr3_init();
板载屏幕为中景园的ST7789屏幕,使用TFT_eSPI库来驱动,由于使用C语言更容易获得更快的刷屏速度,所以我放弃了使用python开发,转为arduino开发。我上传的TFT_eSPI库文件中已经集成好了ST7789屏幕对应的驱动,需要在 User_Setup.h 文件和 User_Setup_Select.h 文件中手动打开 #define ST7789_DRIVER 和 #include <User_Setups/Setup_RP2040_ST7789.h> 注释,注释掉其他型号驱动。
在TFT_eSPI库中,如果使用全屏刷屏的方式还是会比较慢,但是如果使用Sprite方式刷屏,则会流畅很多。具体操作为,先定义一个TFT_eSPI类型的变量tft,然后再定义几个TFT_eSprite类型的变量用来访问tft。
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
TFT_eSprite spr1 = TFT_eSprite(&tft);
TFT_eSprite spr2 = TFT_eSprite(&tft);
TFT_eSprite spr3 = TFT_eSprite(&tft);
使用Sprite移动的原理是,将多个Sprite区域中的内容push到屏幕上,然后移动Sprite区域,或是定义一个全屏的大Sprite,其他内容用新的小Sprite push到大Sprite上,最后将大Sprite push到屏幕上。缺点是消耗内存比较大,当使用的Sprite很多或很大时,可能造成内存不足。
spr.pushToSprite(&spr1, x, y, TFT_BLACK);
spr2.pushToSprite(&spr1, x2+10, 200, TFT_BLACK);
spr3.pushToSprite(&spr1, 200, y2+10, TFT_BLACK);
spr1.pushSprite(0, 0, TFT_TRANSPARENT);
为了使水平仪显示获得更好的流畅度,可以从MMA7660采样数据入手,这里采用一阶滞后滤波对x,y,z三轴数据进行滤波,增加画面稳定性。
// 一阶滞后滤波
x0=0.8*x0+0.2*x1;x1=x0;
y0=0.8*y0+0.2*y1;y1=y0;
z0=0.8*z0+0.2*z1;y1=z0;
气泡小球不能跑出屏幕范围,因此要对小球的运动范围做出限制。这里使用圆的定义(如下)来限定范围。
// 到定点距离为定长的点的轨迹
if(((85-x+dy)*(85-x+dy)+(y+dx-85)*(y+dx-85))<8000){
x=x-dy;
y=y+dx;
x2=x;
y2=y;
}
if(((85-x)*(85-x)+(y-85)*(y-85))<8000){
spr.pushToSprite(&spr1, x, y, TFT_BLACK);
spr2.pushToSprite(&spr1, x2+10, 200, TFT_BLACK);
spr3.pushToSprite(&spr1, 200, y2+10, TFT_BLACK);
spr1.pushSprite(0, 0, TFT_TRANSPARENT);
}
定义整型变量flag来标记显示的内容。flag默认为0,当按键A按下时flag = 0,显示水平仪界面;当按键B按下时flag = 1,显示结束页面,水平仪结束。在loop循环中检测按键状态。
if(digitalRead(button_A) == 0){
flag = 0;
}
if(digitalRead(button_B) == 0){
flag = 1;
tft.fillScreen(TFT_WHITE);
}
显示角度。在屏幕顶端显示三轴方向的角度。
spr1.drawRoundRect(0,0,80,30,6,TFT_WHITE);
spr1.drawFloat((x0/22.0)*90.0, 2, 70, 17, 4);
spr1.drawRoundRect(80,0,80,30,6,TFT_WHITE);
spr1.drawFloat((y0/23.0)*90.0, 2, 150, 17, 4);
spr1.drawRoundRect(160,0,80,30,6,TFT_WHITE);
spr1.drawFloat((z0/22.0)*90.0, 2, 230, 17, 4);
结果展示
附:
关于这个板卡,我还实现了其他功能。
项目1 - 能控制LCD和电脑界面的“鼠标”
使用Thonny IDE进行micropython开发。ADC读取四向摇杆的值,判断值在不同的区间定义鼠标移动的不同速度,提升鼠标控制的流畅度。
# 定义光标移动函数
def ymove(speed):
global x, y
if speed > 60000:# down
m.move(0, 10)
elif (speed > 40000)&(speed < 60000):
m.move(0,6)
elif (speed > 34000)&(speed < 40000):
m.move(0, 2)
elif (speed > 20000)&(speed < 29000):# up
m.move(0,-2)
elif (speed > 3000)&(speed < 20000):
m.move(0, -6)
elif (speed > 0)&(speed < 3000):
m.move(0,-10)
点击与长按,我想到了一个算法可以把二者完美结合起来。
def m_pressLEFT():
m.press(m.BUTTON_LEFT)
while True:
if (xAxis.read_u16() >= 0) & (xAxis.read_u16() <= 68000):
ymove(xAxis.read_u16())
if (yAxis.read_u16() >= 0) & (yAxis.read_u16() <= 68000):
xmove(yAxis.read_u16())
if buttonB.value() == 1:
m.release(m.BUTTON_LEFT)
break
def m_pressRIGHT():
m.press(m.BUTTON_RIGHT)
while True:
if (xAxis.read_u16() >= 0) & (xAxis.read_u16() <= 68000):
ymove(xAxis.read_u16())
if (yAxis.read_u16() >= 0) & (yAxis.read_u16() <= 68000):
xmove(yAxis.read_u16())
if buttonA.value() == 1:
m.release(m.BUTTON_RIGHT)
break
使用micropython的多线程时,我遇到了内存问题和屏幕调用冲突,在老师的指导下,我学会了可以使用内存垃圾收集、锁定线程内存和使用互斥锁防冲突等方法实现。
gc.threshold(20_000)
gc.enable()
lock = _thread.allocate_lock()
_thread.stack_size(20 * 1024)
def blit_buffer(a, b, c, d, e):
lock.acquire()
display.blit_buffer(a, b, c, d, e)
lock.release()
gc.collect()
另外我也在尝试做复古游戏移植,实现一款90版的坦克大战,由于学业繁忙,暂时只完成了一半,后续会跟进这个项目。