×

能够从微型SD卡加载游戏的Arduboy克隆

消耗积分:0 | 格式:zip | 大小:1.16 MB | 2023-02-03

李义坤

分享资料个

描述

ArduboyXL号

我着手制作一个具有更大屏幕并能够从微型 SD 卡加载游戏的Arduboy克隆。TL;DR 是它使用两个 arduino——一个 Atmel 328p 从 SD 卡读取并重新刷新 Atmel 32u4。多路复用器芯片用于将显示器切换到编程器内核以显示菜单。我制作了自定义工具来生成菜单列表和屏幕截图文件。我用数控机床用铝板加工了一个外壳。

背景

Arduboy 是一款基于 arduino 的便携式游戏系统,旨在运行免费和开源游戏。它本质上只是一个 8 位微控制器,以信用卡大小的封装连接到 oled 屏幕。

harwdare 非常有限。该处理器是一个 16MHz ATMega 32u4,具有 32KB 闪存和 2.5KB RAM。屏幕是 1.3"、128x64 像素、1 位 OLED。有 512 字节的 EEPROM 可用于保存游戏或高分。系统一次只能容纳一个游戏,必须从 PC 刷新才能切换游戏。

有趣的是,这设法创造了一种颇具吸引力的体验,吸引了许多明显的开发者和玩家。它在 2015 年获得了成功的 kickstarter ,筹集了超过 40 万美元,并且有 100 多款游戏,从儿童制作的第一款游戏到具有专业外观的游戏。从游戏的角度来看,这远远领先于Ouya等其他草根游戏机开发项目,其资助金额是其十倍以上。我认为,成功来自于限制——简单的硬件意味着你需要制作一款简单的游戏,这使得它更容易上手,也更容易完成。完成后,游戏将在一个完全独立的平台上运行,没有其他东西可以搞砸它。如果您将您的游戏发送给朋友,他们会获得完全相同的体验,并且没有真正的恶意软件可能性(尽管我认为您可以制作一个会损坏硬件的程序,但它不像普通的可执行文件那样可以勒索您的整个硬盘驾驶)。显然范围不一样,但我认为小赢总比大失败好。

我的电子游戏囤积本能被激活了。我想拥有所有的游戏。我已经着手制作我自己的系统版本,该系统包含系统本身中的所有游戏,无需从计算机重新刷新。

研究

你怎么做到这一点?我知道让 arduino 与 SD 卡对话是相当简单的——有图书馆可以做到这一点。所以我们可以从 SD 卡加载文件,但是如何让它们在 arduino 上运行?程序存储器中有一个小的保留部分称为引导加载程序,目前允许您通过串行通信对芯片进行重新编程。理论上,可以重写这段 4KB 的代码,以便从 SD 卡对芯片本身进行重新编程……

但这听起来真的很难。一个更简单的解决方案是让第二个 arduino 与 SD 卡通信,并将程序发送到玩游戏的 arduino。

“双核”设计已经确定,但是如何让一个arduino程序成为另一个arduino程序呢?

选项 1:STK500 over serial利用引导加载程序,您可以将串行命令从一个 arduino 发送到另一个以对其重新编程。这与当您通过 FTDI usb 连接到串行适配器时计算机对芯片重新编程的方式相同。对于布线,您只需连接 RX->TX 和 TX->RX(和 GND)。

我发现了以下示例:

George Caley 的Arduino 复印机

我能够让这段代码在 Arduino Uno 和 Pro-Mini 上运行,它们都是基于 ATMega 328p 的开发板。

困难在于代码的文档记录不完善。有大量的幻数块,其中一些需要更改才能使代码适用于 32u4 芯片。我开始使用 STK500 文档将幻数转换为命名常量,但我遇到的第一件事是“设置设备”指令,后跟设备代码 0x86,我在任何地方都找不到它。我假设它是 328 的设备代码,但我找不到准确进行所需的文档。

Bald Wisdom 的引导驱动器

这看起来很棒,它使用命名常量,它实际上完全按照我的需要做,甚至从 SD 卡加载草图。但是,我无法让它工作。它使用硬件串行线进行编程,并使用第二条软件驱动的串行线进行调试——我把它连接得很好,但从来没有遇到过几种不同类型的错误垃圾邮件。还有一些巨魔狗屎,比如接线图,上面有几段注释,上面写着“图表是错误的”——它让你质疑每一行代码。

这里故事的寓意是使用选项 2。

