
给项目加一块能画图能触摸的屏,ILI9341 这块 2.8 寸 TFT 是老熟人了:240×320 分辨率、SPI 接口、自带电阻触摸,十几块钱就能拿下。不过第一次接 ESP32 的人,十个里有八个卡在两件事上:线接错一根就全屏发白,或者 TFT_eSPI 库的 User_Setup.h 没配对、编译直接报错。这篇照着一份完整的实操教程,把接线、配置、代码、触点读取一次走通,新手容易栽的坎我都标出来了。
先认识这块屏:屏和触摸是两套 SPI
这块屏的驱动芯片是 ILI9341,负责显示;触摸部分用的是 XPT2046 电阻触控芯片,也是走 SPI。划重点:显示和触摸是两路独立的 SPI,各有一组引脚,接线时千万别混。屏背面能看到两排引脚,一排是显示用的 SDO(MISO)、LED、SCK、SDI(MOSI)、D/C、RESET、CS、GND、VCC,另一排是触摸专用的 T_IRQ、T_OUT、T_DIN、T_CS、T_CLK,旁边还有个 SD 卡座,想给项目加载图片素材可以用。

还有一个关键小细节:模块上有个叫 J1 的跳线,它决定 VCC 该接 5V 还是 3.3V。J1 默认一般是断开的,这时候 VCC 接 5V;如果你把 J1 短接了,就得改接 3.3V。接错不一定马上烧,但屏幕大概率不亮或者显示发虚,排查时先看这里。

接线:照表抄,别自由发挥
这块屏的引脚映射是定死的,教程里明确说了:必须用下面这些 GPIO,换别的脚项目跑不起来。因为 TFT_eSPI 和触屏库都是按这套引脚写死的配置。准备的东西也简单:一块 ESP32 DOIT DEVKIT V1 开发板、面包板、杜邦线,再加屏本身。逐条照着接:
- 显示部分:SDO(MISO) 接 GPIO 12
- SCK 接 GPIO 14
- SDI(MOSI) 接 GPIO 13
- D/C 接 GPIO 2
- CS 接 GPIO 15
- LED 接 GPIO 21(背光)
- RESET 接开发板的 EN 或 RESET 引脚
- 触摸部分:T_CLK 接 GPIO 25
- T_CS 接 GPIO 33
- T_DIN 接 GPIO 32
- T_OUT 接 GPIO 39
- T_IRQ 接 GPIO 36
- GND 接 GND,VCC 接 5V(J1 短接的改 3.3V)

你会发现触摸那五个脚用的 GPIO(36、39、32、33、25)和显示那排完全错开,这就是前面说的两路独立 SPI。代码里触摸走 VSPI,显示的引脚则写死在 User_Setup.h 里,两边互不打架。接完先别急着上电,从头到尾再数一遍线,杜邦线插错相邻的脚是最常见的事故。
装两个库,再换上配套的 User_Setup.h
Arduino IDE 里打开 库管理器(Sketch > Include Library > Manage Libraries),搜两个库:
- TFT_eSPI,作者是 Bodmer,负责驱动屏幕显示;
- XPT2046_Touchscreen,作者是 Paul Stoffregen,负责读触点,这个库不用额外配置。
装完别急着写代码。TFT_eSPI 的玄机全在一个叫 User_Setup.h 的配置文件里,它放在库文件夹内,定义了驱动型号、引脚、SPI 频率这些参数。这份教程配套了一份现成的配置文件,关键定义给你过一遍:驱动用的是 ILI9341_2_DRIVER(不是普通那个 ILI9341_DRIVER,这是很多人编译报错的成因);分辨率 TFT_WIDTH 240、TFT_HEIGHT 320;显示引脚 MISO 12、MOSI 13、SCLK 14、CS 15、DC 2,RST 设成 -1 表示接在开发板 RST 上;触摸片选 TOUCH_CS 33;背光 TFT_BL 21、高电平点亮;SPI 频率 55MHz、读频率 20MHz、触摸频率 2.5MHz,另外定义了 USE_HSPI_PORT。
怎么放这个文件:打开 Arduino IDE 的 首选项(File > Preferences),复制"Sketchbook location"那个路径,比如 Windows 下通常是 C:\Users\你的用户名\Documents\Arduino,Mac 下是 /Users/你的用户名/Documents/Arduino。进到这个目录下的 libraries 文件夹,再进 TFT_eSPI 文件夹,把下载的 User_Setup.h 覆盖掉同名文件就完事了。
教程里特意加粗提醒过一句,这里原样转告:网上随便找的 User_Setup.h 大概率配不上这套示例,必须用教程提供的这份,不然引脚对不上,白折腾一晚上。
示例代码:显示文字加触点测试
配置就位后,烧一段完整示例:屏幕顶部居中显示 Hello, world!,中间提示 Touch screen to test,手指按下去,屏幕上实时显示触点坐标和压力值,串口也同步打印。完整代码如下,可以整段复制进 Arduino IDE:
#include "SPI.h"
#include "TFT_eSPI.h"
#include "XPT2046_Touchscreen.h"
TFT_eSPI tft = TFT_eSPI();
// Touchscreen pins
#define XPT2046_IRQ 36 // T_IRQ
#define XPT2046_MOSI 32 // T_DIN
#define XPT2046_MISO 39 // T_OUT
#define XPT2046_CLK 25 // T_CLK
#define XPT2046_CS 33 // T_CS
SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 2
// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;
// Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor
void printTouchToSerial(int touchX, int touchY, int touchZ) {
Serial.print("X = ");
Serial.print(touchX);
Serial.print(" | Y = ");
Serial.print(touchY);
Serial.print(" | Pressure = ");
Serial.print(touchZ);
Serial.println();
}
// Print Touchscreen info about X, Y and Pressure (Z) on the TFT Display
void printTouchToDisplay(int touchX, int touchY, int touchZ) {
// Clear TFT screen
tft.fillScreen(TFT_WHITE);
tft.setTextColor(TFT_BLACK, TFT_WHITE);
int centerX = SCREEN_WIDTH / 2;
int textY = 80;
String tempText = "X = " + String(touchX);
tft.drawCentreString(tempText, centerX, textY, FONT_SIZE);
textY += 20;
tempText = "Y = " + String(touchY);
tft.drawCentreString(tempText, centerX, textY, FONT_SIZE);
textY += 20;
tempText = "Pressure = " + String(touchZ);
tft.drawCentreString(tempText, centerX, textY, FONT_SIZE);
}
void setup() {
Serial.begin(115200);
// Start the SPI for the touchscreen and init the touchscreen
touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
// Set the Touchscreen rotation in landscape mode
// Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);
touchscreen.setRotation(1);
// Start the tft display
tft.init();
// Set the TFT display rotation in landscape mode
tft.setRotation(1);
// Clear the screen before writing to it
tft.fillScreen(TFT_WHITE);
tft.setTextColor(TFT_BLACK, TFT_WHITE);
// Set X and Y coordinates for center of display
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;
tft.drawCentreString("Hello, world!", centerX, 30, FONT_SIZE);
tft.drawCentreString("Touch screen to test", centerX, centerY, FONT_SIZE);
}
void loop() {
// Checks if Touchscreen was touched, and prints X, Y and Pressure (Z) info on the TFT display and Serial Monitor
if (touchscreen.tirqTouched() && touchscreen.touched()) {
// Get Touchscreen points
TS_Point p = touchscreen.getPoint();
// Calibrate Touchscreen points with map function to the correct width and height
x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
z = p.z;
printTouchToSerial(x, y, z);
printTouchToDisplay(x, y, z);
delay(100);
}
}代码开头那三行 include 写成双引号形式,和尖括号写法在 Arduino 工具链下编译等价,搜库路径一样,不用在意这个差别。
代码怎么走:三个关键点
第一,初始化顺序。setup() 里先把触摸的 SPI 起起来,五个引脚宏一一传进 touchscreenSPI.begin(),注意参数顺序是 CLK、MISO、MOSI、CS,别按惯性写成 MOSI 在前。然后 tft.init() 起显示,两边都用 setRotation(1) 转成横屏。有个小提示教程里写了:个别屏的触摸方向是倒的,遇到按 A 出 F 的情况,把触摸的 setRotation 改成 3 就正了。
第二,触点坐标要校准。getPoint() 返回的是触摸芯片的原始 ADC 值,范围大概几百到三千多,不是屏幕像素。代码里用 map() 把 p.x 从 200 到 3700 映射到 1 到 320 宽度、p.y 从 240 到 3800 映射到 1 到 240 高度,z 直接当压力值用。你要是发现点哪里坐标都顶在边缘,就是映射区间和你的屏不匹配,微调那四个数字即可。
第三,判断是否触摸用的是两个条件相与:tirqTouched() 检查中断脚有没有触发,touched() 确认真按上了,两个都满足才去取点,避免误报。取完点调 printTouchToSerial 打到串口,调 printTouchToDisplay 刷到屏上,后者每次先 fillScreen 清屏再写三行文字,所以屏上永远只有最新一次的触点信息。
烧录验证:两分钟看到结果
上传前在 Tools > Board 里选 ESP32 Dev Module,Port 选对准的 COM 口,点上传。烧完屏幕顶部居中出现 Hello, world!,中间一行 Touch screen to test,就说明显示部分通了。拿手指或触屏笔按下去,屏上刷出 X、Y 和 Pressure 三行数值,串口监视器(波特率 115200)同步滚动同样的内容。


顺带说一句,这块是 240×320 的电阻触摸屏,要用点力气按才灵敏,别拿它跟手机电容屏比手感。坐标会跳也是电阻屏的正常现象,map 校准区间调准后误差能压到很小。
收个尾
走完这篇,你手里就有了一块 ESP32 能画图、能触摸的彩色屏。这套组合很适合做桌面小仪表、传感器面板、菜单界面这类需要 GUI 的 DIY 项目。后面想更进一步,可以试试往 SD 卡里放图片做开机画面,或者换 LVGL 这类图形库上更精细的控件。把这篇的接线表存好,下次重接不用再翻教程。











J1 跳线这个细节没人讲啊,我接 5V 之前压根不知道还有这说法,还好这次看到文章了。
按这个接线表走了一遍,第一次卡在 D/C 和 RESET 这两根上,看图才发现 RESET 是接开发板 EN 那排的,不是 GPIO。上电屏亮起来那一刻还是挺有成就感的,Hello, world! 出来之后按一下屏幕,坐标和压力值直接刷出来,比想象中顺利。提醒后来的:杜邦线一定要数两遍,我邻座的朋友把 SCK 和 SDI 插反,全屏白屏查了半小时。
User_Setup.h 这个坑是真的,我之前随手搜了个配置文件,编译报错查了一晚上,后来才知道驱动要用 ILI9341_2_DRIVER 这版。
电阻屏要用力按这点太真实了,轻轻划一下坐标根本不出来,我一度以为屏是坏的。