硬件组成

目标是不使用电线和面包板的情况下,将显示器与 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显示器的基本连线如下:

可视化大屏屏幕适配方案(项目分享小屏幕)(1)

为了方便连接,定制一个PCB载板,通过一个简单的原理图实现。

可视化大屏屏幕适配方案(项目分享小屏幕)(2)

此外,原理图上还有一个 CON2 用于添加用于为该设置供电的外部电池以及与 ESP32 的 D2 连接的 LED。

完成原理图文件后,我们将其转换为电路板文件并准备 96mm x 55mm 外形尺寸的 PCB。

可视化大屏屏幕适配方案(项目分享小屏幕)(3)

ILI9341 显示器添加在 TOP 侧,ESP32 位于底部,在显示端口和 ESP32 连接附近添加了额外的连接器,因此我们可以在这些连接器中添加接头引脚,以使用 ESP32 GPIO 引脚或使用显示引脚。

✔ 软件部分

现在将 ESP32 和 显示器添加到载板上并准备该项目的软件部分。

库 TFT_eSPI

为了驱动 ILI9341 显示器,使用 Bodmer 流行的 TFT_eSPI 库。

下载链接如下:

Github/Bodmer/TFT_eSPI

TFT_eSPI 是一个不错的库,支持所有使用的主要显示器,如 ILI9430、ST7735,甚至圆形 LCD GC9A01。

步骤:

为了使用不同类型的显示器,在这个库的 User_Setup 文件中进行了更改,默认设置为ILI9341 显示器,因此我们不必更改任何内容以使用当前显示器,但如果我们想使用不同的显示器,如 GC9A01 圆形LCD,然后必须编辑 User_Setup.h 文件。

如果是第一次使用 ESP32,Arduino IDE 默认不包含 ESP32 板,必须通过将以下链接放入 Arduino IDE 的首选项中,然后通过板管理器添加它们。

dl.espressif/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,它显示随机数字和字母从显示屏的顶部滚动到底部,就像在电影矩阵中一样。

可视化大屏屏幕适配方案(项目分享小屏幕)(4)

这是相应的代码:

#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 在上传之前从计算机获取的实时数据。

可视化大屏屏幕适配方案(项目分享小屏幕)(5)

如果重置或拔下设备并从外部源重新启动,时间将重置。

3)键盘 240x320

这是一个有趣的交互式数字键盘demo,可以在上面输入任何数字,并将该数字发送到串行监视器。

可视化大屏屏幕适配方案(项目分享小屏幕)(6)

嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!

无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。

点击这里找小助理0元领取:s.pdb2/l/cnklSITCGo24eIn

可视化大屏屏幕适配方案(项目分享小屏幕)(7)

4)开关按钮

接下来是修改的 ON-OFF Button Sketch,这样当按钮切换时,可以控制 LED 的状态。

可视化大屏屏幕适配方案(项目分享小屏幕)(8)

5)Boing球

这是一个要求最高的demo,它充分利用了 ESP32 的处理能力,即Boing Ball 动画demo。

可视化大屏屏幕适配方案(项目分享小屏幕)(9)

它包含一个附加的头文件,该文件包含在代码部分中。

// 'Boing' ball demo #define SCREENWIDTH 320 #define SCREENHEIGHT 240 #include "graphic.h" #include <TFT_eSPI.h> // Hardware-specific library TFT_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 bounce float 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 position uint16_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"); } } }

原文作者:达尔闻说

原文标题:项目分享| 小屏幕,N多玩法!快让屏幕动起来

原文链接:mp.weixin.qq/s/qyxqPbhAgMNSt6AYnsolNg

,