选项 2:在线串行编程器 这是一种使用 SPI 接口的更直接的编程方法。这种方法实际上可以刷新引导加载程序本身,这很好,因为那里有很多关于如何在引导加载程序出错时恢复你的 arduboy 的线程。这就引出了一个问题,即为什么我们首先要有引导加载程序,尤其是在需要外部设备进行串行通信的 pro-mini 之类的东西上。为什么不直接插入不同的东西并通过 ICSP 编程呢?

Nick Gammon 的十六进制上传器

这几乎立即起作用,从一种类型的 arduino 到另一种类型没有任何困难。

问题是它真的很慢,需要大约 45 秒才能刷新。需要进行一些优化。

我实施了以下优化:

删除文件长度验证。在上传之前读取整个文件以避免超出程序内存边界(实质上是读取文件两次)。引导加载程序区域受到保护,因此如果它确实通过,将会发生的只是上传失败,因此这一步似乎是不必要的。

删除了行校验和验证。十六进制文件中的每一行都包含一个校验和,但如果它是错误的,那么文件无论如何都会被破坏。你从哪里得到这个文件?只要有好的文件。

删除了 clearPage 命令。我相信这是在向其上传新数据之前清除目标芯片上的工作页面。这需要很长时间,不做似乎不会造成任何问题。一个副作用是最后一页的末尾可能有重复或垃圾数据(唯一不会被新数据完全覆盖的页面),但如果你最终进入该部分,你的程序就会偏离轨道无论如何,您只是想获得一致的失败状态。

如果您确实需要此功能,更好的方法是将 0x00 写入仅最后一页的未使用部分。

减少/消除 BB_DELAY_MICROSECONDS 延迟。编程信号通过软件定义的 SPI 总线,BB_DELAY_MICROSECONDS 是保持每个时钟周期上升或下降的微秒数。默认值为 6,即 83KHz。SPI 总线可以很好地进入 MHz 范围,因此增加它应该不是什么大问题。我尝试了 3,然后是 1,然后我用一些汇编 NOP 指令(在一个时钟周期内什么也不做)替换了微秒延迟,以延迟不到一微秒。

最后我把它调低到一个 NOP,最大速度,没有问题。

删除了上传验证。回读整个上传的闪存以验证需要很长时间。为什么不运行它看看会发生什么?

更改了 SPI 总线初始化 SPI_HALF_SPEED -> SPI_FULL_SPEED。半速应该避免面包板上的错误。我最终在调试过程中来回改变并且没有注意到任何区别。我不认为文件吞吐量是这里的瓶颈。

向 BB_SPITransfer() 添加了“inline”关键字。bit-bang spi 函数现在每秒被调用数百万次,所以我认为内联它可以减少一些开销。我没有衡量任何改进。

删除了 chipErase 命令。不得不撤消这一点。原来你必须在写之前擦除,你不能只是覆盖。

这些变化结合起来使刷新时间减少到大约 7 秒!程序员将基于此代码的修改版本。

部分

阿杜诺微型

基于 arduboy 中使用的相同 ATMega 32u4,该板断开了所有必要的引脚以进行精确克隆。

比亲微大,不过有保障的测试平台还是不错的。

请注意,此版本具有更大的芯片封装,因此可以将一些额外的电线直接焊接到芯片上并获得与 arduboy 的完全引脚兼容性。

您确实至少需要其中三个 - 一个用于面包板,一个用于最终项目,一个用于搞砸。

基于 328p 的 pro-mini 需要外部usb 转串口设备对其进行编程,但它比基于 32u4 的 pro-micro 上的虚拟串口可靠得多。

我最终将其用于程序员核心。

是的,我们将有一个带有 2KB RAM 的 CPU 连接到 8GB 存储空间。有点荒谬,但他们并没有真正制造比这更小的 SD 卡。

这些用于最终版本的按钮。为了进行测试,我们将使用普通的触觉开关。

值得注意的是,它还具有低电压截止功能,因此可以在其他项目中与裸锂聚合物电池一起使用。

互联网告诉我三星 Galaxy S4 是世界上最受欢迎的智能手机,所以我想有人会为它制造新电池。此外,它是一个很好的方形,很容易与其他组件配合使用。

购买手机电池的一个大问题是,大量电池是从电子垃圾箱中挑选出来并作为新电池出售的。这个很可能是电子垃圾,上面贴着红色标签,但至少他们并没有试图将其假冒为正品。

