ESP32-S3, 教學資訊

【ESP32-S3 教學】ESP32-S3 + ILI9341 2.8吋觸控螢幕製作打地鼠遊戲|Arduino IDE 從接線、觸控校正到遊戲實作完整教學

DIY ESP32 Whack-a-Mole game on ILI9341 TFT touch screen display fully wired hardware circuit

想學習 ESP32-S3 圖形介面與人機介面(HMI)開發嗎?本篇教學將帶您使用高效能的 ESP32-S3 開發板 搭配 2.8 吋 ILI9341 TFT 觸控螢幕,從零開始打造一款經典好玩的「打地鼠遊戲(Whack-a-Mole)」。

透過本篇完整教學,您將學會以下核心技術:

  • ESP32-S3 SPI TFT 螢幕驅動與 ILI9341 顯示器設定

  • XPT2046 觸控控制器應用與獨立 SPI 架構

  • TFT_eSPI 高效能圖形函式庫使用技巧

  • 觸控座標即時校正與數據對應

  • 遊戲介面繪製、觸控事件判斷與計分邏輯

  • Arduino IDE 開發環境設定與常見問題排解

本次使用硬體與購買連結

在開始動手實作前,請先準備好以下硬體元件:

  1. ESP32-S3 N16R8 開發板

  2. 2.8 吋 ILI9341 TFT 觸控螢幕

專案成果展示

完成本教學的程式碼與硬體組裝後,您將實現以下互動功能:

  • 隨機洞穴位置出現地鼠

  • 觸控螢幕直接擊打地鼠得分

  • 30 秒遊戲倒數計時機制

  • 遊戲結束畫面與最終總分統計

第一階段:Arduino IDE 環境配置與程式庫安裝

Step 1:安裝 Arduino IDE 與 ESP32 核心

建議使用以下穩定版本進行開發:

  • Arduino IDE:1.8.19(或相容版本)

  • ESP32 Core:2.0.13

Step 2:安裝必要函式庫

在編寫程式前,需透過 Arduino IDE 安裝專門處理顯示與觸控的第三方程式庫:

  1. TFT_eSPI(建議版本:2.4.79)

    • 開啟 Arduino IDE,點選左側「程式庫管理員」(或至選單 工具 > 管理程式庫)。

    • 搜尋 TFT_eSPI,找到指定版本並點擊安裝。

  2. XPT2046_Touchscreen(建議版本:1.4)

    • 同樣透過程式庫管理員搜尋 XPT2046_Touchscreen,找到指定版本並點擊安裝。

第二階段:ESP32-S3 與 ILI9341 接線圖解

為了確保顯示流暢並避免訊號干擾,建議將 TFT 顯示器與 XPT2046 觸控晶片採用獨立的 SPI 腳位連接方式。

1. TFT 顯示器接線表

TFT 模組腳位

ESP32-S3腳位

VCC

3V3

GND

GND

LCD_CS

GPIO10

LCD_RST

GPIO14

LCD_RS(DC)

GPIO9

SDI(MOSI)

GPIO11

SCK

GPIO12

SDO(MISO)

GPIO13

LED

3V3

2. XPT2046 觸控接線表(獨立 SPI)

TOUCH腳位

ESP32-S3腳位

T_CS

GPIO16

T_CLK

GPIO6

T_DIN

GPIO4

T_DO

GPIO5

T_IRQ

不接

Wiring diagram and SPI pinout mapping table for ESP32-S3 connected to ILI9341 TFT display and XPT2046 touch controller

第三階段:TFT_eSPI 函式庫設定

安裝完成後,必須修改 TFT_eSPI 的設定檔以對應您的硬體腳位。

找到以下路徑的設定檔: Documents/Arduino/libraries/TFT_eSPI/User_Setup.h

請根據上方的接線對應修改 User_Setup.h 內容:

#define ILI9341_DRIVER

#define TFT_MISO 13
#define TFT_MOSI 11
#define TFT_SCLK 12

#define TFT_CS   10
#define TFT_DC   9
#define TFT_RST  14

#define SPI_FREQUENCY       40000000
#define SPI_READ_FREQUENCY  20000000

 

第四階段:觸控座標校正實作

由於每片觸控面板的物理特性略有差異,使用前必須透過校正程式取得對應的原始數值(Raw Data)。

觸控校正程式碼

程式載點:Touch Calibration Code

#include <SPI.h> 
#include <XPT2046_Touchscreen.h> 
#include <TFT_eSPI.h> 

TFT_eSPI tft = TFT_eSPI();

// Touch SPI
SPIClass touchSPI(FSPI);

#define TOUCH_SCLK 6
#define TOUCH_MISO 5
#define TOUCH_MOSI 4
#define TOUCH_CS   16

XPT2046_Touchscreen ts(TOUCH_CS);

void waitTouchRelease()
{
  while(ts.touched())
  {
    delay(10);
  }
}

void getCalibrationPoint(
  const char* text,
  int px,
  int py,
  uint16_t color,
  int &rawX,
  int &rawY
)
{
  tft.fillScreen(TFT_BLACK);

  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);

  tft.drawString(text, 20, 20);

  tft.fillCircle(px, py, 10, color);

  while(!ts.touched())
  {
    delay(10);
  }

  TS_Point p = ts.getPoint();

  rawX = p.x;
  rawY = p.y;

  Serial.printf(
    "%s : X=%d Y=%d Z=%d\n",
    text,
    p.x,
    p.y,
    p.z
  );

  waitTouchRelease();

  delay(500);
}

