单片机 GUI 设计(十六) 您所在的位置:网站首页 仪表盘出现efc 单片机 GUI 设计(十六)

单片机 GUI 设计(十六)

2024-06-02 07:36| 来源: 网络整理| 查看: 265

基于 GD32F303ZET6 苹果派开发板

简介

仪表盘,广泛应用于汽车显示速度,也可以用来做时钟面板、温湿度计面板等等,应用十分广泛,在生活中随处可见。汽车的仪表盘如下所示。(本想做一段生动的描述,奈何文化水平不够)

仪表盘驱动难点在于指针的绘制,好多同学不知道如何让指针转起来。想要显示指针并不难,难就难在如何让指针消失,总不至于画着画着,屏幕上满是指针吧,要遵循只有一个指针原则。本文中将介绍四种可以让指针转起来的办法。

方法一:暴力刷图

刷图是实现仪表盘最简单、最暴力的方法。如果你的仪表盘有一百个分度格,那么就直接准备一百张指针角度不通的图片,需要显示哪个角度时,直接显示对应的图片即可。简单、实用,就是不怎么方便,因为要准备好多张图片,想想就心累。

方法二:刷整屏

首先要找到一张干净的仪表盘图片,必须是不带指针的那种,如下所示。当然,不一定非得要整张图片,读者也可以用画弧线的方式,手绘出一个不带指针的仪表盘图案。就我个人而言,更倾向于使用图片,因为简单方便,刷图就完事了。

使用刷整屏的方式实现指针转动原理很简单,就是每次绘制指针之前,用一张带仪表盘的图片刷新整个屏幕,然后再将指针描上去。刷屏的过程中会很自然的将上一个指针抹去,重新绘制指针后,仪表盘上就只剩下一条指针了。

这个方法适用于 GD32F470、STM32F429 等带硬件刷图机制的处理器。这些处理器刷图的速度很快,而且通常会带有很大的外拓 SDRAM,一般是 32M,比单片机内部 Flash 还要大得多。使用时可以提前将整张背景图片从文件系统导入到内存,即 SDRAM 中,刷图时直接用硬件加速机制,将整张图片填充到屏幕显示。硬件刷图通常很快,肉眼几乎看不到刷图过程。

对于使用了 MCU 屏的苹果来,因为 GD32F303 不支持硬件刷图,而且外拓的 SRAM 只有 1M,所有拿图片刷整屏会比较慢。一般都是用纯色刷整屏,然后再用画弧线函数,描绘出仪表盘,这样速度会快一些。当然,读者也可以将图片显示范围缩小,刷整屏时用纯色,然后再将仪表盘图片显示到对应位置即可,这样也可以得到一个不错的刷新速度,就是仪表盘图片别太大,会很慢。。。。

对于指针,前期我们可以简单的用一条直线代替,如此一来,绘制指针时,只需要调用画线函数即可。

使用刷整屏实现指针转动的代码如下所示。指针的起点,也就是仪表盘中心的位置比较难确定,一般我是通过打点函数,尝试多次后才能得到理想位置。知道了起点,确定了指针的长度之后,需要用量角器测量速度为 0 r/min 和 1200 r/min 两个转速对应的角度,然后将传进来的速度值转换成百分比,紧接着就可以用三角函数计算指针的终点了。得到了指针的起点和终点之后,直接用画线函数将两个坐标点连起来即可。

#include "./LCD/LCD.h" #include "./Picture/BMP.h" #include //指针相关信息 #define POINTER_X0 (400) #define POINTER_Y0 (260) #define POINTER_LEN (200) #define POINTER_COLOR (LCD_COLOR_RED) //绘制背景 static void DrawBackground(void) { extern const unsigned char g_arrSpeedBgImage800x480[768068]; DrawBMP(g_arrSpeedBgImage800x480, 0, 0); } //设置速度仪表盘速度显示 //speed:0~1200 void SetSpeedometer(unsigned int speed) { double pi, angle0, angle1, angle, rate; int x, y, x0, y0, x1, y1; //限定范围 if (speed > 1200) { speed = 1200; } //锁定 LCD LCDLock(); //绘制背景 DrawBackground(); //计算占比大小 rate = 100.0 * (double)speed / 1200.0; rate = 100.0 - rate; //求直线在直角坐标系中的角度 pi = 3.1415926535; angle0 = pi * -0.2445; //100%对应的角度(弧度) angle1 = pi * 1.2445; //0%对应的角度(弧度) angle = angle0 + (angle1 - angle0) * rate / 100.0; //求直线起点终点 x = POINTER_LEN * cos(angle); y = POINTER_LEN * sin(angle); y = -y; x0 = POINTER_X0; y0 = POINTER_Y0; x1 = x0 + x; y1 = y0 + y; //绘制直线 LCDDrawLine(x0, y0, x1, y1, POINTER_COLOR); //解锁 LCD LCDUnlock(); } 方法三:用纯色刷新指针所在区域

刷图固然简单,但是卡慢,苹果派开发板承受不来。光是仪表盘显示,可能就耗光了苹果派的算力。如果仪表盘的元素比较简单,背景是纯色的,就像本文提供的那种图片一样。这时候我们可以选择将指针长度缩短一些,用纯色刷新指针显示区域。

这个方法具体步骤是,首先规划指针显示的矩形区域,注意,必须是矩形,且矩形区域不能覆盖到仪表盘的刻度尺上,然后每次显示指针之前,用纯色(与仪表盘背景颜色相同)填充这片矩形区域,最后将指针画上去即可。因为指针显示的区域有限制,所以用此方法时,指针一般不会太长,且不会延伸到仪表盘的刻度尺上。

