首页-达尔闻    全部    项目分享| 小屏幕,N多玩法!快让屏幕动起来

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

1块ESP32板+1块屏幕,能玩出什么花样呢?
收藏
  • 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 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");
          }
        }
      }

    •  

    除了以上项目之外,相信各位小伙伴还有更多有趣好玩的demo,欢迎来分享哦。


    原文链接:

    https://www.hackster.io/Arnov_Sharma_makes/ili9341-display-and-lolin-d32-carrier-board-d36e2e项目作者:Arnov Sharma

Control Render Error!ControlType:productSlideBind,StyleName:Style1,ColorName:Item0,Message:InitError, ControlType:productSlideBind Error:未将对象引用设置到对象的实例。