·3 分钟阅读
ESP32 上跑 NES 模拟器:移植与踩坑记录
记录在 ESP32-S3 掌机上移植 NES 模拟器的全过程:nofrendo 核心的库化改造、WiFi 传 ROM、屏幕驱动、以及崩溃排查。
ESP32嵌入式模拟器
ESP32 上跑 NES 模拟器:移植与踩坑记录
在资源受限的 ESP32-S3 上跑 NES 模拟器,是把 nofrendo 模拟器核心搬上 Arduino 框架的完整过程。这篇记录整个移植链路和踩过的坑。
系统架构
flowchart LR
WIFI[WiFi AP + WebServer] -->|上传 ROM| SPIFFS[SPIFFS 存储]
SPIFFS --> NOFRENDO[nofrendo 模拟核心]
NOFRENDO --> DISP[GC9A01 屏显]
BT[BLE 手柄] --> INPUT[按键输入]
INPUT --> NOFRENDO
核心改造:nofrendo 库化
nofrendo 是 ESP-IDF 官方的 NES 模拟器参考实现,但它是按 ESP-IDF 组件组织的。要在 Arduino 框架里用,第一步是把它的全部 C 实现(65 个文件)搬到 lib/nofrendo/ 下,作为 Arduino 库编译。
这一步解决的是"两个框架的组织差异"——nofrendo 的 .c 文件之间用相对 include,搬进 lib 后编译顺序和头文件路径都要重新理顺。
WiFi 传 ROM:摆脱反复烧录
每次改 ROM 都重新烧录太痛苦,所以加了一个 WebServer:
flowchart LR
PHONE[手机/电脑浏览器] -->|连接热点| AP[ESP32 WiFi AP]
AP -->|上传 .nes 文件| SPIFFS[SPIFFS]
SPIFFS -->|/go 路由| RUN[启动模拟]
- 板子开机开热点,浏览器连上去上传 ROM 到 SPIFFS
/go路由直接启动 nofrendo 跑 ROM- 早期用
-nosound无声版,先把画面跑通
屏幕驱动:USE_HSPI_PORT 是命门
TFT_eSPI 驱动 GC9A01 屏,最关键的配置是端口选择:
#define GC9A01_DRIVER
#define USE_HSPI_PORT // 必须!否则崩溃 StoreProhibited
#define TFT_WIDTH 128
#define TFT_HEIGHT 128
#define SPI_FREQUENCY 40000000
不用 USE_HSPI_PORT 会直接 StoreProhibited 崩溃——这是花了不少时间才定位到的。
屏幕的引脚映射(T-QT Pro):
| 功能 | GPIO |
|---|---|
| MOSI | GPIO2 |
| SCLK | GPIO3 |
| CS | GPIO5 |
| DC | GPIO6 |
| RST | GPIO1 |
| 背光 BL | GPIO10 |
按键与输入
T-QT Pro 只有两个实体按键,映射成 NES 的两个键:
| 按键 | GPIO | NES 键 |
|---|---|---|
| 左键 SELECT | GPIO0 | A(确认) |
| 右键 START | GPIO47 | START |
BLE 手柄后来也接上了(ESP32-BLE-Gamepad 库),但早期调试时是注释状态,先用两个实体键跑通流程。
踩过的坑
| 坑 | 现象 | 解决 |
|---|---|---|
| SPI 端口选错 | 一进显示初始化就 StoreProhibited 崩溃 | 强制 USE_HSPI_PORT |
| SPIFFS 数据损坏 | 上传 ROM 后崩溃,反复起不来 | 全擦 SPIFFS 后恢复正常 |
| USB 挂死 | 崩溃后串口连不上 | 重新上电 / 进下载模式 |
| ROM 太大 | 大 ROM 放不进 SPIFFS | 先用小的(如魂斗罗 168KB)跑通 |
最终固件编出来约 853KB,能在 128×128 的屏上稳定跑起来。这个项目最深的体会是:在受限硬件上,"跑通"本身就是最大的坎,每个功能都要在性能、内存、体验之间反复权衡。