这将从单个 3.7v LiPo 电池中为我们提供稳定的 5 伏电压。请注意,arduino 板上的稳压器只会向下调节,并且需要高得多的电压。

额定300ma,应该没问题。还有一个用于高电流应用的MOSFET 开关,但我认为这不是必需的。

电池充电器上有一个 USB 端口,但如果我们想要与 arduboy 建立数据连接,我们可以先使用这个 breakout。

内置电平转换器可转换为所需的 3.3v,具有卡检测引脚,因此我们可以判断何时插入了 SD 卡。

比压电略好。

这个屏幕和arduboy的SSD1306控制器一模一样,而且便宜,所以拿来测试。

几乎是默认 arduboy 屏幕的两倍,具有非常相似的 SSD1309 控制器。游戏可以通过一些小的十六进制编辑转换为使用此显示。

 

面包板克隆

Breadboard-Clone.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

最好从复制已经完成的事情开始,然后再做新的事情。我使用 SSD_1306 屏幕和标准压电在面包板上连接了一个 arduboy 克隆,下载了游戏的源代码并进行了编译。一切都立即有效。

当我 10 岁的时候,我从音乐生日贺卡中取出压电 - 我知道有一天它会派上用场。

增强型面包板克隆

Breadboard-Clone-2.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

我用更大的 SSD_1309 OLED 替换了屏幕,并用扬声器替换了压电显示器。使用arduboy 自制包和不同的屏幕选项重新编译游戏效果很好。

重新闪现概念证明

这个概念证明证明可以在不连接到计算机的情况下在两个不同的游戏之间切换。程序员核心位于前台面包板上,带有一些用于上传状态的 LED 和两个硬编码的专用按钮,用于将两个不同的十六进制文件上传到后台的 arduboy 核心。

为简单起见,显示和移动按钮仅连接到 arduboy 核心。切换游戏时会出现一些屏幕垃圾,因为屏幕与 ICSP 在同一总线上,此时程序员核心无法将其关闭。

完整原型

这是一个更复杂的原型,它显示了整个过程的工作。当电源打开时,arduboy 核心正常启动并开始玩它在内存中的任何游戏,而编程器核心进入睡眠模式(由于编程核心上运行的一些调试代码,会出现短暂的重置问题)。

然后当你按下一个专用的菜单按钮时,编程核心就会唤醒,在 arduboy 上拉 reset,翻转一个多路复用器芯片来控制屏幕并显示游戏菜单。它可以在列表中显示任意数量的文件,并在您选择时显示每个游戏的 64x64 像素屏幕截图。

程序员核心使用 SSD1306_text 库的修改版本来显示文本和图形。有更精细的图形库可用,但要进行 SD 卡读取和 ICSP 编程,闪存空间和内存非常宝贵。我修改了库以使用具有比例间距的自定义压缩字体,并编写了一个命令行实用程序将 png 图像转换为可以直接嵌入到 arduino 草图中的字体字节。

通常为了显示这样的列表,我会把整个东西加载到 ram 中并完成,但我们只有 2KB 的 ram,其中 75% 已经在使用中。我们甚至没有足够的空间来缓冲屏幕上显示的文本。文件系统也是 FAT32,在 arduino 上只支持 8.3 文件名。这两个问题的解决方案是使用一个预生成的列表文件,其中包含较短的 8.3 十六进制文件名和较长的显示名称。列表文件中的条目是固定长度的,所以我们可以很容易地跳到任何一行,阅读它,并在我们进行时将显示名称打印到屏幕上。

屏幕截图是从单独的文件中读取的,并一次一个字节地打印到屏幕上。它是在某种文本模式下完成的,其中 8 像素高的水平行填充有垂直条纹(每个一个字节)。我编写了另一个命令行实用程序,可以将 png 屏幕截图转换为特殊格式并一次性生成列表文件。通过这种方式,您可以在您的计算机上保存一组具有常规长文件名和匹配屏幕截图的游戏文件,然后只需运行该实用程序,然后再将所有内容上传到 SD 卡。

硬件计划

还有一些组件需要完成:电池、声音和 RGB LED。我测试了电流消耗,最大电流约为 200ma,所以我可能会使用手机电池来确保较长的电池寿命。我用来测试的扬声器很好很薄,但它的直径很大,我可能会货比三家,买一个小一点的。我还需要一个指轮电位器来调节音量,我不得不求助于 ebay - 他们不再真正制造它们了,所有新东西都使用数字电位器,这只会使事情复杂化。

