GameBoy Emulator on ESP32 + Eink

Lobsters Hottest 工具

摘要

作者成功在M5 Paper S3(ESP32 S3+电子墨水屏)上实现可玩的Gameboy模拟器,通过高刷新率驱动、优化模拟核心和创新的伪复音声音方案,使经典游戏在电子墨水屏上流畅运行。

<p><a href="https://lobste.rs/s/bsojea/gameboy_emulator_on_esp32_eink">Comments</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/01 18:03

TL;DR: 作者成功在M5 Paper S3(ESP32 S3 + 电子墨水屏)上实现了可玩的 Gameboy 模拟器,通过高刷新率驱动、优化模拟核心和创新的声音方案,让经典游戏在电子墨水屏上流畅运行。 ## 硬件准备:M5 Paper S3 与电子墨水屏 最近入手了一台 M5 Paper S3,这是一款 ESP32 S3 开发套件,外形像便携电子阅读器,配有一块 4.7 英寸、960×550 分辨率的电子墨水触摸屏。它和其他小型电子墨水开发套件的最大区别在于屏幕采用了原始行列驱动接口(通常也叫并行接口,但和 LCD 的并行接口不同)。这种接口让我能够绕过常规波形方法,实现高达 60-70 FPS 的刷新率。后面会详细介绍。 几年前我用 FPGA 重写了一个 Gameboy 模拟器,对 Gameboy 架构有一定了解。所以这次想尝试把两者结合——为 Paper S3 制作第一个真正可玩的 Gameboy 模拟器。 ## 高刷新率电子墨水驱动 电子墨水通常被认为速度慢、刷新率低。但如果有合适的硬件,完全可以大幅改善。我之前专门做过一个视频,讲解如何在电子墨水屏上实现 60 Hz 和低延迟(把它变成电子墨水显示器)。关键在于:逐像素处理、实现流水线更新,并配合 FPGA 运行这些操作。这次要看看能否在 ESP32 上做到同样的效果。 幸运的是,Gameboy 只有 160×144 像素。我需要三个像素来表示四种灰度。将抖动图像复制三倍,就能得到 3 倍像素完美缩放和抖动图像。这意味着我只需要在 60 Hz 下处理 160×3×144 像素,远低于 960×540 像素的处理目标。 我为每个像素存储了四个状态位,并与输入缓冲区比较来决定发送到屏幕的内容。利用 LCDIF 和 DMA 实际输出像素,核心可以计算下一批要显示的像素。整个过程在 ESP32 的第二个核心上运行。每次迭代设置约 17 毫秒,从而实现 60 Hz 刷新率。同时实现了双缓冲,让模拟器能够渲染无撕裂的图像。 ## 选择模拟器核心 虽然我之前在 FPGA 上实现过 Gameboy 模拟器,但微控制器上的模拟器已经有很多人做过。这次决定使用现有模拟器,而不是重新发明轮子。尝试了三个核心: - **PeanutGB**:最流行的微控制器模拟器选项,性能不错。 - **WanaCGB**:PeanutGB 的一个分支。我修改了它,将指令读取从 8 位改为 16 位,从而可能节省内存访问时间(主要减少昂贵的模拟内存映射路径的访问次数)。 - **Crankboy**:P Nintendo GB 的一个分支,也是 Playdate 项目的前端。他们为移植做了很多优化,尤其是内存映射路径,性能更好,不需要像 WanaCGB 那样采用不寻常的双读取结构。 在 ESP32 S3 上测试了各种游戏中的模拟速度(性能以相对于 Gameboy 的倍数衡量,1 为相同速度)。最终使用了性能最好的 **Crankboy** 作为模拟核心。通过动态跳帧,它在许多游戏中能达到 100% 的速度,以 30-60 FPS 渲染。实际帧率取决于运行时情况。以当前性能,启用 Gameboy Color 支持还不现实(Gameboy Color 允许 CPU 以双倍速度运行,还差得远)。 ## 声音输出:从 PCM 到伪复音 三个模拟器都使用 Mini GB APU 作为声音模拟引擎。Gameboy 集成了一个四通道可编程声音发生器:两个方波通道、一个采样通道、一个噪声通道,然后混音产生声音输出。在真实 Gameboy 中,部分混音是在模拟域中完成的;通常模拟器最简单的方法就是将所有通道以 32.768 kHz 采样率生成 PCM 样本。 然而 Paper S3 只有一个蜂鸣器(PWM 驱动只能发出单音)。一种已知技术是使用 PWM DAC 输出 PCM 样本:把直流偏置的 PCM 样本直接作为占空比送入 PWM 模块。但 ESP32-S3 的 PWM 模块无法由 DMA 提供数据,要么被拉取,要么在采样率产生中断更新占空比寄存器,都需要预留 CPU 周期。因此我使用了 **I2S 外设的 PDM 输出功能**(原本用于连接接受 PDM 的 DAC 或放大器),给它加一个 RC 滤波器,它就成了一个 DAC。首次实现马上就能工作,但声音太小。 我试了 32 kHz 的 PWM,效果没有明显改善,所以坚持用 PDM。但觉得还不够:有声音但如果很多情况下听不清就没多大用。要发出足够响亮的蜂鸣声,基本上只能靠单音——这正是蜂鸣器设计的初衷。那干脆就用单音?Gameboy 声音发生器的前两个通道是纯方波,采样通道通常也可以用方波近似(虽然牵强),噪声通道也可以改用方波。这样就变成了四个方波通道。 虽然蜂鸣器只能发出单音,但可以快速切换多个声音通道,产生**伪复音**效果——这在只有单个蜂鸣器的平台上(如没有声卡的 IBM 兼容 PC)是一种广为人知的技术。 实际效果:“既出奇地好又差劲”。好的方面是声音响亮清晰,游戏音乐仍然容易辨认,整体听起来挺有意思。差的方面是 Gameboy 本来不应该这样响。为了确保不占用模拟器有限的 CPU 资源,我把整个 PSG 模拟放在了第二个核心上。根据性能分析,声音模拟每帧最多运行约 2 毫秒,具体取决于声音复杂程度。 ## 输入与控制 输入方面比较直接:M5Paper S3 有触摸屏,所以很自然地在屏幕上显示一个触摸屏手柄。不过对于硬核玩家,我又添加了**实验性的蓝牙低功耗手柄支持**。它没有完全实现 HID 协议,只是猜测了报告格式,因此兼容性很差。我觉得作为演示可以接受,但肯定有改进空间。 ## 额外功能:快速存档 我还添加了**快速保存和快速加载**功能,本质上保存和加载整个模拟 Gameboy 的状态,允许快速创建检查点并返回到检查点。这样下次关闭 Paper S3 后,再次拿起时只需快速加载,就能立即回到游戏。 ## 总结与后续 最终结果为 M5 Paper S3 提供了一个相当不错且可用的 Gameboy 模拟器。遗憾的是 Paper S3 已经停产,希望很快能有合适的替代产品。如果你想尝试这个固件,可以在 M5 burner 上找到。 **Source:** https://www.youtube.com/watch?v=oPbOK90aJEo

相似文章

M5Stack PaperColor

Product Hunt

M5Stack PaperColor 是一款由 ESP32 驱动的 4 英寸彩色 E-ink 开发板,具备音频 I/O 能力。