项目分享|小屏幕,N多玩法!快让屏幕动起来


硬件组成

  • 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,可以在上面输入任何数字,并将该数字发送到串行监视器。

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

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

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

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 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多玩法!快让屏幕动起来

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

举报
评论 0