W80X(W800/W806/W801)NES游戏模拟器 带声音 自制开源掌机
时间:2023年04月09日 人气:...

        先用个视频展示一下效果:

 

特点:支持双手柄双人玩耍、可小屏/全屏、可选择游戏、一键静音/恢复、跳帧调节、屏幕可调亮度、支持存档/读档……


接下来才是本文正文:

        起先,我以为w806也就只能玩玩俄罗斯方块这种级别的游戏:

        然后有次在csdn上看到这位大佬的文章“单片机---HLK-W801移植Nes模拟器”,通过他的最后一章研究得出一个数据:infones的帧生成速度是28fps左右,w801的主从spi传输时钟最高20MHZ所以理论最高每秒传输20帧,最终加上实际执行时的各种开销耗时实际刷屏速度为11fps左右。这没毛病,就算实际刷屏跑满能到20fps,这对nes模拟器还是远远不够的,要知道nes游戏在PAL制式是50帧、NTSC是60帧的,于是我更认为w80x系列的芯片是做不了nes模拟器了。

         直到有一天,又在csdn上看到了一位大佬的文章“W800 W801 W806 SDIO的SPI模式驱动TFT LCD 突破低速SPI的20M限制 高达120M的SPI”,瞬间眼前一亮,前面所说的nes模拟器刷屏速度问题不就解决了吗?于是大感兴趣,准备好好学习一下这位大佬的思路,无奈发现这文章竟然还需要开通vip才能阅读,这。。。。

穷逼的我只好止步于此了,刚准备关掉网页,这时眼睛嫖过评论发现有好几条评论,于是决定先看看评论没准能得到点信息,幸好csdn没有也把评论也给需要vip才能查看。

终于在一条评论中得到了我期望的信息“W80X驱屏方案 SDIO SPI模式 最高120M时钟驱动LCD屏 LVGL界面开发”,isme大佬

        赶紧下载了这份代码,通过了一段时间的努力学习,终于搞明白了原理,大佬们的变通能力真流弊

        于是回到开头所说的那个NES模拟器,这回刷屏的问题应该不是问题了,那么接下来的问题是啥呢?是infones的帧生成速度!每秒只有28fps,这你敢信?百度了一下其它mcu的nes模拟器情况,stm32上有超频后以不到200MHZ流畅玩的,esp32也有畅玩的。那么问题来了w801这可是一颗240MHZ的cpu,不至于还跑不过一个跑不到200MHZ的stm32吧。

看来是软件运行效率的问题,大概了解了一下开源的那些个nes模拟器:

infones是一个很早之前由岛国人开发的模拟器,其软件设计结构清晰明了,移植层也被独立抽离出来,小白都能轻松上手,相应的,软件设计的非常清晰易懂,那就意味着运行效率不够好了,网上不少说它慢,还有声音只能听个响啥的问题。

使用arm核的mcu,有个比较变态的nes模拟器,6502模拟全用汇编实现的,这种执行效率高,一片好评。

esp32使用的nes模拟器应该是从arduino社区被发布出来的,它的nes模拟运算跑在一个核上, 刷屏和声音由另一个核去做。

        看起来最好的方法还是移(借)植(鉴)开源模拟器,w801使用的平头哥的cpu,汇编还不同于arm的汇编,咱的汇编也不咋行,移植难度比较大,所以汇编版本的模拟器直接就不考虑了。

esp32的模拟部分也是跑的单核,所以这点上看和跑infones一样,考虑到咱是个nes模拟器小白,目前完全不懂任何nes模拟知识,所以还是选择通俗易懂的infones算了,把它的运行效率搞搞就好了。

当然也不用从头搞,如果有csdn下载能力的话,可以直接从开头那位大佬的文章处下载w801现成的代码,但是我的话么有能力下载呀,只好下载另一位大佬的代码“手把手教你移植InfoNES(到HANKER-LM4F232)”,这位大佬写了系列的文章,可以都看下了解下优化思路。

        因为上次听一个朋友将联盛德有一款w806比较火,所以我这次也想使用w806来做这个模拟器,况且这个模拟器也用不到wifi和蓝牙功能。w80x系列都是同一颗cpu,代码其实通用的,区别就只是部分配置的差异,比较一下两份sdk很容易就知道怎把w801sdk用在w806上了。代码很容易就能集进w801的sdk,进行编译,这里也不多说。

于是先上淘宝购买了一块w806,拿到手之后,看了半天,又对比了一下人家卖的w801,不由心里失望:

