项目分享| 小屏幕,N多玩法!快让屏幕动起来
-
1块ESP32板+1块屏幕,能玩出什么花样呢?
可以说,这个视频充分展现了ESP32的性能,虽然板子简单,但玩法超多。下面就说看看视频中几种花样展示,是如何做的吧。
✔ 硬件组成ILI9341 显示器
- ESP32
- Lolin D32 Pro
- 定制 PCB(由 PCBWAY 提供)
- 用于显示器和 ESP32 板的母头针连接器
目标是不使用电线和面包板的情况下,将显示器与 ESP32 板连接起来,以完成展示与显示器相关的项目。
关于ILI9341显示器:
ILI9341是一个支持分辨率为240RGBx320点阵的a-TFT LCD 的262 144色单片驱动器。
这个单片驱动器包含了一个720通道的源极驱动器(source driver),一个320通道的栅极驱动器(gate driver),172800字节的GRAM用于显示240RGBx320分辨率的图片数据,一套电源支持电路。
ILI9341 可在 1.65V ~ 3.3VI/O 接口电压下工作,并集成电压跟随器电路以产生驱动 LCD 的电压电平。
ILI9341提供8位/9位/16位/18位的并行MCU数据总线,6位/16位/18位RGB接口数据总线以及3或4线SPI接口。
该项目中使用的显示模块有一个内置的触摸界面,带有一个 SD 卡读卡器插槽,我们可以使用它来读取 SD 卡数据。
关于ESP32 Lolin D32 Pro:
Wemos Lolin D32 Pro 用于驱动该项目中的显示器。基于具有 16MB/4MB FLASH、4MB PSRAM 的 ESP32-WROVER 模块,并具有板载 SD 卡读卡器、显示端口和 I2C 连接器。
✔ 电路板设计
ESP32和 ILI9341显示器的基本连线如下:
为了方便连接,定制一个PCB载板,通过一个简单的原理图实现。
此外,原理图上还有一个 CON2 用于添加用于为该设置供电的外部电池以及与 ESP32 的 D2 连接的 LED。
完成原理图文件后,我们将其转换为电路板文件并准备 96mm x 55mm 外形尺寸的 PCB。
ILI9341 显示器添加在 TOP 侧,ESP32 位于底部,在显示端口和 ESP32 连接附近添加了额外的连接器,因此我们可以在这些连接器中添加接头引脚,以使用 ESP32 GPIO 引脚或使用显示引脚。
✔ 软件部分
现在将 ESP32 和 显示器添加到载板上并准备该项目的软件部分。
库 TFT_eSPI
为了驱动 ILI9341 显示器,使用 Bodmer 流行的 TFT_eSPI 库。
下载链接如下:
https://github.com/Bodmer/TFT_eSPI
TFT_eSPI 是一个不错的库,支持所有使用的主要显示器,如 ILI9430、ST7735,甚至圆形 LCD GC9A01。
步骤:
- 去 Github Page 并下载 RAW 文件。
- 在 Documents>Arduino>Libraries 中提取文件夹,我们将在其中保存所有自定义库。
- 打开 Arduino IDE 并到库管理器中添加的 TFT_eSPI。
为了使用不同类型的显示器,在这个库的 User_Setup 文件中进行了更改,默认设置为ILI9341 显示器,因此我们不必更改任何内容以使用当前显示器,但如果我们想使用不同的显示器,如 GC9A01 圆形LCD,然后必须编辑 User_Setup.h 文件。
如果是第一次使用 ESP32,Arduino IDE 默认不包含 ESP32 板,必须通过将以下链接放入 Arduino IDE 的首选项中,然后通过板管理器添加它们。
https://dl.espressif.com/dl/package_esp32_index.json
✔ 示例demo
为了测试设置,首先通过 USB 将 ESP32 开发板与 Arduino IDE 连接,然后将开发板更改为 Lolin D32 Pro 并选择正确的端口。
将从GitHub上下载的文件中:File>Examples> TFT_eSPI>320x240,选择任意demo,将其上传到ESP32。
1)矩阵
选择 TFT_Matrix Sketch,它显示随机数字和字母从显示屏的顶部滚动到底部,就像在电影矩阵中一样。
这是相应的代码:
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
#define TEXT_HEIGHT 8 // Height of text to be printed and scrolled
#define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
#define TOP_FIXED_AREA 0 // Number of lines in top fixed area (lines counted from top of screen)uint16_t yStart = TOP_FIXED_AREA;
uint16_t yArea = 320 - TOP_FIXED_AREA - BOT_FIXED_AREA;
uint16_t yDraw = 320 - BOT_FIXED_AREA - TEXT_HEIGHT;
byte pos[42];
uint16_t xPos = 0;void setup() {
Serial.begin(115200);
randomSeed(analogRead(A0));
tft.init();
tft.setRotation(0);
tft.fillScreen(ILI9341_BLACK);
setupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA);
}void loop(void) {
// First fill the screen with random streaks of characters
for (int j = 0; j < 600; j += TEXT_HEIGHT) {
for (int i = 0; i < 40; i++) {
if (pos[i] > 20) pos[i] -= 3; // Rapid fade initially brightness values
if (pos[i] > 0) pos[i] -= 1; // Slow fade later
if ((random(20) == 1) && (j<400)) pos[i] = 63; // ~1 in 20 probability of a new character
tft.setTextColor(pos[i] << 5, ILI9341_BLACK); // Set the green character brightness
if (pos[i] == 63) tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); // Draw white character
xPos += tft.drawChar(random(32, 128), xPos, yDraw, 1); // Draw the character
}
yDraw = scroll_slow(TEXT_HEIGHT, 14); // Scroll, 14ms per pixel line
xPos = 0;
}while (1) {yield(); yDraw = scroll_slow(320,5); }// Scroll 320 lines, 5ms per line
}
void setupScrollArea(uint16_t TFA, uint16_t BFA) {
tft.writecommand(ILI9341_VSCRDEF); // Vertical scroll definition
tft.writedata(TFA >> 8);
tft.writedata(TFA);
tft.writedata((320 - TFA - BFA) >> 8);
tft.writedata(320 - TFA - BFA);
tft.writedata(BFA >> 8);
tft.writedata(BFA);
}int scroll_slow(int lines, int wait) {
int yTemp = yStart;
for (int i = 0; i < lines; i++) {
yStart++;
if (yStart == 320 - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA;
scrollAddress(yStart);
delay(wait);
}
return yTemp;
}void scrollAddress(uint16_t VSP) {
tft.writecommand(ILI9341_VSCRSADD); // Vertical scrolling start address
tft.writedata(VSP >> 8);
tft.writedata(VSP);
}2)数码时钟
使用 Digital_Clock Sketch 来显示 MCU 在上传之前从计算机获取的实时数据。
如果重置或拔下设备并从外部源重新启动,时间将重置。
3)键盘 240x320
这是一个有趣的交互式数字键盘demo,可以在上面输入任何数字,并将该数字发送到串行监视器。
4)开关按钮
接下来是修改的 ON-OFF Button Sketch,这样当按钮切换时,可以控制 LED 的状态。
5)Boing球
这是一个要求最高的demo,它充分利用了 ESP32 的处理能力,即Boing Ball 动画demo。
它包含一个附加的头文件,该文件包含在代码部分中。
-
// 'Boing' ball demo
#define SCREENWIDTH 320
#define SCREENHEIGHT 240
#include "graphic.h"
#include <TFT_eSPI.h> // Hardware-specific libraryTFT_eSPI tft = TFT_eSPI(); // Invoke custom library
#define BGCOLOR 0xAD75
#define GRIDCOLOR 0xA815
#define BGSHADOW 0x5285
#define GRIDSHADOW 0x600C
#define RED 0xF800
#define WHITE 0xFFFF#define YBOTTOM 123 // Ball Y coord at bottom
#define YBOUNCE -3.5 // Upward velocity on ball bouncefloat ballx = 20.0, bally = YBOTTOM, // Current ball position
ballvx = 0.8, ballvy = YBOUNCE, // Ball velocity
ballframe = 3; // Ball animation frame #
int balloldx = ballx, balloldy = bally; // Prior ball positionuint16_t renderbuf[2][SCREENWIDTH];
uint16_t palette[16]; // Color table for ball rotation effect
uint32_t startTime, frame = 0; // For frames-per-second estimate
void setup() {
Serial.begin(115200);
// while(!Serial);
tft.begin();
tft.setRotation(3); // Landscape orientation, USB at bottom right
tft.setSwapBytes(false);
// Draw initial framebuffer contents:
//tft.setBitmapColor(GRIDCOLOR, BGCOLOR);
tft.fillScreen(BGCOLOR);tft.initDMA();
tft.drawBitmap(0, 0, (const uint8_t *)background, SCREENWIDTH, SCREENHEIGHT, GRIDCOLOR);
startTime = millis();
}void loop() {
balloldx = (int16_t)ballx; // Save prior position
balloldy = (int16_t)bally;
ballx += ballvx; // Update position
bally += ballvy;
ballvy += 0.06; // Update Y velocity
if((ballx <= 15) || (ballx >= SCREENWIDTH - BALLWIDTH))
ballvx *= -1; // Left/right bounce
if(bally >= YBOTTOM) { // Hit ground?
bally = YBOTTOM; // Clip and
ballvy = YBOUNCE; // bounce up
}int16_t minx, miny, maxx, maxy, width, height;
// Determine bounds of prior and new positions
minx = ballx;
if(balloldx < minx) minx = balloldx;
miny = bally;
if(balloldy < miny) miny = balloldy;
maxx = ballx + BALLWIDTH - 1;
if((balloldx + BALLWIDTH - 1) > maxx) maxx = balloldx + BALLWIDTH - 1;
maxy = bally + BALLHEIGHT - 1;
if((balloldy + BALLHEIGHT - 1) > maxy) maxy = balloldy + BALLHEIGHT - 1;width = maxx - minx + 1;
height = maxy - miny + 1;// Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5;
if(ballframe < 0) ballframe += 14; // Constrain from 0 to 13
else if(ballframe >= 14) ballframe -= 14;// Set 7 palette entries to white, 7 to red, based on frame number.
// This makes the ball spin
for(uint8_t i=0; i<14; i++) {
palette[i+2] = ((((int)ballframe + i) % 14) < 7) ? WHITE : RED;
// Palette entries 0 and 1 aren't used (clear and shadow, respectively)
}// Only the changed rectangle is drawn into the 'renderbuf' array...
uint16_t c, *destPtr;
int16_t bx = minx - (int)ballx, // X relative to ball bitmap (can be negative)
by = miny - (int)bally, // Y relative to ball bitmap (can be negative)
bgx = minx, // X relative to background bitmap (>= 0)
bgy = miny, // Y relative to background bitmap (>= 0)
x, y, bx1, bgx1; // Loop counters and working vars
uint8_t p; // 'packed' value of 2 ball pixels
int8_t bufIdx = 0;// Start SPI transaction and drop TFT_CS - avoids transaction overhead in loop
tft.startWrite();// Set window area to pour pixels into
tft.setAddrWindow(minx, miny, width, height);// Draw line by line loop
for(y=0; y<height; y++) { // For each row...
destPtr = &renderbuf[bufIdx][0];
bx1 = bx; // Need to keep the original bx and bgx values,
bgx1 = bgx; // so copies of them are made here (and changed in loop below)
for(x=0; x<width; x++) {
if((bx1 >= 0) && (bx1 < BALLWIDTH) && // Is current pixel row/column
(by >= 0) && (by < BALLHEIGHT)) { // inside the ball bitmap area?
// Yes, do ball compositing math...
p = ball[by][bx1 / 2]; // Get packed value (2 pixels)
c = (bx1 & 1) ? (p & 0xF) : (p >> 4); // Unpack high or low nybble
if(c == 0) { // Outside ball - just draw grid
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR;
} else if(c > 1) { // In ball area...
c = palette[c];
} else { // In shadow area...
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDSHADOW : BGSHADOW;
}
} else { // Outside ball bitmap, just draw background bitmap...
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR;
}
*destPtr++ = c<<8 | c>>8; // Store pixel color
bx1++; // Increment bitmap position counters (X axis)
bgx1++;
}tft.pushPixelsDMA(&renderbuf[bufIdx][0], width); // Push line to screen
// Push line to screen (swap bytes false for STM/ESP32)
//tft.pushPixels(&renderbuf[bufIdx][0], width);bufIdx = 1 - bufIdx;
by++; // Increment bitmap position counters (Y axis)
bgy++;
}
//if (random(100) == 1) delay(2000);
tft.endWrite();
//delay(5);
// Show approximate frame rate
if(!(++frame & 255)) { // Every 256 frames...
uint32_t elapsed = (millis() - startTime) / 1000; // Seconds
if(elapsed) {
Serial.print(frame / elapsed);
Serial.println(" fps");
}
}
}
除了以上项目之外,相信各位小伙伴还有更多有趣好玩的demo,欢迎来分享哦。
原文链接:
https://www.hackster.io/Arnov_Sharma_makes/ili9341-display-and-lolin-d32-carrier-board-d36e2e项目作者:Arnov Sharma
-