arduboy 上的 RGB LED 是共阳极,幸运的是,我周围的所有 LED 都是共阴极。我想在设备的顶部或侧面做一些光管设计,让它不仅仅是一个点光源。Bezek上的光管运行良好。

对于按钮,我计划使用 NES 控制器维修套件中的橡胶圆顶(仍在制作新的)。我计划将按钮与外壳一起用铝加工出来。

软件计划

我希望能够通过按左/右键在菜单的不同显示模式之间循环。一方面是纯文本显示,每个游戏的 1-5 星评级,然后是当前名称/屏幕截图拆分,然后是完整屏幕截图显示。可能还有标题屏幕显示。

有些游戏保存高分或将游戏保存到 512 字节的 EEPROM 中。我希望能够在切换游戏时备份和恢复这些数据。

案例设计

CAD.png?auto=compress%2Cformat&w=740&h=555&fit=max

这是一个透明视图,显示了外壳顶部和一些内部组件的切口。外壳设计为铣削,因此内角需要倒圆角。

这是一个有点复杂的装配,所以我在 3D Studio 中制作了一些模型以确保一切都合适。

Case-Render.png?auto=compress%2Cformat&w=740&h=555&fit=max

1 / 4 •组装设备的渲染图。白色条是 RGB LED 的光管。

电路板设计

PCB-Tape-Out.png?auto=compress%2Cformat&w=740&h=555&fit=max

DesignSpark PCB 布局

我使用 DesignSpark PCB 来...设计(激发?)PCB。迹线布局是手动布线和自动布线器的混合,之后会进行调整。我在仔细检查所有内容时发现了一些错误,希望我都能找到它们!

我导出了 gerber 文件并将它们上传到 OSHPark 进行制造。

OSHPark-Top.png?auto=compress%2Cformat&w=740&h=555&fit=max

1 / 2 • OSHPark 正面渲染

PCB-Bare.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

OSHPark 的物理 PCB。

PCB-Mistake.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

1 / 2 •安装屏幕,间隙问题。

我犯了一个明显的错误,部分电路板挡住了屏幕模块上的某些组件。幸运的是,电路板的那部分没有痕迹,所以只需要一点锉就可以修复它。

internals-front_dLOMWtoul2.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

组装电子产品,正面

internals-rear_fEwTrnDZqN.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

组装电子产品,后部

电子设备的背面。电池设计为与电线连接,但由于焊盘在 PCB 上抬起,因此连接到主开关的电线需要维修。我应该扩展 PCB 以从所有 4 个角支持 USB 充电模块,并从一开始就将其粘住。

表壳加工

Case-Raw.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

刚加工完。

呃……长话短说,我用铝加工了一个箱子。我在当地的创客空间使用了 Bridgeport V2XT。它不是这项工作的理想机器,因为主轴在 4000rpm 时达到上限 - 对于 1/8" 铝制刀具,您确实需要 20, 000+ rpm 才能获得适当的表面速度。刀具夹持设置存在一些错误不够安全,最重要的是我在编程时犯了一个错误,所以 D-Pad 和菜单按钮孔最终太大了。不过它仍然可以通过。

case-inside_by2uBd3sgT.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

外壳和按钮,清理干净。

自然地,经过几个小时的加工,我折断了其中一个螺丝孔内的丝锥。我只是把它留在那儿了。剩余的螺丝足以将其牢固地固定在一起。

case-outside_hWda4VgwVe.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

外壳的外面。

由于振动,它有点条纹。这本可以通过使用更厚的固定片或带有定制软钳口的虎钳来避免。在后面板右侧的最后一次通过时,抖动特别严重。

assembly-1_Hbn2xiwfJh.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

1 / 3 •按钮到位。

Assembled.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

完毕!

在这里它已经完全组装好了!

Charging.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

给电池充电。

我用亚克力制作了一根导光管,将充电状态 LED 引导至外壳外部。

Light-Pipe.jpg?auto=compress%2Cformat&w=740&h=555&fit=max

灯管特写。

当电池充满电时,指示灯变为蓝色。

文件

该 zip 包含 ICSP flasher arduino 项目、C++ 二进制文件和用于转换屏幕截图、PCB 文件以及外壳和按钮的 STL 文件的源代码。

我不希望任何人能够直接使用它,但如果您有类似的项目,它可能会对您有所帮助。

 

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !