ESP32与GC9A01显示屏实现仪表盘项目
创作时间:
作者:
@小白创作中心
ESP32与GC9A01显示屏实现仪表盘项目
引用
CSDN
1.
https://blog.csdn.net/qq_64795369/article/details/141727327
本文将介绍如何使用ESP32和GC9A01显示屏实现一个可以显示随机数据的仪表盘项目。通过本教程,你将学习到硬件接线、环境配置、代码修改和程序运行等步骤。
一、效果预览
让我们先看看最终的效果:
中间的表针是可以移动的哦,那么直接进入正题。
二、硬件接线
这次的接线方式和之前有所不同,参考了其他代码,你可以根据需要进行调整:
VCC - 3.3V
GND - GND
SCL - D18
SDA - D23
DC - D16
CS - D22
RST - D4
三、环境配置
首先需要安装Adafruit_GC9A01A库,可以从以下GitHub地址获取:
GitHub - adafruit/Adafruit_GC9A01A: Adafruit_GFX-compatible library for GC9A01A display driver
https://github.com/adafruit/Adafruit_GC9A01A
安装完成后,还需要安装TFT_eSPI库,这个库可以直接在Arduino IDE中安装。选择最左边的第三个选项,然后搜索并安装即可。
四、修改配置文件
因为TFT_eSPI库兼容很多显示屏和开发板,所以需要进行一些设置。在C:\Users\Admin\Documents\Arduino\libraries\TFT_eSPI目录下找到User_Setup.h文件,复制一份备份,然后修改User_Setup.h文件,将内容替换为以下配置:
#define GC9A01_DRIVER
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 22
#define TFT_DC 16
#define TFT_RST 4
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define SPI_FREQUENCY 27000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
保存并退出。
五、查看示例并录入程序
现在打开Arduino IDE,依次选择:文件 -> 示例 -> TFT_eSPI -> Sprite -> Animated_dial
然后点击录入,就可以了。现在你就可以看到你的屏幕上显示了一个随机移动表针的仪表盘了。以下是完整的代码:
// 定义指针的长度、宽度、半径和颜色
#define NEEDLE_LENGTH 35 // 可见长度
#define NEEDLE_WIDTH 5 // 指针宽度 - 设置为奇数
#define NEEDLE_RADIUS 90 // 指针顶端的半径
#define NEEDLE_COLOR1 TFT_MAROON // 指针外围颜色
#define NEEDLE_COLOR2 TFT_RED // 指针中心颜色
#define DIAL_CENTRE_X 120 // 表盘中心的X坐标
#define DIAL_CENTRE_Y 120 // 表盘中心的Y坐标
// 加载字体
#include "NotoSansBold36.h"
#define AA_FONT_LARGE NotoSansBold36
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI(); // 创建TFT对象
TFT_eSprite needle = TFT_eSprite(&tft); // 用于指针的精灵对象
TFT_eSprite spr = TFT_eSprite(&tft); // 用于显示刻度的精灵对象
// 加载jpeg图片数据
#include "dial.h"
// 包含jpeg解码库
#include <TJpg_Decoder.h>
uint16_t* tft_buffer; // 用于存储TFT屏幕像素块的缓冲区
bool buffer_loaded = false; // 缓冲区是否已加载
uint16_t spr_width = 0; // 精灵宽度
uint16_t bg_color =0; // 背景颜色
// 这个函数将在解码jpeg文件时被调用
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
// 如果图片超出了屏幕边界,停止进一步解码
if ( y >= tft.height() ) return 0;
// 该函数会自动剪裁图像块,并将其渲染到TFT屏幕上
tft.pushImage(x, y, w, h, bitmap);
// 返回1以解码下一个块
return 1;
}
// 初始化设置
void setup() {
Serial.begin(115200); // 仅用于调试
// 设置字节顺序(对于TFT_eSPI设置为true)
TJpgDec.setSwapBytes(true);
// 必须给jpeg解码器指定渲染函数
TJpgDec.setCallback(tft_output);
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// 绘制表盘
TJpgDec.drawJpg(0, 0, dial, sizeof(dial));
tft.drawCircle(DIAL_CENTRE_X, DIAL_CENTRE_Y, NEEDLE_RADIUS-NEEDLE_LENGTH, TFT_DARKGREY);
// 加载字体并创建显示刻度的精灵
spr.loadFont(AA_FONT_LARGE);
spr_width = spr.textWidth("777"); // 数字7在此字体中最宽
spr.createSprite(spr_width, spr.fontHeight());
bg_color = tft.readPixel(120, 120); // 从表盘中心获取颜色
spr.fillSprite(bg_color);
spr.setTextColor(TFT_WHITE, bg_color, true);
spr.setTextDatum(MC_DATUM);
spr.setTextPadding(spr_width);
spr.drawNumber(0, spr_width/2, spr.fontHeight()/2);
spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2);
// 绘制标签文字
tft.setTextColor(TFT_WHITE, bg_color);
tft.setTextDatum(MC_DATUM);
tft.drawString("(degrees)", DIAL_CENTRE_X, DIAL_CENTRE_Y + 48, 2);
// 在创建指针之前,定义指针在TFT上的旋转中心点
tft.setPivot(DIAL_CENTRE_X, DIAL_CENTRE_Y);
// 创建指针精灵
createNeedle();
// 将指针位置重置为0
plotNeedle(0, 0);
delay(2000); // 延迟2秒
}
// 主循环
void loop() {
uint16_t angle = random(241); // 随机生成0到240范围内的角度
// 以每次40ms的增量绘制指针到随机角度
plotNeedle(angle, 30);
// 在新位置暂停2.5秒
delay(2500);
}
// 创建指针精灵
void createNeedle(void)
{
needle.setColorDepth(16); // 设置颜色深度
needle.createSprite(NEEDLE_WIDTH, NEEDLE_LENGTH); // 创建指针精灵
needle.fillSprite(TFT_BLACK); // 用黑色填充精灵
// 定义相对于精灵左上角的指针旋转点
uint16_t piv_x = NEEDLE_WIDTH / 2; // 精灵的旋转点x(在中间)
uint16_t piv_y = NEEDLE_RADIUS; // 精灵的旋转点y
needle.setPivot(piv_x, piv_y); // 设置旋转点
// 在精灵中绘制红色指针
needle.fillRect(0, 0, NEEDLE_WIDTH, NEEDLE_LENGTH, TFT_MAROON);
needle.fillRect(1, 1, NEEDLE_WIDTH-2, NEEDLE_LENGTH-2, TFT_RED);
// 获取旋转后边界的参数
int16_t min_x;
int16_t min_y;
int16_t max_x;
int16_t max_y;
// 计算必须从TFT上抓取的最坏情况区域(45度旋转时)
needle.getRotatedBounds(45, &min_x, &min_y, &max_x, &max_y);
// 计算缓冲区大小并分配用于抓取TFT区域的缓冲区
tft_buffer = (uint16_t*) malloc( ((max_x - min_x) + 2) * ((max_y - min_y) + 2) * 2 );
}
// 将指针移动到新位置
void plotNeedle(int16_t angle, uint16_t ms_delay)
{
static int16_t old_angle = -120; // 初始角度为-120度
// 边界框参数
static int16_t min_x;
static int16_t min_y;
static int16_t max_x;
static int16_t max_y;
if (angle < 0) angle = 0; // 限制角度以模拟指针停止
if (angle > 240) angle = 240;
angle -= 120; // 从-120度开始
// 逐步移动指针,直到达到新角度
while (angle != old_angle || !buffer_loaded) {
if (old_angle < angle) old_angle++;
else old_angle--;
// 仅在偶数值时绘制指针以提高绘制性能
if ( (old_angle & 1) == 0)
{
if (buffer_loaded) {
// 将原始区域推回到屏幕上以擦除旧指针图形
tft.pushRect(min_x, min_y, 1 + max_x - min_x, 1 + max_y - min_y, tft_buffer);
}
if ( needle.getRotatedBounds(old_angle, &min_x, &min_y, &max_x, &max_y) )
{
// 在绘制指针之前抓取该区域的副本
tft.readRect(min_x, min_y, 1 + max_x - min_x, 1 + max_y - min_y, tft_buffer);
buffer_loaded = true;
}
// 在新位置绘制指针,指针图像中的黑色为透明
needle.pushRotated(old_angle, TFT_BLACK);
// 在下一次更新之前延迟
delay(ms_delay);
}
// 更新表盘中心的数字
spr.setTextColor(TFT_WHITE, bg_color, true);
spr.drawNumber(old_angle+120, spr_width/2, spr.fontHeight()/2);
spr.pushSprite(120 - spr_width / 2, 120 - spr.fontHeight() / 2);
// 在指针接近新位置时稍微放慢速度
if (abs(old_angle - angle) < 10) ms_delay += ms_delay / 5;
}
}
通过这个代码,你可以在loop函数中随机生成数据并显示在仪表盘上。这非常便捷,你可以直接利用这个方法,增加一个可变电阻器来控制仪表盘的显示。
热门推荐
职场人必备:盐酸雷尼替丁胶囊救急指南
盐酸雷尼替丁使用指南:正确服用方法与注意事项
服用盐酸雷尼替丁胶囊期间,这些饮食禁忌要知道!
秋冬养生新宠:香附佩兰木香香囊
上海十大古镇全攻略:从朱家角到召稼楼,探寻魔都古镇韵味
斯大林格勒战役:从二战到现代城市战的启示
斯大林格勒到巴格达:现代城市作战的战术进化史
春节烟花祝福语大集合:创意、时效、经典一网打尽
元夕烟花诗词:古人如何描绘璀璨夜空?
解码沃尔沃车标:战神玛尔斯符号背后的传奇
解码沃尔沃车标:一个关于力量与安全的传奇
从包头前往新疆那拉提景区必游景点全解析
斯大林格勒战役:从古至今的城市作战战术变迁
冬游洛阳:提前规划2025年牡丹文化节之旅
洛阳牡丹花海打卡指南:绝美春日大片等你拍!
第41届洛阳牡丹文化节最佳观赏指南
洛索洛芬 vs 布洛芬:止痛药如何选?
洛索洛芬 vs 布洛芬:谁更适合你的慢性疼痛?
Steam新游上线:体验汉尼拔经典战术!
汉尼拔:战略大师的不朽传奇
汉尼拔战术如何助你打赢职场“坎尼之战”
《文明与征服》:汉尼拔阵容搭配攻略
玻尿酸 vs 肉毒素:谁才是悬针纹克星?
告别悬针纹,这三个面部瑜伽动作最有效
苹果手机流量管理小窍门,你get了吗?
苹果手机流量管理全攻略:从设置到省流技巧
鼋头渚的日出日落,绝美景观不容错过!
无锡自驾游打卡:惠山古镇&东林书院
无锡三天两夜:打卡江南名胜
元胡止痛片:慢性疼痛患者的福音?