公司动态

ESP32-S3刷屏实战:TFT_eSPI与LVGL驱动显示

📅 2026/8/31 1:40:31
ESP32-S3刷屏实战:TFT_eSPI与LVGL驱动显示
这次我们来看一个很有意思的嵌入式显控项目ESP32-S3 刷屏效果。如果你最近在折腾 ESP32-S3 开发板想把屏幕点亮、跑动画、刷 UI或者想做一块能联网的桌面信息屏那这篇文章可以直接收藏。先说清楚“刷屏”这个词。在 ESP32-S3 的使用场景里它有两层意思一是“刷机”把编译好的固件烧录到芯片里二是“屏幕刷新”让 TFT LCD 或 OLED 屏循环更新画面实现动效、数字仪表、天气界面等信息展示。本文聊的主要是第二种但也会把第一种的完整流程讲清楚因为刷机是刷屏的前提。ESP32-S3 是乐鑫推出的高集成度 Wi-Fi BLE 微控制器双核 Xtensa LX7 架构自带 USB-Serial/JTAG 控制器配合 ST7789、ILI9341、SSD1306 等常见屏幕非常适合快速做出低成本、可联网的图形显示设备。它的核心优势不在算力而在“外围省事”USB 直连下载、SPI 驱动屏幕、硬件 SPI 频率可以拉高搭配 LVGL 或 TFT_eSPI 可以做出接近小卡片电脑的视觉效果。这篇文章会带你别完成这些内容先梳理 ESP32-S3 刷屏需要的硬件和软件环境然后配置 TFT_eSPI 驱动库再通过 Arduino 编写并烧录一个刷屏测试程序接着验证文字、颜色、动画、图片和 LVGL UI最后给出常见问题排查、性能观察思路和批量烧录建议。全程按照“能跑通优先再优化效果”的顺序来。1. 核心能力速览能力项说明芯片平台ESP32-S3双核 Xtensa LX7支持 2.4G Wi-Fi BLE 5显示接口SPI 为主常见屏幕驱动为 ST7789、ILI9341、SSD1306 等驱动库TFT_eSPI、Arduino_GFX、LVGL、ESP-IDF 组件开发环境Arduino IDE、VSCode PlatformIO、ESP-IDF、MicroPython、CircuitPython烧录方式USB 串口一键烧录部分开发板支持原生 USB 下载刷新优化支持硬件 SPI、DMA 传输、双缓冲、分段刷新联网能力Wi-Fi 获取天气、NTP 时间、MQTT 数据再显示到屏幕批量任务支持命令行 esptool.py 批量烧录也可脚本化固件合并与下载适合场景桌面信息屏、天气时钟、音乐频谱、卡片电脑原型、产品验证需要说明的是具体帧率、内存占用和刷新延迟取决于屏幕型号、SPI 频率、固件优化程度和是否带 PSRAM。不同开发板的引脚定义也不完全一致所以下面所有配置都必须按你自己的板子和屏幕型号调整。2. 适用场景与使用边界ESP32-S3 刷屏适合这几类场景第一快速原型验证。比如你有一个产品想法想在屏幕上展示传感器数据、Wi-Fi 状态、电机转速用 ESP32-S3 TFT_eSPI 一天内就能把交互页面搭起来。第二桌面信息设备。接上小型 IPS 屏幕通过 Wi-Fi 拉取天气、时间、新闻标题或 Bilibili 粉丝数做成一个低功耗桌面摆件。这类项目在 GitHub 上非常多很多都基于 ESP32-S3 和 LVGL。第三学习嵌入式图形栈。TFT_eSPI 代码清晰LVGL 组件丰富通过刷屏动画可以很好地理解 SPI 时序、DMA、帧缓冲、字体渲染这些底层机制。不适合的场景也要讲清楚。ESP32-S3 不适合做高分辨率视频播放也不适合跑重量级 3D 渲染。它内部 SRAM 有限不带 PSRAM 的话大尺寸 RGB 屏或复杂 LVGL 界面会非常吃力。如果你追求 60 帧动画、高分辨率 UI 和音频视频同步应该考虑更高性能的芯片平台。使用边界集中在合规和授权。如果你用别人设计的 UI 素材、字体文件、图标库、屏幕模块代码要注意确认授权范围尤其是商用项目。如果屏幕会显示他人照片、人脸、品牌 Logo 或敏感数据必须获得相应授权。联网功能如果用到了第三方天气、时间或消息服务要遵守对应接口的使用条款不要频繁请求导致被封。Wi-Fi 凭据和 API Key 不要硬编码后公开发布否则存在信息泄露风险。3. 环境准备与前置条件3.1 硬件清单开始之前请准备以下硬件硬件说明ESP32-S3 开发板推荐带原生 USB 接口的版本下载更稳定屏幕模块常见 ST7789、ILI9341 TFT 屏或 SSD1306 OLED分辨率从 0.96 寸到 2.8 寸不等接线工具杜邦线、面包板或电烙铁建议直接焊接避免接触不良USB 数据线必须支持数据传输不是只有充电功能电源多数开发板从 USB 5V 供电板上稳压到 3.3V屏幕背光电流较大的话要注意供电余量选屏幕时建议先选 SPI 接口的常见型号。4 线 SPI 屏幕MOSI、SCLK、CS、DC、RESET在 TFT_eSPI 里支持最好中文字库文档也最常见。RGB 接口屏幕虽然速度更快但占用引脚多接线复杂不适合第一次调试。3.2 软件环境软件部分选择 Arduino IDE 是最快上手的路径也可以用 VSCode 加 PlatformIO。下面以 Arduino IDE 为例。安装 Arduino IDE 后需要先添加 ESP32 开发板支持。在 Arduino IDE 的“文件 - 首选项 - 附加开发板管理器网址”中添加https://espressif.github.io/arduino-esp32/package_esp32_index.json然后打开“工具 - 开发板 - 开发板管理器”搜索esp32安装esp32 by Espressif Systems。这里要注意不同 Arduino IDE 版本的界面名称略有差异但流程一致。安装时间较长取决于网络环境。如果安装失败可以检查网络连接或者使用国内镜像源。不要同时安装多个版本的 ESP32 开发包避免库冲突。3.3 库安装在库管理器里搜索并安装TFT_eSPI用于 SPI 屏幕驱动LVGL如果要做复杂 UI建议用这个Arduino_GFX备选驱动库API 风格不同Adafruit GFX和Adafruit SSD1306如果使用 OLED 屏3.4 引脚接线注意事项ESP32-S3 的引脚不是所有 GPIO 都能随意用的。有些引脚在启动时有特殊功能比如 GPIO0、GPIO3、GPIO46 等部分引脚被 Flash 或 PSRAM 占用。屏幕的 MOSI、SCLK、CS、DC、RST、BL 引脚都需要选择空闲 GPIO。比较稳妥的做法是参考你的开发板原理图选一组不冲突的引脚。下面是一份常见的接线模板具体请按开发板和屏幕修改屏幕信号ESP32-S3 引脚示例VCC / VDD3.3VGNDGNDSCL / SCLKGPIO6SDA / MOSIGPIO7CSGPIO10DC / RSGPIO9RST / RESGPIO8BL / LEDGPIO5接线完成后先把代码里对应的引脚配置好再通电测试。因为 GPIO 冲突问题在 ESP32-S3 上很容易出现屏幕花屏或无反应时首先检查引脚配置。4. TFT_eSPI 驱动配置与编译TFT_eSPI 是刷屏项目中最常用的屏幕驱动库。它的特点是性能高、代码简洁、支持多种屏幕但需要你在库的配置头文件里手动选择屏幕驱动和引脚。4.1 修改 User_Setup.h在 Arduino 的 libraries 目录下找到 TFT_eSPI 文件夹打开User_Setup.h按下面的模板修改// 选择屏幕驱动这里以 ST7789 为例 #define ST7789_DRIVER // 屏幕分辨率 #define TFT_WIDTH 240 #define TFT_HEIGHT 240 // SPI 引脚配置请按你的实际接线修改 #define TFT_MOSI 7 #define TFT_SCLK 6 #define TFT_CS 10 #define TFT_DC 9 #define TFT_RST 8 #define TFT_BL 5 // 背光引脚如果屏幕背光独立控制则开启 #define TFT_BACKLIGHT_ON HIGH // SPI 频率常见为 27MHz 或 40MHz #define SPI_FREQUENCY 40000000如果你用的是 ILI9341就把ST7789_DRIVER注释掉改成#define ILI9341_DRIVER #define TFT_WIDTH 240 #define TFT_HEIGHT 320如果你用的是 SSD1306 OLEDTFT_eSPI 不适用建议使用 Adafruit SSD1306 库。配置文件的改动要格外小心。TFT_eSPI 对驱动宏定义是唯一的如果你同时启用了多个驱动编译会报错。改完配置后直接编译一个空工程确认没有语法错误。4.2 最小刷屏代码新建一个 Arduino 工程输入下面的测试代码。这段代码会先显示白底黑字然后循环切换纯色背景用来验证屏幕驱动和引脚是否正确。#include TFT_eSPI.h #include SPI.h TFT_eSPI tft TFT_eSPI(); void setup() { Serial.begin(115200); tft.init(); tft.setRotation(0); tft.fillScreen(TFT_WHITE); tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.setTextSize(2); tft.setCursor(15, 20); tft.println(ESP32-S3); tft.setCursor(15, 50); tft.println(TFT_eSPI); tft.setCursor(15, 80); tft.println(LCD Test); delay(2000); } void loop() { uint16_t colors[] {TFT_RED, TFT_GREEN, TFT_BLUE, TFT_YELLOW, TFT_CYAN, TFT_MAGENTA}; for (int i 0; i 6; i) { tft.fillScreen(colors[i]); delay(500); } }编译前在“工具 - 开发板”里选择ESP32S3 Dev Module。如果开发板没有自动进入下载模式可能需要手动按住 BOOT 键再插入 USB。5. 烧录与刷屏验证流程5.1 一键烧录刷机在 Arduino IDE 里点击右上角的“上传”按钮IDE 会先编译然后通过串口把固件写入 ESP32-S3。上传日志最后几行一般会有类似下面的输出Writing at 0x00010000... (100%) Wrote 442016 bytes (123456 compressed) at 0x00010000 in 3.6 seconds (effective 982.2 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin...看到Hash of data verified就说明固件写入成功开发板会自动重启。如果上传失败最常见的原因是开发板没有进入下载模式。解决方法是按住开发板上的 BOOT 键再按一下 RESET 键然后松开 BOOT 键立刻重新点击上传。5.2 验证显示效果上传成功并自动重启后屏幕应该显示文字然后进入颜色循环。如果你看到白屏、花屏或黑屏先不要怀疑代码优先检查 4 个地方TFT_eSPI 的User_Setup.h配置是否正确。屏幕引脚和代码引脚是否一致。屏幕供电是否稳定背光是否点亮。屏幕排线或杜邦线是否松动。如果屏幕能显示文字但颜色不对可能是驱动型号选择错误或者 SPI 字节序配置不匹配。ST7789 和 ILI9341 的初始化命令有差异选错驱动会出现“颜色反转”“局部花屏”之类的现象。5.3 测试其他刷新方式图片、动画、LVGL纯色循环只是最基础的验证。接下来可以测试三种更有价值的刷屏效果。第一种图片显示。把图片转为 C 数组或放入 LittleFS 文件系统通过tft.pushImage()显示。把 240x240 的 JPG 转为 RGB565 数组后显示整屏图片的典型流程是// 伪代码需要先解码图片并转为数组 tft.pushImage(0, 0, 240, 240, image_data);图片数据来自你手动转换的资源文件。实际使用时更推荐把图片文件放到 LittleFS 中运行时用 JPEGDEC 解码再显示这样固件体积更小。第二种动画刷新。通过不断改变绘制坐标或颜色来实现动画效果。一个简单的“移动方块”测试int x 0; int direction 1; void loop() { tft.fillRect(x - direction, 40, 20, 20, TFT_BLACK); x direction; tft.fillRect(x, 40, 20, 20, TFT_BLUE); if (x 200 || x 0) { direction -direction; } delay(10); }这种测试可以观察刷新速度。如果方块移动看起来不流畅说明 SPI 频率偏低、未用 DMA 或者帧率不足。第三种LVGL UI。在 ESP32-S3 上跑 LVGL 是目前比较主流的刷屏玩法。要跑 LVGL需要在 Arduino 中安装lvgl库并在代码里初始化显示器驱动和触摸驱动。LVGL 的典型初始化流程是#include lvgl.h #include TFT_eSPI.h static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[TFT_WIDTH * 10]; void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) { uint32_t w lv_area_get_width(area); uint32_t h lv_area_get_height(area); tft.startWrite(); tft.setAddrWindow(area-x1, area-y1, w, h); tft.pushColors(color_p-full, w * h, true); tft.endWrite(); lv_disp_flush_ready(disp); } void setup() { tft.init(); lv_init(); lv_disp_draw_buf_init(draw_buf, buf, NULL, TFT_WIDTH * 10); static lv_disp_drv_t disp_drv; lv_disp_drv_init(disp_drv); disp_drv.hor_res TFT_WIDTH; disp_drv.ver_res TFT_HEIGHT; disp_drv.flush_cb my_disp_flush; disp_drv.draw_buf draw_buf; lv_disp_drv_register(disp_drv); }这段代码里的TFT_WIDTH * 10是缓冲大小实际需要按你的内存余量调整。LVGL 版本不同API 会略有差异以你安装的版本为准。如果屏幕带触摸还需要注册触摸驱动。没有触摸时也可以直接用按键控制焦点和选中或者在电脑端通过 LVGL 模拟器先把界面调好再移植到 ESP32-S3。6. 接口能力与批量烧录思路6.1 串口输出与日志接口刷屏项目调试时串口是最重要的接口。在代码里用Serial.printf输出帧率、内存、错误信息通过 Arduino IDE 的串口监视器查看。常用调试信息模板void printFps() { static unsigned long lastTime 0; static unsigned int frameCount 0; static int fps 0; frameCount; unsigned long now millis(); if (now - lastTime 1000) { fps frameCount; frameCount 0; lastTime now; Serial.printf(FPS: %d\n, fps); } }这里的Serial走的是 USB 调试串口。如果你的代码里用了硬件串口或与蓝牙冲突需要注意日志输出也会消失。6.2 Wi-Fi 联网接口ESP32-S3 的联网能力是刷屏项目的重要加分项。通过 Wi-Fi 获取数据后再显示到屏幕可以让刷屏从“演示动画”变成“有用工具”。代码模板#include WiFi.h #include HTTPClient.h const char* ssid your_ssid; const char* password your_password; void connectWiFi() { WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(WiFi connected); }连接 Wi-Fi 后可以请求天气 API、NTP 时间服务器或自己的后端服务。请求返回的 JSON 数据用 ArduinoJson 库解析再通过 TFT_eSPI 的setCursor和println显示到屏幕。注意Wi-Fi 扫描、TLS 握手和 JSON 解析都比较吃内存。如果设备不稳定或频繁重启优先检查可用堆内存。建议在loop()里定时打印Serial.printf(Free heap: %lu bytes\n, ESP.getFreeHeap());6.3 批量烧录与命令行接口如果手上有几十块同样的 ESP32-S3 开发板需要批量烧录固件Arduino IDE 逐个上传效率太低。这时可以用 esptool.py 命令行工具。先安装 esptoolpip install esptool然后查看设备端口esptool.py chip_id烧录命令模板具体偏移地址和文件路径需要按你的项目调整esptool.py --chip esp32s3 --port COM3 --baud 460800 write_flash 0x0 merged_firmware.bin如果固件是 Arduino IDE 生成的.bin文件一般会生成在临时目录或者在菜单里开启“编译时显示详细输出”找到路径。普通用户更推荐先在 Arduino IDE 里打开“文件 - 首选项 - 显示详细输出 - 编译”然后从日志中找到固件路径再复制出来用于批量烧录。批量烧录时建议一次只接一块开发板脚本化循环处理。不要在一个 USB 口上插多块板子端口识别会非常混乱。烧录完成后用串口工具或自动化测试脚本验证每块板子屏幕显示是否正常。7. 资源占用与性能观察7.1 刷新率的观察方式ESP32-S3 刷屏最影响体验的指标是刷新率。你需要观察两块数据每秒帧数FPS和从画图到显示输出的延迟。Serial.printf是最直接的观察方式。可以在主循环里统计 FPS输出到串口终端。如果你用 LVGL还可以在my_disp_flush回调里累加 flush 次数每隔一秒计算一次。刷新率受这几个因素影响SPI 频率常见 27MHz、40MHz、80MHz。频率越高刷新越快但信号质量可能下降花屏概率上升。是否启用 DMATFT_eSPI 和 ESP-IDF 都支持 SPI DMADMA 可以显著减少 CPU 参与传输的时间。分辨率320x240 比 240x240 需要的像素多 1.8 倍刷屏压力明显更大。双缓冲LVGL 开启双缓冲后动画更流畅但内存占用翻倍。CPU 频率ESP32-S3 可以设置 240MHz 运行绘图运算会更快。这些因素都是“影响趋势”具体数值需要在你的屏幕上实跑才能确定。不要盲目相信网上随便给的帧率数据同型号屏幕、不同引脚、不同接线长度、不同固件版本结果差异很大。7.2 内存与 PSRAMESP32-S3 内置 SRAM 一般为 512KB 左右实际可用受链表、Wi-Fi 协议栈、TFT_eSPI 缓冲和 LVGL 缓冲影响。不带 PSRAM 的版本跑 LVGL 时内存很紧张。带 PSRAM 的版本可以把 LVGL 的绘制缓冲放到 PSRAM内存余量更充裕。在 Arduino 代码中查看内存的方法Serial.printf(Free heap: %lu\n, ESP.getFreeHeap()); Serial.printf(PSRAM size: %lu\n, ESP.getPsramSize()); Serial.printf(Free PSRAM: %lu\n, ESP.getFreePsram());如果屏幕显示刷新时频繁重启很可能就是内存不足或电源供电不足。此时先降低 LVGL 缓冲、关闭双缓冲、降低分辨率再观察稳定性。7.3 功耗与电源屏幕背光是大功耗来源。一块 2.4 寸 TFT 全亮时背光电流可能达到 20mA 到 60mAESP32-S3 在 Wi-Fi 开启时电流会冲到 300mA 以上。如果 USB 口供电能力不足或者用了劣质数据线刷屏过程中可能出现屏幕闪、自动重启、Wi-Fi 断连。建议使用带有足够余量的 5V/2A 电源并尽量不要同时带动多个大功耗外设。如果要做电池供电的桌面摆件屏幕亮度要降低Wi-Fi 连接要尽量短时工作CPU 在无动画时可降频这样可以显著延长续航。实际续航数据取决于电池容量和固件策略需要实测。8. 常见问题与排查方法问题现象可能原因排查方式解决方案上传固件失败开发板未进入下载模式 / USB 驱动异常 / 端口选错按住 BOOT 键再复位重新插拔 USB查看设备管理器端口正确进入下载模式安装 CP210x 或 CH340 驱动确认端口号屏幕一直黑屏引脚配置错误 / 驱动型号不对 / 背光未开启检查 User_Setup.h、确认屏幕接线、测量背光引脚电压按屏幕型号改驱动宏正确配置 BL 引脚屏幕白屏或花屏SPI 频率过高 / CS、DC 引脚接错 / 供电波动降低 SPI 频率到 10MHz 测试检查接线和电源换更可靠的接线降低 SPI_FREQUENCY显示内容颜色不对驱动型号不匹配 / RGB 顺序错误换驱动宏或调整 TFT_eSPI 的 RGB 顺序宏找到屏幕数据手册确认是 RGB 还是 BGR 顺序中文显示乱码固件中没有中文字库查看字体支持库和字库生成工具生成需要的中文取模字库或用 LVGL 字体转换工具刷屏动画卡顿SPI 频率低 / 未用 DMA / 缓冲不足打印 FPS对比不同配置下的帧率提升 SPI 频率启用 DMA合理设置缓冲Wi-Fi 连接后重启电源不稳或内存不足打印 Free heap检查串口崩溃日志改善供电关闭不用的功能释放内存多次烧录后无法识别串口USB 驱动或开发板处于异常状态重新安装驱动按住 BOOT 键插拔 USB重新进入下载模式必要时用 esptool 擦除 Flash 后重新烧录排查问题时串口日志是第一优先级。很多“屏幕不亮”的问题其实不是屏幕问题而是固件压根没跑起来。上传完成后打开串口监视器波特率设为 115200查看是否输出了启动日志。如果完全没有输出先从 USB 连接和芯片启动开始排查。9. 最佳实践与使用建议如果你准备在 ESP32-S3 上长期做刷屏项目下面这些建议可以帮助你少走弯路。第一先把最小 demo 跑通再优化。不要一上来就接 LVGL、接 Wi-Fi、接触摸。先用 TFT_eSPI 显示纯色和文字确认屏幕驱动和引脚没问题再叠加动画、图片、UI 组件和联网功能。每增加一个模块都单独验证一次这样问题定位会容易很多。第二维护一套固定的引脚配置。不同屏幕模块可能有不同引脚别今天用 GPIO7明天用 GPIO8代码里改来改去很容易出问题。选定一套引脚后把配置文件放到工程目录里用代码注释或 README 记录方便以后复用。第三合理使用文件系统存放资源。屏幕图片、中文字库、UI 背景图这些资源不建议全部塞进固件。用 LittleFS 分区存放既减小固件体积也不需要频繁重新编译固件。上传文件到 LittleFS 可以使用 Arduino 的ESP32 Sketch Data Upload插件或使用单独的上传工具。第四批量烧录要自动化加日志。批量产线场景下用 esptool.py 写一个简单的批处理脚本按序列号或 MAC 地址记录烧录结果。每块板子烧录后接一个自动验证程序检查屏幕显示是否正常。脚本不需要很复杂关键是每一步都有日志方便追溯问题板。第五UI 和业务逻辑分离。虽然嵌入式代码不像后端那样强调高内聚低耦合但至少把屏幕绘制、数据获取、业务逻辑分成独立文件。推荐目录结构project/ ├── src/ │ ├── main.cpp │ ├── display/ │ │ ├── screen_ui.cpp │ │ └── screen_ui.h │ ├── network/ │ │ ├── wifi_manager.cpp │ │ └── api_client.cpp │ └── config/ │ └── pins_config.h这样后期加功能、换屏幕、调 UI 时不会互相牵连。第六注意版权和隐私合规。屏幕使用的字体文件、图标素材、UI 设计稿、图片、音乐波形图都可能涉及版权。如果做开源项目一定要确认素材许可证如果做商用产品最好全部使用自研或明确授权的资源。第七Wi-Fi 凭据和 API Key 管理。不要把自己的 Wi-Fi 密码、MQTT 密钥、API Token 硬编码在公开仓库里。可以把配置放到单独头文件并加入.gitignore或者通过 Web 配网界面动态输入。对外展示屏幕内容时也要避免展示未经授权的个人信息或敏感数据。10. 总结与下一步ESP32-S3 刷屏项目最值得尝试的点是它用很低的成本实现了“带屏幕的联网设备”。无论是 TFT_eSPI 的轻量渲染还是 LVGL 的组件化 UI都能在几十块钱的开发板 屏幕上跑出相当不错的视觉效果。第一次上手时建议优先验证三个功能一是一键烧录流程是否顺畅二是 TFT_eSPI 驱动是否正确点亮屏幕并显示颜色三是通过串口打印确认 FPS 数值是否在合理范围内。把这三个步骤跑通ESP32-S3 刷屏的基本功就算建立了。最容易踩的坑也很集中TFT_eSPI 的 User_Setup.h 配置错误、引脚冲突、供电不足、Wi-Fi 内存溢出。只要按排查表逐个确认大部分问题都能在十分钟内定位。后续可以扩展的方向包括做一个联网天气时钟接一个麦克风模块做音乐频谱显示用 LVGL 做一个简单的菜单系统或者接上传感器数据做成工业采集显示终端。如果在此基础上再把低功耗休眠、OTA 远程升级和批量烧录流程加上这个项目就可以从“玩一玩”变成“能落地”的产品原型了。建议先收藏这篇文章等开发板到手后照着流程跑一遍。屏幕点亮的那一刻你会觉得所有接线和翻车都值了。