void setup()
{
  Serial.begin(115200);

  tft.init();
  tft.setRotation(1);

  touchSPI.begin(
    TOUCH_SCLK,
    TOUCH_MISO,
    TOUCH_MOSI,
    TOUCH_CS
  );

  ts.begin(touchSPI);

  int x1,y1;
  int x2,y2;
  int x3,y3;
  int x4,y4;

  getCalibrationPoint(
    "Touch TOP LEFT",
    20,
    20,
    TFT_RED,
    x1,
    y1
  );

  getCalibrationPoint(
    "Touch TOP RIGHT",
    300,
    20,
    TFT_GREEN,
    x2,
    y2
  );

  getCalibrationPoint(
    "Touch BOTTOM LEFT",
    20,
    220,
    TFT_BLUE,
    x3,
    y3
  );

  getCalibrationPoint(
    "Touch BOTTOM RIGHT",
    300,
    220,
    TFT_YELLOW,
    x4,
    y4
  );

  tft.fillScreen(TFT_BLACK);

  Serial.println();
  Serial.println("===== Calibration Result =====");

  Serial.printf("TOP LEFT     : X=%d Y=%d\n", x1, y1);
  Serial.printf("TOP RIGHT    : X=%d Y=%d\n", x2, y2);
  Serial.printf("BOTTOM LEFT  : X=%d Y=%d\n", x3, y3);
  Serial.printf("BOTTOM RIGHT : X=%d Y=%d\n", x4, y4);

  Serial.println("==============================");

  tft.drawString("Calibration Done", 50, 100);
}

void loop()
{
}

依照畫面指示依序點擊螢幕四角:

  1. TOP LEFT(左上)

  2. TOP RIGHT(右上)

  3. BOTTOM LEFT(左下)

  4. BOTTOM RIGHT(右下)

取得校正數值與座標對應

透過序列埠監視視窗取得實測結果後,定義您的極值並進行座標對應轉換:

#define TS_MINX 608
#define TS_MAXX 3714

#define TS_MINY 544
#define TS_MAXY 3545

 

轉換為螢幕座標:

int tx = map(
p.x,
TS_MAXX,
TS_MINX,
0,
320
);
  
int ty = map(
p.y,
TS_MAXY,
TS_MINY,
0,
240
);

 

第五階段:打地鼠遊戲核心功能實作

在主程式中,我們透過繪圖函式與邏輯判斷完成遊戲互動機制。

程式載點:Whack A Mole Code

1. 洞穴與地鼠繪製

利用圓形繪製函式在地畫出地鼠洞與隨機出現的地鼠:

洞穴繪製:tft.fillCircle(x, y, 35, TFT_DARKGREY);

地鼠繪製:tft.fillCircle(x, y, 28, TFT_BROWN);

 

2. 擊中判斷邏輯

透過計算觸控座標與地鼠中心點的幾何距離,判定玩家是否成功擊中目標:

int dx = tx - holes[moleIndex].x;
int dy = ty - holes[moleIndex].y;

if ((dx * dx + dy * dy) < 900)
{
    score++;
}

 

3. 倒數計時與遊戲結束控制

利用系統時間差計算剩餘秒數,時間歸零時觸發結算畫面:

int remain =
30 - ((millis() - gameStart) / 1000);
if ((millis() - gameStart) >= 30000)
{
    showGameOver();
}

常見問題與疑難排解 (FAQ)

Q1:ILI9341 螢幕沒有顯示畫面?

請確認以下項目:

  • TFT_CS 與 TFT_DC 實體接線與程式碼定義是否完全正確。

  • TFT_eSPI 的 User_Setup.h 設定檔是否已正確儲存並重新編譯。

  • SPI 通訊頻率是否設定過高(建議初始測試使用 40MHz 以下)。

Q2:XPT2046 觸控螢幕完全沒有反應?

請確認以下項目:

  • 觸控晶片的 T_CS 腳位連接是否正確。

  • 是否已執行並完成上文的觸控校正程式。

  • 觸控 SPI 是否採用獨立的腳位設定,避免與顯示器資料線衝突。

Q3:為什麼 ESP32-S3 與 XPT2046 建議要分開獨立 SPI?

部分市售的 ILI9341 模組若讓顯示與觸控共用同一組 SPI 匯流排,在高頻率存取時容易發生以下狀況:

  • 螢幕畫面閃爍

  • 觸控功能突然失效

  • 座標偏移或飄移

採用獨立 SPI 腳位能夠有效隔離訊號,大幅提升系統穩定度。

結語

透過本篇完整的實作教學,您已經成功學會使用 ESP32-S3 搭配 ILI9341 觸控螢幕 開發互動式遊戲。這項技術不僅能應用於趣味小遊戲,未來更能廣泛延伸至智慧家居控制面板、物聯網(IoT)狀態顯示器、教育互動設備以及各類 AI 人機介面專案中。

歡迎持續關注米羅科技 Mirotek,探索更多關於 ESP32、Arduino、樹莓派與電子模組的實用教學與硬體應用!

延伸閱讀與推薦課程