ESP32-S3口袋终端实战:从PlatformIO配置到LVGL UI与USB HID

发布时间:2026/10/9 3:30:24
ESP32-S3口袋终端实战:从PlatformIO配置到LVGL UI与USB HID
前一阵我拿到一块 ESP32-S3 Super Mini 核心板规格是 WROOM-1 模块、16MB Flash、8MB PSRAM也就是市面上常说的 N16R8。我的目标很明确用它在办公桌上放一台能随时掏出来把玩的口袋终端——一块全彩小屏幕能显示时间天气能当 USB 键盘用外观还得说得过去不能像实验室里飞线的裸板。整个项目从选板、配 PlatformIO、确认驱动到接线、屏幕渲染、外壳设计一路踩了不少坑。这篇文章把这些过程完整复盘一遍给同样打算用 ESP32-S3 做口袋终端、桌面小屏组件或便携输入设备的朋友一条可复制的路线。1. 选型复盘为什么口袋终端最终落在 ESP32-S3 上1.1 口袋终端到底需要什么算力、内存、屏幕还是生态做终端之前我先列了一份需求清单要有全彩屏幕、UI 要流畅、网络要能拿到时间和天气、最好还能直接用 USB 口变成键盘。更重要的是整套系统要能在电池下跑住不能一开屏幕就电流爆炸。按这个清单去选型纯 MCU 里其实没几个选择。ESP32 经典款有 WiFi 和蓝牙但那个时代的普通版本不带原生 USB OTG也没配 PSRAM 的完整生态做 UI 时内存捉襟见肘。RP2040 的双核 133MHz 够用USB PIO 也很好玩但缺 WiFi 和蓝牙天气和时间这种功能就得外挂模块口袋终端的体积和功耗都撑不住。ESP32-C3 便宜、低功耗但单核 160MHz 在 240x240 全彩屏的刷新场景下明显吃力真跑过 LVGL 渲染就知道差距。真正让我定下 ESP32-S3 的是它在全套功能上的自给自足双核 Xtensa LX7 跑 240MHz原生 USB-OTG 可以绕过串口芯片直接变 HID 键盘再加上带向量指令的 AI 加速能力——哪怕只是用来做触摸滤波、数据拟合这类小算法也绰绰有余。这些能力叠在一起正好是口袋终端什么都要有却什么都不想外挂的典型场景。1.2 N16R8 的168组合在项目里意味着什么N16R8 这个名字要拆开看N 代表 NOR Flash16 是 16MB FlashR8 是 8MB Octal PSRAM。很多人觉得 Flash 大只是能装更多固件其实对终端类项目16MB 真正的价值是能把整套 UI 资源直接放进文件系统。我做过一个对比一个 40px 的中文字体文件包含常用 3500 字不做压缩大概 1MB 出头一张 240x240 的 JPEG 背景图约 30-60KB如果把开机动画做成 20 帧 PNG 序列又要占掉几百 KB。普通 4MB Flash 的板子塞完固件就剩下一两 MB根本不敢放这些资源。N16R8 的 16MB 让我在设计资源时完全不用抠门甚至可以把 LVGL 的字体缓存和图片缓存都做成预置式不需要每次开机从网络拉。8MB PSRAM 更是 UI 流畅度的命根子。240x240 全彩屏一帧 RGB565 数据是 115200 字节约 113KB。不用 PSRAM 的话内部 SRAM 往往只有 300-400KB 可用还要分给 WiFi 协议栈、任务栈、LVGL 堆最后能留给显示缓冲的也就几 KB刷新动画时卡到怀疑人生。有了 PSRAM我可以直接放两个 113KB 的全屏缓冲做双缓冲甚至再留几 MB 给 LVGL 做动态内存池渲染时随便创建对象、销毁对象几乎不用担心内存碎片。1.3 和同类芯片摆在一起怎么选如果你的项目不是口袋终端而是纯 USB 小键盘或超低功耗传感器那选 S3 确实有点大材小用。但从终端这个品类看横向对比是这样的对比项ESP32-S3 Super MiniRP2040ESP32-C3 SuperMini经典 ESP32双核主频双核 240MHz双核 133MHz单核 160MHz双核 240MHzFlash / PSRAM16MB / 可选 8MB2MB / 无4MB / 无4-16MB / 无原生 USB OTG有有无无WiFi / 蓝牙WiFi4 BLE5无WiFi4 BLE5WiFi4 经典蓝牙UI 渲染上限高配合 PSRAM 流畅跑 LVGL中低中低典型售价20-30 元20 元15 元25 元表格看下来就很清楚S3 在屏幕好 网络好 USB 好用这个三维需求里几乎没有对手。做终端这件事选 S3 不是最便宜的方案但一定是最省力的方案。2. PlatformIO 选板与驱动确认拿到 Super Mini 之后的第一个坎2.1 开发框架为什么选 PlatformIO 而不是 Arduino IDE开发环境我直接选了 PlatformIO原因很现实管理依赖库版本、多环境编译、命令行烧录都更顺手。Arduino IDE 插个库倒是方便但 LVGL、LovyanGFX、ArduinoJson 这些库一多起来版本冲突和全局包含的坑能让人崩溃。PlatformIO 默认每个项目隔离依赖platformio.ini里写清楚就能复现环境换台电脑也不用重新装一堆东西。对于 ESP32-S3 这种比较新的芯片PlatformIO 的 espressif32 平台更新也快。你只需要装好 VS Code 的 PlatformIO 插件然后新建项目时选择 Espressif ESP32-S3 框架即可。注意如果选芯片型号时找不到 Super Mini 这种第三方板子不要慌这是正常的看下一节。2.2 板载 N16R8 在 board 字段里的正确打开方式很多人在 PlatformIO 的 boards 列表里翻半天想找一个叫 esp32-s3-super-mini 的选项结果发现根本没有。实际上Super Mini 不是官方开发板它是第三方厂商把 WROOM-1 模块的所有引脚引出来的最小系统板。PlatformIO 里最接近的官方板型是esp32-s3-devkitc-1因为它的核心就是同一个 WROOM-1 模块引脚定义和启动逻辑一致。但这里有一个隐藏问题esp32-s3-devkitc-1的默认配置是 8MB Flash、2MB PSRAM而你的 N16R8 是 16MB Flash、8MB PSRAM。如果不做覆盖程序编译出来会用错内存布局轻则 PSRAM 未启用重则 Flash 分区不对、启动崩溃。我实际用的platformio.ini配置长这样[env:esp32-s3-super-mini] platform espressif32 board esp32-s3-devkitc-1 framework arduino board_build.arduino.variant esp32s3 board_build.mcu esp32s3 board_build.f_cpu 240000000L board_build.flash_mode qio board_build.flash_size 16MB board_build.psram_type opi board_build.arduino.memory_type qio_opi build_flags -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE1 -DARDUINO_USB_CDC_ON_BOOT1 monitor_speed 115200这里最关键的两行是flash_size 16MB和psram_type opi。opi表示 Octal PSRAM对应 N16R8 的 8MB PSRAM。如果你拿到的板子是 N8R2 或 N8R8也要看准是 OPI 还是 QPI 封装买得不一致时编译能过但运行会花屏或死机。如果编译日志里出现Flash size: 8.00MB或PSRAM: 2MB的字样说明你的 PlatformIO 版本或 espressif32 平台太老不认识这些覆盖字段。解决办法是升级 platformio 和平台包不要自己硬改 json 配置文件效率太低。2.3 三步确认串口驱动是否装好驱动问题看起来是小事但在这块板子上发生率不低而且报错五花八门。Super Mini 常用的 USB 转串口芯片是 CH343P 或 CP2102不同批次不一样。我手里这块是 CH343P 方案。确认方法分三步插上板子打开系统设备管理器。Windows 下看端口 (COM 和 LPT)如果出现类似USB Serial (COM3)或USB-serial CH343 (COM3)说明驱动已经正常。如果这个分类下什么都没有却在其他设备里看到一个带黄色感叹号的USB Serial那就是驱动没装。Linux 下更简单插拔前后分别执行lsusb如果能看到 WCH沁恒或 Silicon Labs 的条目就说明识别到了。烧录端口通常是/dev/ttyUSB0或/dev/ttyACM0。如果你在设备管理器里看到的是一大串USB Composite Device多半是板子开启了原生 USB CDC 模式后的正常现象这时会在串口列表里多出一个额外的 COM 口。要区分哪个是 CH343 转出来的 UART、哪个是原生 USB就拔掉板子看哪个 COM 消失再插上看哪个恢复多出来的另一个就是原生 USB 设备。2.4 第一次烧录常见报错的排查顺序第一次烧录最容易遇到两个报错。第一个是Timed out waiting for packet content这是 ESP32-S3 没有进入下载模式。解决方法很简单按住板子上标着 BOOT 的那个按键对应 GPIO0再插 USB保持按住直到烧录工具开始连接再松开。Super Mini 没有自动下载的 reset 时序电路很多批次都必须手动进入下载模式这不是你配置错了是板子设计如此。第二个常见报错是Can not open port COMxx。多半是串口被其他软件占用了比如你开着串口监视器再点烧录或者驱动识别但被系统分配给了休眠后的虚拟设备。关掉所有占用程序重新插拔 USB 一般就能解决。如果还不行就同时按住 BOOT 和 RST 键松 RST 再松 BOOT强制重新枚举。还有一个容易忽略的坑供电不稳会导致烧录中途报Brownout detector was triggered。这种情况经常是 USB 线质量太差或插在扩展坞上导致电压跌落。直接插电脑主板后面的 USB 口换一根短粗的数据线问题立刻消失。我一开始用一根 1.5 米的旧安卓线烧录十次失败了七次换成 20cm 的短线后一次就过。3. Super Mini 引脚全画像哪些针能碰哪些针会坑3.1 一份说人话的引脚速查表Super Mini 的丝印因批次不同会有差异但大体结构相似。我建议拿到板子后先拍一张高清单引脚照片对照下面的速查表逐个确认。这是我这块 N16R8 版本实测可用的引脚分类引脚实际用途与坑点供电5V / 3V3 / G5V 是 Type-C 的 VBUS 引出可以从这里给屏幕供电3V3 是板载稳压输出别拿它反灌大电流下载/启动GPIO0默认上拉有 BOOT 按钮。按住再上电进入下载模式日常别接下拉电阻原生 USBGPIO19 (D-) / GPIO20 (D)ESP32-S3 自带 USB-OTG做 HID 键鼠时必须用这里。注意它和 UART 是两套独立通道串口日志GPIO43 (TX0) / GPIO44 (RX0)UART0接板载 CH343。默认调试日志都从这里输出模拟输入GPIO1 - GPIO10对应 ADC1。ADC2 的引脚在 WiFi 开启时会不稳定所以模拟信号优先用 ADC1自由 IOGPIO15、GPIO16、GPIO17、GPIO18、GPIO21、GPIO47无特殊复用几乎可以随便用。屏幕、按键、传感器放这里最省心板载 RGBGPIO48被板上 WS2812 占用。如果你不用那个灯就把它从代码里禁用省一点休眠电流避让区GPIO3、GPIO45、GPIO46上电时的 strapping 引脚静默电平有讲究接外部元件容易导致无法启动。实际项目里我基本不动它们这张表里没有 GPIO33-37因为 N16R8 模块的 Octal PSRAM 总线占用了大部分与这些编号相关的内部引脚就算板上丝印标出来了也不一定能当普通 IO 用。如果你非要确认一个引脚是否可用最稳的办法是写一个简单的 GPIO toggle 程序用万用表测量对应排针的电平变化。3.2 三个最容易踩的引脚坑第一个坑是 ADC2 和 WiFi 冲突。ESP32 系列老传统ESP32-S3 也不例外WiFi 开启后ADC2 的采样会变得极其不可靠连续采集到的数值可能忽高忽低。很多人想用电池电压监测或模拟传感器随手接了 GPIO11-20 这些 ADC2 引脚结果发现数值漂得像心电图最后排查半天才发现是 WiFi 在作祟。解决办法只有一个模拟信号全部走 GPIO1-10 的 ADC1不要心存侥幸。第二个坑是 GPIO0 的用法。它既是普通数字 IO又是下载模式选择脚板上还有上拉电阻和按钮。有些人为了省引脚把 GPIO0 接成外部按键的下拉方案结果每次上电都进下载模式或者程序跑着跑着莫名其妙 restart。我的建议是 GPIO0 永远留给 BOOT 按钮不要参与业务逻辑。第三个坑是 GPIO19/20 原生 USB 引脚和其他外设的复用矛盾。如果你既想用原生 USB 做键盘又想把 GPIO19/20 接成普通 IO两者会打架。开机初始化时 USB 外设会占用这两个引脚的 PHY 控制权强行当普通 IO 用会导致枚举失败或者无法烧录。我在项目里直接把 GPIO19/20 划给了 USB HID完全放弃当 IO 用反而省事。3.3 屏幕、按键、电池的引脚规划基于上面的约束我给这台口袋终端定的接线规划如下供参考外设引脚说明TFT_SCLKGPIO12屏幕 SPI 时钟TFT_MOSIGPIO11屏幕 SPI 数据TFT_DCGPIO10数据/命令选择TFT_CSGPIO9片选TFT_RSTGPIO15屏幕复位TFT_BLGPIO16背光 PWM按键 AGPIO17主菜单/确认按键 BGPIO18返回/切换电池电压检测GPIO1通过电阻分压接 ADC1这套规划的核心思路是屏幕优先占满 SPI 总线按键放在自由 IO 上模拟检测只用 ADC1原生 USB 独占 GPIO19/20。这样调整任何一路外设都不会影响其他模块排查问题也方便。4. 好看的底气240x240 全彩屏与渲染管线调优4.1 屏幕选型与接线走向超好看这个评价一半功劳得给屏幕。市面上常见的小屏幕方案里我最终选了 1.3 寸 ST7789 驱动的 240x240 IPS 全彩屏。理由很简单视角宽、色彩饱和度好、价格只要 15-20 块资料也最多。GC9A01 那种圆屏适合做手表表盘但显示常规文字信息时会裁掉四个角不太适合终端的形态。SSD1306 的 OLED 倒是省电可 128x64 单色还想做好看的 UI几乎不可能字一多就是灾难。接线按照 3.3 节的表格来我这里用 LovyanGFX 库写初始化。LovyanGFX 相对 TFT_eSPI 的优势是支持面板自动配置、DMA 传输、PWM 背光控制而且对 ESP32-S3 的 Arduino-ESP32 原生 API 兼容性很好不需要像 TFT_eSPI 那样手动改User_Setup.h那一大堆宏对小白友好很多。4.2 LovyanGFX 跑通 ST7789 的最小代码#include LovyanGFX.hpp class LGFX : public lgfx::LGFX_Device { lgfx::Panel_ST7789 _panel; lgfx::Bus_SPI _bus; lgfx::Light_PWM _light; public: LGFX(void) { auto cfg _bus.config(); cfg.spi_host SPI2_HOST; cfg.spi_mode 0; cfg.freq_write 80000000; // 80MHz SPI 写时钟 cfg.freq_read 16000000; cfg.pin_sclk 12; cfg.pin_mosi 11; cfg.pin_miso -1; // ST7789 只写不读 cfg.pin_dc 10; _bus.config(cfg); _panel.setBus(_bus); auto pcfg _panel.config(); pcfg.pin_cs 9; pcfg.pin_rst 15; pcfg.pin_busy -1; pcfg.memory_width 240; pcfg.memory_height 240; pcfg.panel_width 240; pcfg.panel_height 240; pcfg.offset_x 0; pcfg.offset_y 0; pcfg.invert true; // ST7789 通常需要反色 pcfg.rgb_order false; _panel.config(pcfg); _panel.setRotation(0); auto lcfg _light.config(); lcfg.pin_bl 16; lcfg.invert false; lcfg.freq 12000; lcfg.pwm_channel 0; _light.config(lcfg); _panel.setLight(_light); setPanel(_panel); } }; LGFX display; void setup() { display.init(); display.setBrightness(120); display.fillScreen(TFT_BLACK); display.setTextColor(TFT_WHITE, TFT_BLACK); display.drawString(Pocket Terminal, 40, 100); } void loop() { }这段代码跑通后屏幕上会出现一行白字。如果白字变成彩色的花屏或者底色发黄多半是invert或rgb_order设置不对逐个切换试一次。如果屏幕全亮、白花花的可能是复位引脚接触不良或背光 PWM 频率太低把freq调到 12000 以上基本能解决。4.3 从能显示到好看字体、配色、卡片布局与帧率能显示字符和好看之间差的是字体、配色和动画三件事。字体方面默认的字体都是点阵感很重的小字体用在这种终端上第一眼就输了。我推荐把开源字体转成 LVGL 格式使用比如中文用得意黑或OPPO Sans数字用JetBrains Mono或DIN 1451这类几何感强的。转换可以用 LVGL 官方的字体转换工具上传 TTF 文件指定字号、颜色格式和需要包含的字符范围生成.c文件后放进工程。注意中文全角字体体积很大最好只转你真正会用到的字符子集我用的是数字 常用标点 100 个天气相关汉字一个 48px 字体也才 200 多 KB放到 16MB Flash 里毫无压力。配色方面我给这台终端定了一个深色系方案背景用深蓝黑#0A0E1A卡片用稍亮的#151B2D主强调色是青色#00E5FF警告色用珊瑚红#FF6B6B正文用偏冷的#E8EDF5。深色背景在高亮度 IPS 屏上非常吃香既掩盖了 IPS 屏边缘漏光的毛病又让青色高亮显得特别打眼。一屏里强调色最多三种再多就乱了。布局和动画方面我用 LVGL 8.3 来做。时间区域用大号数字放在屏幕中心偏左右侧用一个环形进度lv_arc表示秒数流动顶部放日期和星期小卡片底部放天气温度和网速状态。页面切换用lv_anim做 150ms 的水平滑动卡片出现时做轻微放大回弹交互感觉立刻不一样。这里给出 LVGL 的 PSRAM 内存池配置片段放在lv_conf.h中#define LV_MEM_CUSTOM 1 #define LV_MEM_CUSTOM_INCLUDE esp32-hal-psram.h #define LV_MEM_CUSTOM_ALLOC ps_malloc #define LV_MEM_CUSTOM_FREE free这样 LVGL 的堆就从内部 SRAM 挪到 PSRAM 上了创建大量对象、图片缓存时几乎不用担心内存碎片。4.4 PSRAM 在这里为什么是救命稻草很多人不理解双缓冲和 PSRAM 的关系。240x240 的 RGB565 一帧是 113KB如果做双缓冲就是 226KB。ESP32-S3 的内部 SRAM 虽然标称 512KB但 Arduino 核心、WiFi 协议栈、蓝牙协议栈、LVGL 默认内存池都要占用剩下能自由分配的可能不到 200KB。没有 PSRAM你只能用一个很小的局部刷新缓冲跑动画时屏幕一半更新一半撕裂体验非常糟糕。有了 8MB PSRAM我用两个全屏缓冲加一个 32KB 字体缓存LVGL 自带的内存池再分配 1MB总共才用了不到 1.3MB。极端的你可以再分配 4MB 做 GIF 动画或壁纸轮播但这在我的项目里没太大必要。渲染速度方面SPI 写时钟 80MHz 时刷满一屏 113KB 的理论时间约 11ms实际加上协议开销大概 14-16ms。LVGL 的刷新回调只要做到DMA 发送完毕就立刻flush_ready()240x240 这个分辨率跑到 50-60fps 是毫无悬念的。如果发现有人用 40MHz SPI 还抱怨掉帧那瓶颈几乎都出现在缓冲不足或没有用 DMA 上。5. 从裸板到口袋供电、按键与外壳的落地细节5.1 电池与充电别直接用 3.7V 怼 3.3V口袋终端当然不能一直插线所以电池方案必须考虑。最直接的建议不要拿 3.7V 锂电池直接接到 3V3 排针上。电池满电 4.2V放电平台 3.7V给 3.3V LDO 供电时压差太小有些 LDO 会直接进入 dropout导致屏幕闪烁、WiFi 掉线。我自己用的方案是锂电池接到 TP4056 充电保护一体板电池输出再接一个低功耗升压模块升到 5V然后把 5V 接到 Super Mini 的 5V 排针由板载稳压器再转成 3.3V 给核心板供电。屏幕的背光电源也是从 5V 取单独给 PWM 控制脚。整套系统在 240MHz 全速亮屏的状态下峰值电流大概 180-220mA如果让终端每 30 秒刷一次时间其余时间进入light_sleep平均电流能压到 30mA 以下配一个 800mAh 的软包电池待机一整天问题不大。有个功率计算的技巧可以分享LED 背光全亮时整机功耗的一半以上其实是背光。所以我在 UI 里加了一个夜间自动降亮度的逻辑——根据 NTP 拿到的小时数22 点到早上 8 点之间把 PWM 亮度自动降到 30%。这一条改动续航体感直接翻倍比任何电源管理库都来得实在。5.2 按键方案从排针到微动开关终端至少需要两个实体按键一个向前确认一个返回。我从排针上引出两根线到两个微动开关再把微动开关固定在打印外壳的按键孔位上。接线时注意按键的一端接 GPIO另一端接 GNDGPIO 内部开启INPUT_PULLUP这样按下时读到低电平符合常见消抖逻辑。按键消抖不要想当然地在loop里写delay(20)那会卡住整个事件循环。正确做法是用 LVGL 自带的输入设备驱动的回调方式把 GPIO 电平变化上报给 LVGL由它的组控制逻辑统一处理。如果你不用 LVGL也建议用一个 20ms 的定时器扫描按键状态机而不是直接阻塞延时否则 WiFi 和 USB 的响应时间会变得很糟糕。5.3 3D 打印外壳的尺寸与开孔要点外壳我分了前后壳两片中间用 M2 铜柱固定。测量时别只量 PCB 长宽还要把排针高度、Type-C 接口突出量一起算进去。我踩过最典型的坑是只按 PCB 尺寸建模结果 Type-C 插头被后壳顶住线插不进去。开孔位置有四个关键点Type-C 口要避开板载 CH343 那一侧露出 C 口的焊盘BOOT 键单独开一个小圆孔方便用牙签戳进去进入下载模式屏幕窗口比显示区每边多留 0.5mm 的余量否则贴紧后边缘会裁掉像素电池仓做在 PCB 背面用一块 3mm 厚的 EVA 泡棉垫住电池防止短路。打印材料我用的是 PETG耐温和韧性都远好于 PLA而且翘曲比 ABS 小。屏幕用双面胶粘在窗口内圈前面再贴一张手机膜改的亚克力保护片。成品效果非常接近商品拿在手里没有任何裸露的飞线这才对得起超好看三个字。6. 终端不只是屏幕NTP 天气、USB-HID 和几个实用场景6.1 NTP 时间和天气两个免费接口搞定口袋终端最基础的功能就是显示时间和天气。时间获取不用多说ESP32-S3 连 WiFi 后用configTime(8 * 3600, 0, ntp.aliyun.com, pool.ntp.org)即可我这里用了阿里云的 NTP 服务器国内网络环境下同步速度比默认的 pool 快很多。天气我用的是 Open-Meteo 的免费接口不需要 API Key请求一次返回 JSONhttps://api.open-meteo.com/v1/forecast?latitude39.9longitude116.4current_weathertrue代码里用 ArduinoJson 解析current_weather.temperature和current_weather.windspeed再加上is_day字段判断白天黑夜切换屏幕的主题色。这个接口唯一的坑是经纬度精确到小数点后一位即可不要传太多小数否则会被判定为滥用请求。为了省电我没让终端持续轮询天气。策略是开机后第一次同步之后每隔 30 分钟请求一次请求期间在 UI 右上角显示一个很小的同步图标。时间则依赖 NTP 同步后的struct tm主板自带 RTC 在睡眠期间也能保持走时不需要额外外挂 RTC 芯片。6.2 原生 USB 口变身 HID 键盘/鼠标ESP32-S3 最让我满意的点是原生 USB OTG。只要在platformio.ini里开了ARDUINO_USB_MODE1和ARDUINO_USB_CDC_ON_BOOT1代码里就能直接用 USB HID 键盘类#include USB.h #include USBHIDKeyboard.h USBHIDKeyboard Keyboard; void setup() { USB.begin(); Keyboard.begin(); } void loop() { Keyboard.press(a); Keyboard.release(a); delay(2000); }编译烧录后把 Type-C 线插到电脑系统会识别出一个 USB 键盘。这个功能的实用场景非常多我可以让终端显示一个随机密码生成器按一下按键就把密码打出来也可以做成快捷短语板一键输出常用回复甚至可以把它变成一个演示翻页器按按键发左右方向键PPT 翻页完全脱离遥控器。需要提醒的是一旦启用原生 USBUART0 的日志就不再是唯一调试通道了。我做了一个小配置开发时用-DARDUINO_USB_CDC_ON_BOOT0让日志走串口发布固件前再改成 1 开启 HID。这个开关在platformio.ini里改一行就行非常方便。6.3 实测体会哪些地方最值得复刻把整个项目跑通之后我复盘出三条最有价值的经验。第一屏幕刷新卡顿几乎都不是 CPU 不行而是缓冲配置不对。只要把 PSRAM 打开、双缓冲开起来、SPI 频率上到 80MHz240x240 的小屏在 ESP32-S3 上流畅得不像 MCU。第二外壳和供电的工程感决定了这个项目是玩具还是成品。很多人在裸板上调通 UI 就觉得完工了结果电池没法装、按键没手感、Type-C 插不进外壳整个项目搁置。我强烈建议在第一天就把电池、屏幕、按键的物理大小列成清单一边写代码一边想外壳最后出成品的概率会高很多。第三功能不要太贪多。最初我计划在这台终端上做蓝牙、WiFi、TF 卡、传感器、USB 键盘全都支持结果引脚规划乱成一团。后来我砍到屏幕 两键 电池 USB HID WiFi 天气五个核心点整个项目才真正完工。电子项目最怕的不是做不出来而是什么都想要最后什么都没做完。说回到开头那句评价ESP32-S3 Super Mini 这块 20 多块的板子其实已经把口袋终端的硬件天赋全部给足了剩下的活全在驱动、渲染、供电和外壳这些看起来不炫的细节里。你可以先从点亮屏幕开始一步一步来。如果在这条路上也踩到类似的坑这篇文章里的排查顺序和引脚规划应该能帮你少走不少弯路。