2022年寒假在家一起练——基于树莓派RP2040制作的模拟水平仪
基于树莓派RP2040、mma7660 、 st7789制作的一种模拟水平仪
标签
ST7789
mma7660
RP2040
2022寒假在家练
虚拟水平仪
老孙头
更新2022-03-04
854

B站视频地址为https://www.bilibili.com/video/BV1sF411t7Tj?share_source=copy_web

任务要求

    完成具体项目为任务8 利用姿态传感器制作一个水平仪

  1. 通过开发板自带的mma7660收集姿态信息
  2. 通过滤波算法对收集来的姿态信息做滤波处理,消除抖动
  3. 通过ST7789主控,将收集到的姿态信息绘制到内建的240*240屏幕上

一:环境配置

   1、使用arduino IDE:

    因为我没有使用micropython的经验,而且过去用过一些arduino设备进行过小东西的制作,所以本次使用了arduino IDE作为开发环境。    

     如果是纯新手,什么也不懂的,建议使用官方推荐的开发软件,thonny,这个软件页面简洁,基础功能齐全,简单易上手,非常适合初学者(比如我)。安装的教程网上比较多,这里推荐一个硬禾的教学视频https://class.eetree.cn/live_pc/l_60fe7f4fe4b0a27d0e360f74

   2、硬禾学堂的基于树莓派RP2040的嵌入式系统学习平台:

         硬禾学堂为“2022年寒假在家一起练活动统学习平台”制作了一系列平台,我选用的平台是RP2040 Game kit。

他的原理图如下,具体可以参考https://www.eetree.cn/project/detail/698Fg6gesESvY0_ip3V2CN9ZDl4avY7

 

二:程序实现:

程序使用arduino IDE编写,其实mcropython非常好,有非常多的例程,而且还是未来的趋势,主要我时间太短了,假期天天在家带孩子,比平时上班更忙。时间实在不允许。实际上这个活动2.27结束,我是2.25才完成程序,2.26写这个和做视频。

1、模块介绍:

1、显示屏的使用:

首先需要下载st7789的库,使用的是一个成品库TFT_eSPI。这里要非常感谢杨半泛大佬,是这位大佬给提供的库,要是从github上直接搜的话,有的接口引脚定义的不太一致,是没法运行的。

使用方式如下:

      初始化配置

// 以下初始设置来自TFT_SPI的示例程序,第1、2行引入库文件,第三行是给屏幕命名叫tft

#include "SPI.h"
#include "TFT_eSPI.h"
TFT_eSPI tft = TFT_eSPI();
uint32_t targetTime = 0; //这句用来定义刷新屏幕的间隔

      屏幕填充

  tft.init();                         //ST7889屏幕初始调用
  tft.setRotation(0);                 //0是正上,1是顺时针旋转1x90度
  tft.fillScreen(TFT_WHITE);          // 设置背景为白色。或许白色看起来好看点
//tft.fillScreen( TFT_BLACK);     这里往下是一些其他可以用的颜色。
//tft.fillScreen(TFT_RED);       这里设个红色,结果背景出的是蓝色,我的天
//tft.fillScreen(TFT_GREEN);
//tft.fillScreen(TFT_BLUE);
//tft.fillScreen(TFT_BLACK);
// tft.fillScreen(TFT_GREY);
//tft.fillScreen(TFT_WHITE); //但是设的白色确实是显示的白色

     

      画线

tft.drawLine(x0, y0, x1,y1, color); 
参数:
	x0:起点横坐标
	y0:起点纵坐标
	x1:终点横坐标
	y1:终点纵坐标
	color:线的颜色
tft.drawCircle(y1, x1, 5, TFT_GREEN);//画个坐标是x1,y1的小绿圈
//上一句如果是用的tft.fillCircle,就是画个实心的球,

      写字

  int xx=1;
   tft.setTextColor(TFT_BLUE, TFT_WHITE);//括号里分别是文字颜色和文字的背景颜色
  tft.drawCentreString("X=",10,10,2);// “”里是字符串,10和10是xy的坐标,2是代表文字大小
  tft.setTextColor(TFT_BLUE, TFT_WHITE);//同上
  tft.drawFloat(xx,1,25,10,2);//跟第3行类似,这里是告诉大家怎么显示一个运算出来或是传感器发送来的数值,1代表小数点后1位。25和10代表xy坐标,2代表字高

2、MMA7660的调用

mma7660是一种比较低级的姿态传感器,精度比较差。角度信息是从-32到31,也就是只有0-64,这是只有6位吗。。。例程只有一个。相对是非常简单的。这个库可以在gitHUB搜到,是seeed做的

     初始化

#include <Wire.h>
#include "MMA7660.h"
MMA7660 accelemeter;

  int8_t x;
  int8_t y;
  int8_t z;
  float ax,ay,az;

  float xa ;
  float ya ;
/*
头三行是调用相关的库文件和使能。4567行是用来定义相关参数,xyz代表xyz轴的角度,ax ay az是xyz轴的加速度。实际后者并没有用到。最后的xa ya是用来数值变换的。

*/

    setup里这样调用

accelemeter.init();   //mma7660初始调用

     采集数据我写了两个小函数,我估计正经是可以写一个函数就可以的。初哥没办法,愚公移山吧。这两个函数分别读xy轴的角度变化,为什么读x结果用的y的数据呢。是因为屏幕方向x的方向对应着传感器的y。所以就这样了。


//读取x坐标
int GetX(){
  accelemeter.getXYZ(&x,&y,&z); //利用mma获取小珠的xyz变化
    return y;
  }
//读取y坐标
int GetY(){
  accelemeter.getXYZ(&x,&y,&z); //利用mma获取小珠的xyz变化
    return x;
  }

 

4、具体显示

     显示内容是这样的,首先擦除小球过去位置的图像,然后画小球现在位置的图像,最后画标尺线,

//这段内容是在loop里的, 画框线和写入xy值,用了两个小函数,让程序可编辑性或是代用性更强一点。
//我怀疑画小球可能也可以用一个小函数,时间有点紧,脑袋有点笨,只能直接放进loop了
if (targetTime <= millis()) {
    targetTime = millis() + 15; // Update emter every 35 milliseconds 每35ms刷新一次 
//现在要开始画小圈了,先画个白色的,和背景颜色一样。相当于恢复背景颜色
 tft.fillCircle(FilterValueXB, FilterValueYB, 10, TFT_WHITE); //画个中间位置的圈
tft.drawCircle(FilterValueXB, 231, 5, TFT_WHITE); //画个水平位置的圈
 tft.drawCircle(231, FilterValueYB, 5, TFT_WHITE); //画个垂直位置的圈
FilterValueYB = FilterValueY;//用新的y参数带入y参数,x也是一样原理
FilterValueXB = FilterValueX;
//下面3行是把新的参数带入进去,这回得画带颜色的了
 tft.fillCircle(FilterValueXB, FilterValueYB, 10, TFT_GREEN); //画个中间位置的圈
tft.drawCircle(FilterValueXB, 231, 5, TFT_BLACK); //画个水平位置的圈
 tft.drawCircle(231, FilterValueYB, 5, TFT_BLACK); //画个垂直位置的圈



drawStaff(); //画框线
drawTxt();  //写入xy的值

画框线和写如xy值的小函数

void drawStaff() //弄一个画标尺的小函数,包括大框中间的十字标尺和水平,垂直标尺
{
tft.drawLine(221, 0, 221, 221, TFT_GREEN); 
tft.drawLine(0, 221, 221, 221, TFT_GREEN); 
for(int i=-12; i<13; i+=1)
{
tft.drawLine(110+i*8, 106, 110+i*8, 114, TFT_BLUE);//中间大框的横线
tft.drawLine(110+i*8, 222, 110+i*8, 225, TFT_BLUE);//下面水平条的横线1
tft.drawLine(110+i*8, 237, 110+i*8, 240, TFT_BLUE);//下面水平条的横线2

tft.drawLine( 106, 110+i*8, 114, 110+i*8, TFT_BLUE);//中间大框的竖线
tft.drawLine(222, 110+i*8, 225, 110+i*8, TFT_BLUE);//下面水平条的竖线1
tft.drawLine(237, 110+i*8, 240, 110+i*8, TFT_BLUE);//下面水平条的竖线2
}
for(int i=0; i<13; i+=4)
{
tft.drawCircle(110, 110, i*8, TFT_RED); //画个垂直位置的圈

}

}


void drawTxt()
{
  int xx=map(xa,32, -31,-90,90);
  int yy=map(ya,32, -31,-90,90);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawCentreString("X=",10,10,2);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawFloat(abs(xx),1,25,10,2);
  
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawCentreString("Y=",10,30,2);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawFloat(abs(yy),1,25,30,2);
  
  }

5、滤波算法

      这个mma7660,至少我这个真的是抖的太厉害了。从网上找的滤波算法,一共10种,第十一种卡曼滤波我没试,其他都试了一遍,按有关资料来说,用32个平均值来滤波就行了,实际效果不好,还是抖的很厉害。

    算法抄自极客工坊  http://www.geek-workshop.com/thread-7694-1-1.ht

   我就不转载了,这里把我修改后使用的分享一下,因为我比较笨,还是xy轴各用一次滤波算法,而不是只用一个函数,分别算两次。

//算术平均滤波法
#define FILTER_N 30 //理论上越大越平滑,但是太大会卡,因为mcu算力不够。。。
float FilterX() {
  int i;
  int filter_sum = 0;
  for(i = 0; i < FILTER_N; i++) {
    filter_sum += GetX();
    delay(5);//原版是1
  }
  return (float)(filter_sum / FILTER_N);
}


float FilterY() {
  int i;
  int filter_sum = 0;
  for(i = 0; i < FILTER_N; i++) {
    filter_sum += GetY();
    delay(1);
  }
  return (float)(filter_sum / FILTER_N);
}

6、整体实现

                                     流程图如下

FkxIGGBw4XCYCpzXNFa9_UC93pFZ

      这一部分我将把全部代码写出来并用注释的方式来介绍代码的实现。里面定义成float的,完全都应该是定义成int的,只不过我想定义成float是不是能平滑点呢,似乎是效果有限。毕竟传感器来的数据只有6位。

Fj95kutWuN-fgDS1Iz-PqiO6ZwWS

/*
 * 以下初始设置来自mma7660fc的示例程序,前3行是引入库文件,后面4行是引入小珠坐标的定义字,其实就x和y有用
 */

#include <Wire.h>
#include "MMA7660.h"
MMA7660 accelemeter;

  int8_t x;
  int8_t y;
  int8_t z;
  float ax,ay,az;

  float xa ;
  float ya ;
/*
 * 以下初始设置来自TFT_SPI的示例程序,第1、2行引入库文件,第三行是给屏幕命名叫tft
 */

#include "SPI.h"
#include "TFT_eSPI.h"
TFT_eSPI tft = TFT_eSPI();

uint32_t targetTime = 0; //这句用来刷新屏幕的

//定义滤波前后的x值 y值
float FilterValueX;//滤波后的x
float ValueX;
float ValueXB;
float FilterValueY;
float ValueY;
float ValueYB;
int Value=0;  //这个可能是没啥用,本来是用在最后滤波的,我怀疑全用一个value是不行的,所以后面滤波那里又把vlaue分别携程valuex和valuey了

//定义时刻2的x值和y值,可能直接用滤波后的当时间2的值也行,回头要测试以下
int FilterValueXB;//x滤波后的值2,也就是值B
int FilterValueYB;





void setup()
{
accelemeter.init();   //mma7660初始调用
Serial.begin(9600);

  /*
   * 下面是设置屏幕的部分,先画出一部分来,省的完全都是用loop画,节省一些资源
   */
  
  tft.init();                         //ST7889屏幕初始调用
  tft.setRotation(0);                 //0是正上,1是顺时针旋转1x90度
  tft.fillScreen(TFT_WHITE);          // 设置背景为白色。或许白色看起来好看点
//tft.fillScreen( TFT_BLACK);       一些其他可以用的颜色。
//tft.fillScreen(TFT_RED);       这里设个红色,结果背景出的是蓝色,我的天
//tft.fillScreen(TFT_GREEN);
//tft.fillScreen(TFT_BLUE);
//tft.fillScreen(TFT_BLACK);
// tft.fillScreen(TFT_GREY);
//tft.fillScreen(TFT_WHITE); //但是设的白色确实是显示的白色
  
  targetTime = millis(); // Next update time 设置刷新用的,要不这东西不刷新  
}


//---------------
void drawStaff() //弄一个画标尺的小函数,包括大框中间的十字标尺和水平,垂直标尺
{
tft.drawLine(221, 0, 221, 221, TFT_GREEN); 
tft.drawLine(0, 221, 221, 221, TFT_GREEN); 
for(int i=-12; i<13; i+=1)
{
tft.drawLine(110+i*8, 106, 110+i*8, 114, TFT_BLUE);//中间大框的横线
tft.drawLine(110+i*8, 222, 110+i*8, 225, TFT_BLUE);//下面水平条的横线1
tft.drawLine(110+i*8, 237, 110+i*8, 240, TFT_BLUE);//下面水平条的横线2

tft.drawLine( 106, 110+i*8, 114, 110+i*8, TFT_BLUE);//中间大框的竖线
tft.drawLine(222, 110+i*8, 225, 110+i*8, TFT_BLUE);//下面水平条的竖线1
tft.drawLine(237, 110+i*8, 240, 110+i*8, TFT_BLUE);//下面水平条的竖线2
}
for(int i=0; i<13; i+=4)
{
tft.drawCircle(110, 110, i*8, TFT_RED); //画个垂直位置的圈

}

}
//----------------



//----------------
/*
void xyzBall() //弄一个找珠子位置的函数

tft.setTextColor(TFT_BLACK);  // Text colour 定义了文字颜色 这有用吗?
tft.drawCircle(y1, x1, 5, TFT_GREEN);//画个坐标是x1,y1的小绿球 


}
*/

void drawTxt()
{
  int xx=map(xa,32, -31,-90,90);
  int yy=map(ya,32, -31,-90,90);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawCentreString("X=",10,10,2);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawFloat(abs(xx),1,25,10,2);
  
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawCentreString("Y=",10,30,2);
  tft.setTextColor(TFT_BLUE, TFT_WHITE);
  tft.drawFloat(abs(yy),1,25,30,2);
  
  }


void loop() {
//下面一段是数值变换一下。把数值变换成lcd屏的坐标
xa=FilterX();
FilterValueX =map(xa,32/3, -31/3,0,220) ;  //满量程32到-31 
 ValueX = FilterValueX; 

ya=FilterY();
FilterValueY =map(ya,-32/3, 31/3,0,220) ;  //满量程32到-31 
ValueY = FilterValueY; 



if (targetTime <= millis()) {
    targetTime = millis() + 15; // Update emter every 35 milliseconds 每35ms刷新一次 
//现在要开始画小圈了,先画个白色的,和背景颜色一样。相当于恢复背景颜色
 tft.fillCircle(FilterValueXB, FilterValueYB, 10, TFT_WHITE); //画个中间位置的圈
tft.drawCircle(FilterValueXB, 231, 5, TFT_WHITE); //画个水平位置的圈
 tft.drawCircle(231, FilterValueYB, 5, TFT_WHITE); //画个垂直位置的圈
FilterValueYB = FilterValueY;//用新的y参数带入y参数,x也是一样原理
FilterValueXB = FilterValueX;
//下面3行是把新的参数带入进去,这回得画带颜色的了
 tft.fillCircle(FilterValueXB, FilterValueYB, 10, TFT_GREEN); //画个中间位置的圈
tft.drawCircle(FilterValueXB, 231, 5, TFT_BLACK); //画个水平位置的圈
 tft.drawCircle(231, FilterValueYB, 5, TFT_BLACK); //画个垂直位置的圈



drawStaff(); //画框线
drawTxt();  //写入xy的值
  }
}


//读取x坐标
int GetX(){
  accelemeter.getXYZ(&x,&y,&z); //利用mma获取小珠的xyz变化
    return y;
  }
//读取y坐标
int GetY(){
  accelemeter.getXYZ(&x,&y,&z); //利用mma获取小珠的xyz变化
    return x;
  }





//算术平均滤波法
#define FILTER_N 30 //不是越大越好,太大会卡
float FilterX() {
  int i;
  int filter_sum = 0;
  for(i = 0; i < FILTER_N; i++) {
    filter_sum += GetX();
    delay(5);//原版是1
  }
  return (float)(filter_sum / FILTER_N);
}


float FilterY() {
  int i;
  int filter_sum = 0;
  for(i = 0; i < FILTER_N; i++) {
    filter_sum += GetY();
    delay(1);
  }
  return (float)(filter_sum / FILTER_N);
}

 

三:后记

除了选择用arduino IED开发外,最好还是用tonny开发,硬禾官方也提供了非常好的例程和相关的课程。。。。可是,我对python实在是一窍不通,实在是可惜了那么好的例程。。主要是用那个还能移植小游戏玩。还可以选择使用VSCODE开发。官方为VSCODE写好了一个扩展插件Pico-Go。 

我觉得不像我有arduino包袱的完全可以从tonny入手,路要比用arduino宽广的多,也能走的更远。毕竟arduino更像是个玩具。

还有RP2040 game kit的硬件。实际我只是用了非常少的一部分功能,它已经留好了I2C SPI口。5个数字io口和2个adc口。还有红外线发射和接受原件。4个可定义按键和一个xy拨盘,真的能用来做很多东西,没准也可以发展成硬禾的wio terminal。 但是这个硬件还需要进一步打磨。要它的稳定性和一致性再好一些。比如这个姿态传感器,至少我的这个,抖的实在是太厉害了,又比如小行星dx发的游戏机固件,我的game kit刷完了就根本没有任何反应。而我这个硬件应该是好的,毕竟刷官方的演示小程序,小球是可以乱飞的。我怀疑还是我这个体质要差一点的原因。也不排除是我在拧外壳的时候,力量没掌握好,导致电路板上有应力,造成哪里损坏了。

 

 

附件下载
2022-1-eetree1-sunT2.21_n3f_.ino.ino
水平珠项目全部源码
相关配套文件.rar
显示屏的库文件,滤波算法文件
Accelerometer_MMA7660.rar
mma7660的库文件
团队介绍
自己一个人
团队成员
老孙头
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号