【立创开发板】基于梁山Pi的多功能游戏机扩展板1.3寸屏、WiFi与NES模拟器实战最近在玩梁山Pi这块国产主控板发现用它来做游戏机特别有意思。官方虽然有游戏机扩展板的参考设计但总觉得少了点自己的味道。于是我动手设计了一款集成了1.3寸屏幕、WiFi、锂电池供电和PSP摇杆的扩展板并且成功在上面跑起了NES红白机模拟器。最让我有成就感的是我修改了官方的模拟器代码让游戏画面在240x240的小屏幕上实现了无裁切全屏显示玩起来更爽了。这篇教程我就来手把手带你了解这个项目的硬件构成并重点分享如何修改和适配NES模拟器源码让它完美运行在我们自制的扩展板上。无论你是想复刻一个自己的掌机还是对嵌入式软硬件协同开发感兴趣相信都能从中获得启发。1. 硬件资源全解析咱们先来看看这块扩展板上都有些什么“家当”。它就像是为梁山Pi量身定做的一个多功能“铠甲”让原本核心的板子瞬间变身为一台功能齐全的掌上游戏机。1.1 核心显示1.3寸TFT彩屏显示是游戏机的灵魂。我选用了一块芯美达的1.3寸TFT彩色显示屏它的核心参数如下项目参数分辨率240 × 240 像素驱动芯片ST7789V物理尺寸26.16mm × 29.22mm × 1.5mm接口12PIN0.7mm间距焊接引脚控制方式硬件SPI特性屏幕背光亮度可调注意这块屏幕的封装是我根据实物尺寸自己画的在立创EDA里创建这样焊接后屏幕能和PCB板完美贴合没有缝隙看起来更精致。为什么选240x240的分辨率因为NES游戏的原生分辨率是256x240。虽然我们的屏幕宽度少了16个像素但通过后面会讲到的软件优化可以无损地显示全部游戏画面这个我们放到软件部分细说。1.2 听觉体验音频放大与喇叭光有画面没有声音可不行。扩展板通过梁山Pi的DAC数模转换器输出模拟音频信号然后经过一颗8002A功率放大器进行放大最后推动一个3020腔体喇叭。喇叭参数4欧姆阻抗2瓦功率尺寸是20mm x 30mm x 7mm。这个尺寸刚好能放进设计的外壳空隙里。控制方式DAC模拟输出意味着我们可以通过程序代码来调节音量大小实现静音、音量增减等功能。1.3 移动能量站锂电池供电管理为了让游戏机真正“移动”起来我加入了电池供电系统。电源管理芯片使用了一颗非常常见的TP4056充电管理模块Type-C接口负责给锂电池充电。电池搭配一块3.7V、350mAh的1S锂电池。供电流程电池的3.7V电压先通过一个升压电路升至5V然后供给梁山Pi。梁山Pi内部再通过自身的稳压电路为板上的各个器件包括CPU、屏幕等提供稳定的3.3V工作电压。充电通过Type-C口充电支持5V/1A的输入功率。提示这里我没有采用常见的排针插接TP4056模块的方式而是把模块的封装也画进了PCB直接贴片焊接这样整体性更好更节省空间。1.4 操控中心摇杆与按键操控是游戏体验的核心。这块扩展板提供了丰富的输入方式PSP超薄摇杆1个这是一个模拟摇杆可以测量X轴和Y轴两个方向的分量。它连接到了梁山Pi的两路ADC模数转换器引脚上CPU通过读取ADC值来判断摇杆的偏移方向和幅度。功能按键4个这通常是游戏中的A、B、X、Y键用于游戏内的各种操作。系统按键4个用于模拟器菜单的操作比如开始、选择、退出游戏、保存/加载状态等。RESET按键1个用于硬件复位整个系统当程序跑飞或需要重新启动时非常有用。1.5 其他重要模块EEPROMAT24C02这是一个小容量的存储芯片通过I2C总线与梁山Pi通信。它的作用是保存一些需要断电记忆的数据比如模拟器的全局设置、游戏存档的元数据、用户偏好等。WiFi模块ESP-01S这是扩展板的“联网”能力担当。它通过UART串口与梁山Pi通信使用AT指令集进行配置和控制。有了它未来可以实现通过网络更新游戏、显示天气时钟、甚至远程控制等功能。2. NES模拟器程序适配与优化硬件搭好了接下来就是让游戏跑起来的软件部分。梁山Pi官方其实提供了游戏机扩展板的示例代码但直接拿来用是不行的因为我们的屏幕和按键定义都变了。更重要的是官方的显示方案有“瑕疵”我们需要优化它。2.1 第一步修改按键引脚定义官方的代码里按键对应的GPIO引脚和我们的硬件设计对不上。所以首先要做的就是根据我们扩展板的实际连线修改按键的引脚定义。通常在代码的key.h或gpio_config.c这样的文件中会有类似下面的宏定义或结构体初始化。我们需要将它们修改成我们自己的引脚// 修改前的官方定义示例可能不同 #define KEY_A_PIN GPIO_PIN_0 #define KEY_A_PORT GPIOA // 修改为我们扩展板的实际引脚连接 // 根据原始文章提供的引脚信息修改 #define KEY_S1_PIN GPIO_PIN_5 // PE5 #define KEY_S1_PORT GPIOE #define KEY_S2_PIN GPIO_PIN_2 // PE2 #define KEY_S2_PORT GPIOE #define KEY_S3_PIN GPIO_PIN_13 // PG13 #define KEY_S3_PORT GPIOG #define KEY_S4_PIN GPIO_PIN_9 // PG9 #define KEY_S4_PORT GPIOG #define KEY_X1_PIN GPIO_PIN_3 // PD3 #define KEY_X1_PORT GPIOD #define KEY_Y1_PIN GPIO_PIN_4 // PD4 #define KEY_Y1_PORT GPIOD #define KEY_A1_PIN GPIO_PIN_10 // PG10 #define KEY_A1_PORT GPIOG #define KEY_B1_PIN GPIO_PIN_6 // PD6 #define KEY_B1_PORT GPIOD修改完引脚定义后还要记得在初始化函数里将这些引脚正确地配置为输入模式并设置上拉或下拉电阻根据硬件电路决定。2.2 第二步解决屏幕显示的核心问题——无裁切全屏这是本次适配最核心、也最有技术含量的部分。官方的示例代码存在一个问题问题根源NES游戏的标准输出分辨率是256像素宽 x 240像素高。官方扩展板用的屏幕是240x280比游戏画面高但窄。所以他们的做法是直接把游戏画面左右各裁掉8个像素共16像素只显示中间的240x240区域。这样虽然简单但游戏画面不完整可能会看不到一些边缘的游戏元素比如分数、生命值等。我们的屏幕是240x240宽和高都比NES画面小。如果简单缩放或裁切体验都不好。我的解决方案水平方向降采样垂直方向完美匹配。垂直方向我们的屏幕是240行NES画面也是240行高度上完美匹配一行对一行显示即可无需任何处理。水平方向NES有256列我们只有240列。直接裁切会丢失内容简单压缩会变形。我采用的方法是“降采样”。简单理解就是从NES的256列像素中有规律地挑选出240列来显示。虽然会损失一点点横向的细节但保证了画面的完整性和比例基本正确。具体到代码里就是在将NES帧缓冲区ppu-dummy_buffer的数据发送到屏幕驱动ST7789V via SPI时加入一个计算。下面是我修改后的核心代码片段// 这是一个将NES画面数据发送到SPI屏幕的循环示例 // i 代表当前屏幕输出的列坐标 (0 到 239) // 我们需要从NES的256列中计算出对应的一列 { // 等待SPI发送缓冲区为空 while(RESET spi_i2s_flag_get(SPI4, SPI_FLAG_TBE)); // 关键计算(int)(i * 256 / 240) 或者更精确的 (int)(i * 16 / 15) // 这个公式将屏幕的240列映射到NES的256列上。 // 例如当i0时取NES第0列当i120时取NES第(120*256/240)128列当i239时取NES第255列。 // 原始文章中的 (int)(i*16/15 - 8/15) 是一种等效且可能做了中心对齐微调的写法。 uint16_t nes_column_index (int)(i * 16 / 15 - 8 / 15); uint16_t color_data NES_Palette[ppu-dummy_buffer[nes_column_index]]; // 发送颜色数据的高字节和低字节通常为RGB565格式 spi_i2s_data_transmit(SPI4, color_data 8); while(RESET spi_i2s_flag_get(SPI4, SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI4, color_data 0xFF); }公式(int)(i * 16 / 15 - 8 / 15)的解释i * 16 / 15等价于i * 256 / 240完成了从240到256的线性映射。- 8 / 15是一个微调。因为8/15 ≈ 0.533这个操作相当于将映射后的起始点向中心偏移了约0.5个像素使得最终显示的画面在屏幕上看起来是居中的视觉体验更好。通过这种方式我们就在240像素宽的屏幕上完整地显示了原本256像素宽的游戏画面。实测下来画面没有明显的变形或失真游戏体验比官方裁切方案要好很多。2.3 第三步调整屏幕布局由于我们的屏幕是正方形240x240而官方代码可能为更高的屏幕预留了状态栏空间。因此我们需要移除顶部状态栏和底部信息栏把这些空间全部让给游戏画面。修改屏幕初始化序列确保ST7789V驱动芯片被正确配置为240x240的显示区域并且设置好扫描方向让我们的游戏画面能正确填充整个屏幕。这部分需要参考ST7789V的数据手册和梁山Pi的LCD驱动代码修改lcd_init()函数中的相关寄存器配置值。3. 结构设计与组装心得为了让这一切变成一个真正的“掌机”我设计了上下盖的3D外壳。顶壳固定我们自制的扩展板。底壳固定梁山Pi核心板。组装两者通过扩展板上的排针母座与梁山Pi的排针公座对插结合。内部布局喇叭和锂电池都巧妙地放置在扩展板和梁山Pi主板之间的空隙里充分利用空间。目前遇到的坑 第一版外壳的装配便利性有待提高特别是Type-C充电口的位置开孔不太精准每次充电插拔有点费劲。这是下一版需要优化改进的地方。4. 未来还能玩点什么这个扩展板的潜力不止于游戏机。ESP-01S WiFi模块已经焊上去了我接下来的计划是开发WiFi天气时钟功能联网获取时间和天气信息在待机界面显示。开发简易电子便签可以通过手机APP发送文字到屏幕上显示。功能整合将这些功能做成一个简单的操作系统和NES模拟器整合在一起通过系统按键进行切换。这样一来这块板子就真正成为一个“多功能”的嵌入式学习与娱乐平台了。从硬件设计、焊接到驱动适配、软件优化再到外壳建模整个过程走下来对嵌入式开发的全流程会有非常深刻的理解。希望这个分享对你有帮助也欢迎你基于这个思路创造出属于自己的独特作品。