这板子设计也太不走心了,这么多的io,竟然只有2个gnd,1个3.3v,0个5v,这可是用usb线供电的,连个5v供电都没

         话不多说了,还是搞起代码吧,首先要搞明白为啥infones的绘图速度低,以最经典的超级玛丽为例,首先超级玛丽nes文件按十六进制复制到代码中,先不用适配lcd驱动来刷屏,就测一下运行超级玛丽时的fps情况,每5秒统计一下总帧数,跑完是26fps左右,因为infones非常好移植,所以我又把它也挪到esp32上(配置为只用cpu0,主频也配为240M),运行后发现,esp32也才24fps左右。看来这不是cpu效率问题了,w806的cpu效率还略高于esp32的单核,那么这就属于纯属代码执行效率低问题,毕竟能运行nes模拟器的esp32跑infones也这样。

        通过和esp32的对比时,发现一个有趣的现象:如果游戏文件存在flash中,xip执行时读取,那么w806速度就会降低到11fps左右,而esp32没有变化,于是就得到了一个关键的信息,要把infones尽量变小,以减少xip执行时的耗时开销和腾出更多的ram,然后游戏文件要读取保存在ram才能提速。

        先把mapper进行裁剪,只保留了0-3这四个最简单也是常用的mapper,这样infones就大大瘦身了。

然后分析代码流程,发现瓶颈主要在于大量的循环和函数调用,执行的函数其实就俩:

模拟6502用来执行游戏指令代码的K6502_Step( STEP_PER_SCANLINE )函数 和

模拟PPU用来将NT、Patten Table、AT、Pallette中的数据组合成像素颜色数据的InfoNES_DrawLine()函数,当然最后还有一个我们用来按行刷屏的InfoNES_LoadLine()函数。

接下来就是先不用管InfoNES_LoadLine(),把6502模拟和PPU模拟绘图给使劲优化。各种招数用起来,大力出奇迹!

经过好几个晚上、周末假期等闲暇时间的琢磨,终于有天,绘图生成的帧的速度达到了惊人的76fps:

这时,infones的运行效率问题已经解决,如果是使用高速的并口屏,那么整个活就直接完成了,搞不好还得加延时以抑制过快的速度。

但是咱不是并口屏,咱是慢速的串口屏,而且咱还没实际刷到屏上去呢,所以咱马上给自己定个flag,让实际刷屏达到PAL制式50fps。那么接下来就是实际刷屏了。

屏幕我是用4.0寸的st7796s,分辨率480*320,最高好像可以支持到80MHZ时钟,还带有触摸和sd卡座:

 nes原始的分辨率是256*224,所以就先拿256*224直接输出lcd,刷屏效果很感人:

达到了惊人的33fps,30fps其实就可以玩了,但是距离50fps还是比较遥远的,于是,我又动了一番脑筋,一番折腾之后,刷屏速度又有了明显提升:

已经达到了45fps左右。

打点测了一下256*224目前刷一屏大约是17ms左右,那么45fps大约是765ms,目前的问题在于刷屏太慢,这需要考虑多整几个发送缓冲区让dma循环使用去搬以减少cpu等待时间(一般可以整两个即可达到目的)。考虑到45fps已经很接近了50fps了,继续提升速度空间比较小了,还比较消耗内存,所以暂且就这样,留给以后再进一步优化。

ps:lcd多块buf发送后来做了验证,提升效果很小(只能提高2-3帧)。生成像素数据很快,刷屏速度太慢,需要很多buf来做缓冲让dma首尾相连去无缝发送,这样占用的内存和速度完全不成正比,而咱有需要大量可用的内存来存放游戏rom,所以完全可以忽略多buf机制了,毕竟3帧的影响太小了。

        接下来就是声音了,infones的声音确实只是听个响的,音质完全没有,所以百度了一番,从nester抄取了apu的算法,直接可以输出16位44100采样率的数据,这下听着好多了,不过了节省内存,最终还是选择了22050采样率的音质。音频选择使用i2s输出,简单的可以选择MAX98357模块,这个是放大器可以直接驱动3Ω8w的喇叭发出响亮的声音,但是因为喇叭音质问题,我使用了之前改装天猫精灵时用过的pcm5102(小紫板),这个模块仅是音频输出无放大器功能,所以需要喇叭自带功放才能用好。

使用i2s输出声音的时候,又遇到了问题:本来apu可以改为不输出16位而用8位,这样还能减轻计算量提升速度,但是我是用8位数据后,游戏中apu产生的声音是8位单声道22050采样率的数据,直接用这个参数配置驱动,播放直接没音。因为我对i2s并不熟,所以就乱试了一通,最后竟然发现驱动接口里要配为16位数据格式竟然就能播放了,接着杂音很大,我估计是传输的数据格式缺斤少两了,于是又对声音数据累积了2段,填写驱动长度的时候还要除以2,这样之后竟然杂音小了很多,虽然看起来还是哪儿没配对,但这里我就不纠结了,声音差不多就可以了。

wm_i2s_ws_config(WM_IO_PB_13);//lrck
wm_i2s_do_config(WM_IO_PB_11);//data
wm_i2s_ck_config(WM_IO_PB_12);//blck

I2S_InitDef opts = { I2S_MODE_MASTER, 
I2S_CTRL_MONO, 
I2S_RIGHT_CHANNEL, 
I2S_Standard_MSB, 
I2S_DataFormat_16,
22050, 
5000000 };

wm_i2s_port_init(&opts);
......

//游戏每次生成378长度的16位声音(也就是756字节),需要累积两次也就是攒够756*2字节的数据才去传输,
//传输接口填写的长度是756:
wm_i2s_transmit_dma(&sound_hdma_tx, (uint16_t *)sound_buf, 756);
//sound_buf每次填充新数据前保证了dma已经传输完成
......