具体实现代码如下所示。同样的,刷新区域要通过 LCDDrawRectangle 函数多次标定,才会得到比较理想的值。因为是纯色填充,会显得指针起点比较孤单,为了仪式感,我们可以以指针起点为圆心,绘制一个空心圆,或实心圆。

#include "./LCD/LCD.h" #include "./Picture/BMP.h" #include //刷新区域 #define POINTER_AREA_X0 (300) #define POINTER_AREA_Y0 (160) #define POINTER_AREA_X1 (500) #define POINTER_AREA_Y1 (360) #define POINTER_AREA_WIDTH (POINTER_AREA_X1 - POINTER_AREA_X0 + 1) #define POINTER_AREA_HEIGHT (POINTER_AREA_Y1 - POINTER_AREA_Y0 + 1) //指针相关信息 #define POINTER_X0 ((POINTER_AREA_X1 + POINTER_AREA_X0) / 2) #define POINTER_Y0 ((POINTER_AREA_Y1 + POINTER_AREA_Y0) / 2) #define POINTER_LEN (POINTER_AREA_WIDTH / 2) #define POINTER_COLOR (LCD_COLOR_RED) //背景颜色,初始化时自动读取 static unsigned int s_iBackColor; //绘制背景 static void DrawBackground(void) { extern const unsigned char g_arrSpeedBgImage800x480[768068]; DrawBMP(g_arrSpeedBgImage800x480, 0, 0); } //初始化速度仪表盘 void InitSpeedometer(void) { //绘制背景图片 DrawBackground(); //读取背景色 s_iBackColor = LCDReadPoint(POINTER_X0, POINTER_Y0); //标记矩形区域 //LCDDrawRectangle(POINTER_AREA_X0, POINTER_AREA_Y0, POINTER_AREA_X1, POINTER_AREA_Y1, POINTER_COLOR); //初始化时速度显示值为 0 r/min SetSpeedometer(0); } //设置速度仪表盘速度显示 //speed:0~1200 void SetSpeedometer(unsigned int speed) { double pi, angle0, angle1, angle, rate; int x, y, x0, y0, x1, y1; //限定范围 if (speed > 1200) { speed = 1200; } //锁定 LCD LCDLock(); //刷新指针区域背景 LCDFill(POINTER_AREA_X0, POINTER_AREA_Y0, POINTER_AREA_X1, POINTER_AREA_Y1, s_iBackColor); //计算占比大小 rate = 100.0 * (double)speed / 1200.0; rate = 100.0 - rate; //求直线在直角坐标系中的角度 pi = 3.1415926535; angle0 = pi * -0.2445; //100%对应的角度(弧度) angle1 = pi * 1.2445; //0%对应的角度(弧度) angle = angle0 + (angle1 - angle0) * rate / 100.0; //求直线起点终点 x = POINTER_LEN * cos(angle); y = POINTER_LEN * sin(angle); y = -y; x0 = POINTER_X0; y0 = POINTER_Y0; x1 = x0 + x; y1 = y0 + y; //绘制直线 LCDDrawLine(x0, y0, x1, y1, POINTER_COLOR); //画圆,突出圆心位置(有些半径会显得不够圆,应该是驱动问题) LCDDrawCircle(x0, y0, 21, POINTER_COLOR); //解锁 LCD LCDUnlock(); } 方法四:使用填回背景的方式刷新指针显示区域

如果仪表盘的背景不是纯色的,带了一些图案或者是渐变色,那么就不能简单的用纯色刷了。此时我们依旧可以事先规划指针显示区域,然后在初始化时,将这部分区域的像素点值读取到内存中,每次绘制指针前,先填回背景,然后在画指针,这样也能实现指针的转动,具体如下所示。

LCD 显示时,纯色填充和使用缓冲区填充速度上差不多,所以方法三同样适用于苹果派。但是,方法三也有局限性,那就是指针显示区域不能太大,因为背景缓冲区会占据大量内存,很容易造成内存不足的困境。不过,对于 GD32F470 或 STM32F429 等平台,这都不是事,刷就完事了。

#include "./LCD/LCD.h" #include "./Picture/BMP.h" #include //刷新区域 #define POINTER_AREA_X0 (300) #define POINTER_AREA_Y0 (160) #define POINTER_AREA_X1 (500) #define POINTER_AREA_Y1 (360) #define POINTER_AREA_WIDTH (POINTER_AREA_X1 - POINTER_AREA_X0 + 1) #define POINTER_AREA_HEIGHT (POINTER_AREA_Y1 - POINTER_AREA_Y0 + 1) //指针相关信息 #define POINTER_X0 ((POINTER_AREA_X1 + POINTER_AREA_X0) / 2) #define POINTER_Y0 ((POINTER_AREA_Y1 + POINTER_AREA_Y0) / 2) #define POINTER_LEN (POINTER_AREA_WIDTH / 2) #define POINTER_COLOR (LCD_COLOR_RED) //背景缓冲区 #if(0 != LCD_USE_RGB565) static unsigned short s_arrBackground[POINTER_AREA_WIDTH * POINTER_AREA_HEIGHT]; //RGB565 #else static unsigned int s_arrBackground[POINTER_AREA_WIDTH * POINTER_AREA_HEIGHT]; //RGB888 #endif //绘制背景 static void DrawBackground(void) { extern const unsigned char g_arrSpeedBgImage800x480[768068]; DrawBMP(g_arrSpeedBgImage800x480, 0, 0); } //初始化速度仪表盘 void InitSpeedometer(void) { unsigned int x, y, i; //绘制背景图片 DrawBackground(); //保存指针区域背景 i = 0; for (y = POINTER_AREA_Y0; y


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有