GameBoy on E-Paper ESP32 at 30FPS [video]
摘要
作者为带专用显示控制器的 ESP32 墨水屏开发板自写显示驱动,通过缩短波形、逐像素驱动预算和灰度校准,把 Game Boy 模拟器从不到 2FPS 提升到约 30FPS,并实现了不依赖抖动的真正四灰度级输出。
暂无内容
查看缓存全文
缓存时间: 2026/10/11 06:38
# 用 ESP32 + 墨水屏跑 Game Boy 模拟器:从 2FPS 到 30FPS 的驱动改造
TL;DR:作者为带专用显示控制器的 ESP32 墨水屏开发板自写驱动,通过缩短波形、逐像素驱动预算和灰度校准,把 Game Boy 模拟器从不到 2 帧每秒提升到约 30 帧每秒,并实现了不依赖抖动的真正四灰度级输出。
## 最终效果
这是一块搭载小型墨水屏的 ESP32 开发板,通过作者自写的显示驱动,以大约 30 帧每秒的速度运行 Game Boy 模拟器。而使用开发板自带驱动的效果,理论上应该大约有 3 帧每秒,但实际表现连 2 帧都不到。
那么,是如何把它从幻灯片级别的体验变成现在这样的呢?
## 墨水屏为什么慢
与 LCD 或 OLED 等传统显示器不同,墨水屏通过带电的黑白颜料粒子来物理存储图像。通过改变电场,屏幕推动颜料粒子朝向或远离表面,最终呈现出黑色或白色的像素。
通常需要一系列称为"波形"的电脉冲,让颜料先来回运动,最后再将其驱动到新图像所需的状态。这样可以产生干净的画面,不会残留太多上一帧的图像。但这个过程非常耗时。
WaveShare 规格书上标明局部刷新大约需要 300 毫秒。这对游戏来说远远不够快。
## 为什么在墨水屏上玩 Game Boy
作者当时正在基于这块开发板做一个个人设备,打算做一个有 Anki 卡片、语音备忘录,也许还能当小型电子阅读器的东西。但 UI——比如翻菜单——慢得令人痛苦。起初他觉得这就是墨水屏,它就是慢。
但他知道快速的墨水屏是可能的,因为他之前看过一个视频:Zeph Ray 已经做过 Paperboy——一个运行在墨水屏上的 Game Boy 模拟器。
不过 Paperboy 使用的显示硬件完全不同。它的面板直接暴露了原始的 Roland Column 接口,而作者的板子有一个专用的显示控制器,由它实际驱动面板。他无法绕过那个控制器,这意味着不能简单地移植 Paperboy 的显示驱动,必须为这个控制器自建一个驱动。
## 第一步:缩短波形
首先需要看看这块显示控制器最快能跑多快。
作者尝试的第一件事是缩短波形。标准波形会花费数百毫秒来驱动颜料以产生干净的图像,于是他逐步缩短这个序列,测试能去掉多少而不让图像看起来不对。
出乎意料的是,能去掉的相当多。最终他把波形缩减到了只有两个驱动步骤,从大约 17 步降下来。加上前后的处理时间,一次完整更新大约需要 50 毫秒,接近每秒 20 次更新。
只有一个问题:图像看起来非常糟糕。
### 两个副作用
**残影**:那些本该消失的图像部分仍然清晰可见。通过减少波形产生的脉冲数量,每个像素过渡到目标灰度所需的时间被缩短了。
**整屏变灰**:运行较长时间后,整个屏幕会逐渐变得越来越灰。
## 第二步:逐像素驱动预算
这时作者回到了 Paperboy 的思路:不再每次更新都驱动整个面板,而是为每个像素维护状态,只驱动真正需要驱动的像素。
当一个像素发生变化时,它会获得一个**驱动预算**——一个数值。每次驱动器向该像素发送一个脉冲,剩余预算就减少;当它已经收到足够的驱动时,就停止驱动。
这让静止区域变得干净多了,并显著减少了残影。作者发现大约 **6 到 8 次驱动的预算**就足以完成一次从全黑到白的完整过渡。
仍然存在一个权衡:驱动次数越多,最终像素越清晰,但运动物体的过渡时间更长,看起来更模糊。
到了这个阶段,30 帧每秒开始变得可行。于是作者开始削减周围的处理经过几轮长时间的反复调试,扫描现在大约每 **33 毫秒**开始一次。在实际游戏体验中,效果出乎意料地好。
## 第三步:真正的四灰度级
Game Boy 有四个灰度级,此前一直使用抖动(dithering)来用黑白像素模拟它们。Paperboy 也做了类似的事情,但它的面板分辨率高得多。
Game Boy 的图像只有 **160x144 像素**,所以 Paperboy 可以用多个物理屏幕像素来表示一个 Game Boy 像素。而作者的屏幕只有 **200x200**,每个 Game Boy 像素只能分到一个显示像素。所以虽然抖动确实有效,但那些图案清晰可见,效果远不如真正的灰度。
### 意外发现的灰度
在与残影问题搏斗时,作者注意到了一个有趣的现象:屏幕已经在产生灰色了,只是纯属意外——颜料不会瞬间从黑跳到白。
于是他想到:如果在过渡中途故意停止驱动,能否按需产生这些灰度级呢?结果证明可以。
### 难点:变化中的稳定灰度
产生静态灰度图像相对容易,但在图像不断变化的同时保持稳定的灰度是一个完全不同的问题——**四个灰度级之间有 12 种可能的过渡**,而且它们并不等价:从黑到白需要不同的驱动方式,与从白到浅灰不同,预算也不同。
所以作者为每一种过渡都建立了测试。基本测试很简单:
- 将一个灰度的色块在另一个灰度的背景上移动
- 色块是否真的达到了想要的灰度?
- 当它移走时,背景是否恢复到了它开始时的灰度?
由于显示控制器实际上看不到颜料粒子,作者必须针对实际屏幕来校准模型。他搭建了一个小型装置,在开发板上运行过渡测试,同时拍摄照片和视频;审查结果,调整参数,然后一遍又一遍地重新运行测试。
经过大量——他说是大量的——校准后,效果开始看起来还不错。它仍然不完美,但让他得到了不依赖抖动的真正四灰度级 Game Boy 输出。
## 音频:比 Paperboy 更简单的一部分
这个项目有一部分比 Paperboy 简单得多。不同于只有蜂鸣器发声的 Paper S3,这块板子有一个**专用的音频编解码器和扬声器**,这意味着可以直接播放 Game Boy 模拟器的音频,而且音质也相当不错。
## 控制器与后续计划
到这时,作者对结果已经相当满意。游戏可以玩了,他也享受了一些过去没有机会玩到的佳作。
他还改装了一款 NAS 控制器设计,让它可以通过 USB 插入并与板子通信,但同时也应该支持其他任何蓝牙控制器。
显示驱动还没有完成:画面负载重的场景偶尔还是会掉到 30fps 以下,还有一些灰度过渡想进一步校准。等他满意之后,会把驱动和项目的其他部分一起发布到 GitHub 上,连同控制器的设计文件。
作者特别提到:如果没有 Zeph Ray 的 Paperboy,他绝对不会开始这个项目。他的工作是这个项目的灵感来源,也是逐像素驱动方法的基础。
## 衍生项目
还记得那个引发这一切的板子上的另一个项目吗?它的 UI 和菜单现在也变得非常快了。
作者正在为这块板子打造一个类似小型操作系统的东西,带有:
- 完全在线的 Anki 计划器
- 与 Obsidian 集成的语音备忘录
- 可能的本地语音转文字功能
Source: [GameBoy on E-Paper ESP32 at 30FPS — pizzaiolo](https://www.youtube.com/watch?v=XiZmM6hwwAU)
相似文章
GameBoy Emulator on ESP32 + Eink
作者成功在M5 Paper S3(ESP32 S3+电子墨水屏)上实现可玩的Gameboy模拟器,通过高刷新率驱动、优化模拟核心和创新的伪复音声音方案,使经典游戏在电子墨水屏上流畅运行。
@VedaAI00: 开发者 @zephray_wenting 通过算法与驱动调优,将原本刷新极慢的电子墨水屏(E-ink)推高至 60Hz 极限,成功在 M5Stack PaperS3 开发板上运行 GameBoy 模拟器。 在极高残影的物理限制下,实现了高…
开发者通过算法与驱动调优,将M5Stack PaperS3的电子墨水屏刷新率提升至60Hz,成功运行GameBoy模拟器,实现流畅动态画面。
CGA上的60fps视频?– GlyphBlaster
GlyphBlaster是一款基于Raspberry Pi Pico的设备,它替换了CGA显卡上的字模ROM,通过将字模ROM读取作为像素可寻址帧缓冲区,在文本模式下实现了60fps的视频播放。
@MaximeRivest: 哇,这效果真棒!电子纸不一定就得慢。我之前还担心它达不到真正的60帧每秒。我不知道确切……
用户对一款接近60帧每秒的快速电子墨水屏印象深刻,远超他们自己改装过的Remarkable平板,并计划为其添加触摸屏控制。
用 Rust 驱动 GDEH0154D67 电子纸显示屏
一位开发者记录了用 Rust 重写其 SQFMI Watchy 电子纸手表固件的过程,详细介绍了在 ESP32 上使用 Rust 所面临的挑战,以及为 GDEH0154D67 编写异步电子纸显示屏驱动的体验。