不知道是wmsdk的i2s驱动有问题,还是我不会用的缘故,但是这段音频导出用电脑播放很正常,有知道原因的朋友不妨请下面留意指点一下我哈。

看到屏带有sd卡座,所以考虑从sd卡上去取nes文件来自主选择游戏,所以从使用业界标杆esp32模拟器的韦老师工程“esp32_100ask_project”里抄了个菜单功能。

然后又斥资十六元购买了一个9孔的fc游戏手柄(据老板说这玩意进货价才是九块),用来畅玩游戏

当然还有一张祖传的512M sd卡,用来存放nes游戏文件:

又看到lcd屏带有背光控制引脚,所以使用pwm搞了个亮度调节功能(或许应该在整个光敏电阻,来个自动调节亮度……)。

因为这款lcd分辨率是480*320,如果游戏画面能达到这么大那就看着很舒服了,所以我就尝试着对画面进行了拉伸,当然想法很美妙,效果又一次很感人。画面拉大以后,刷屏需要的数据更多,每行变多成了1.5条,竖线也需要1.73条,所以循环变得更多耗时也增大了,屏变大了传输一屏数据需要的时间也增多了。打点测试了一下,刷完一屏需要48ms,软件循环大概耗去了8ms,真正刷屏传输大约40ms。

到这里我突然灵机一动,因为为了充分利用cpu,一直工作在240MHZ,对于这个屏,spi只能分配为60MHZ,那么在刷屏时可以临时将cpu降为160MHZ spi就可以调整为80MHZ,传输完再改回去,那么这样啥也不耽搁速度还能加快,试了一下,这样传输一屏需30ms左右,大大提高了传输效率,这样480*320分辨率就可以达到30fps左右了。达到30fps,来点跳帧也就可以畅玩了,至此全屏也就达到了目的。

代码暂且还没有整理好,稍后整理后公布,感兴趣的朋友完全可以按照上文的思路和流程自己制作哈,玩这个重在自己动手探索,如果照着保姆教程像个机器人一样跑一遍那是没有任何灵魂的。

对于想体验的小伙伴,可以直接点此下载固件烧录畅玩 ---> w80x_nes.zip 

w80x系列芯片通用,都可以烧录使用,亲测w801和w806都可以使用。

需注意:目前只能玩90kb以下的nes游戏,玩700kb以下nes游戏,当然游戏也得是mapper0-3的才行。后续考虑使用psram,再增加个mapper4,应该就可以玩比较大型的游戏了。

 

接线图:

lcd(使用st7796s):
blk -----WM_IO_PA_07
scl -----WM_IO_PA_09
mosi-----WM_IO_PA_10
dc  -----WM_IO_PA_12 
rst -----WM_IO_PA_13
cs  -----WM_IO_PA_14

sd卡:
cs-----WM_IO_PB_04
ck-----WM_IO_PB_02
di-----WM_IO_PB_03
do-----WM_IO_PB_05
游戏要放置在sd卡根目录下的nes文件夹中。

i2s(需自带mck):
lrck-----WM_IO_PB_13
data-----WM_IO_PB_11
blck-----WM_IO_PB_12

fc手柄(5v供电):
clock-----WM_IO_PB_08
latch-----WM_IO_PB_09
data -----WM_IO_PB_10

在游戏选择菜单
按上下键选择列表中的游戏,按start/a/b键玩游戏

游戏中按键说明(进入游戏后才会生效):
重启到菜单-----select + start
调高亮度-----select + up
降低亮度-----select + down
调高跳帧数-----select + right
降低跳帧数-----select + left
全屏游戏-----select + a
静音/播放-----select + b

后来我又修改了一版,增加了俄罗斯方框游戏和双手柄支持,可以畅玩90坦克、魂斗罗1等游戏了,接线和按键相比上面版本发生变化如下:

fc手柄1(5v供电):
clock-----WM_IO_PB_07
latch-----WM_IO_PB_08
data -----WM_IO_PB_09

fc手柄2(5v供电):
clock-----WM_IO_PB_01
latch-----WM_IO_PB_00
data -----WM_IO_PA_15

在游戏选择菜单
按上下键选择列表中的nes游戏,按start键玩游戏
直接按a键玩俄罗斯方框游戏
直接按b键播放avi视频(播放/media/tangmaru.avi,需pc端提前转码故不建议尝试)

固件点此下载 ---> w800_nes_2p.zip

说明:

1. 进入某些游戏后发现有点慢时,就可以调高点跳帧数,感觉过快时可以降低点跳帧数。

2. 必须要接fc手柄才能正常使用,否则会不停重启复位。

整理了一份调试通的源码,供感兴趣的朋友参考   --->   w806_mynes.7z (只支持cdk编译)


闲置无用,出当时玩这个时所买的全套模块,可咸鱼交易:https://m.tb.cn/h.5Ds0sxY?tk=p2kpWna18ue。

热门评论