根据Arduino文档,ATmega328具有用于引导程序+上传的草图的32KB闪存,以及用于运行时数据的2KB SRAM。ATmega2560具有更多功能,分别为256KB和8KB。
无论哪种情况,这些限制似乎都很小,尤其是与类似大小的消费设备(例如智能手机)相比时。
如果用尽了怎么办?例如,如果草图太大,或者需要在运行时处理大量数据(例如字符串)?有什么方法可以扩展Flash或SRAM?
根据Arduino文档,ATmega328具有用于引导程序+上传的草图的32KB闪存,以及用于运行时数据的2KB SRAM。ATmega2560具有更多功能,分别为256KB和8KB。
无论哪种情况,这些限制似乎都很小,尤其是与类似大小的消费设备(例如智能手机)相比时。
如果用尽了怎么办?例如,如果草图太大,或者需要在运行时处理大量数据(例如字符串)?有什么方法可以扩展Flash或SRAM?
Answers:
优化
针对嵌入式系统的低级编程与针对通用设备(如计算机和手机)的编程大不相同。效率(在速度和空间方面)更为重要,因为资源非常宝贵。这意味着如果空间不足,要做的第一件事就是查看可以优化代码的哪些部分。
就减少程序空间(Flash)的使用而言,如果您没有经验,或者如果您更习惯于不需要使用该技能的台式计算机编程,则很难优化代码大小。不幸的是,虽然认真考虑草图的实际需求会有所帮助,但没有适用于所有情况的“魔术弹”方法。如果不需要某个功能,请将其取出。
有时,确定代码的多个部分相同(或非常相似)的位置也很有帮助。您可能可以将它们压缩为可重用的函数,可以从多个位置调用它们。但是,请注意,有时尝试使代码过于可重用实际上会使它变得更加冗长。实践中往往很难达成平衡。花一些时间查看代码更改如何影响编译器输出会有所帮助。
习惯了运行时数据(SRAM)优化后,它会变得容易一些。对于初学者来说,一个非常常见的陷阱是使用过多的全局数据。在草图的整个生命周期中都会存在在全局范围内声明的所有内容,而这并非总是必要的。如果变量仅在一个函数中使用,并且不需要在两次调用之间持久存在,则将其设置为局部变量。如果需要在函数之间共享值,请考虑是否可以将其作为参数传递而不是使其全局。这样,仅在实际需要时才将SRAM用于这些变量。
SRAM使用的另一个杀手是文本处理(例如,使用String
类)。一般来说,如果可能,应避免执行String操作。他们是巨大的记忆猪。例如,如果您要向串行输出大量文本,请使用多个调用,Serial.print()
而不要使用字符串串联。如果可能,还尝试减少代码中字符串文字的数量。
尽可能避免递归。每次进行递归调用时,它将使堆栈更深。将您的递归函数重构为迭代的。
使用EEPROM
EEPROM用于长期存储仅偶尔更改的事物。如果需要使用较大的列表或固定数据查找表,请考虑将其预先存储在EEPROM中,并仅在必要时取出所需的内容。
显然,EEPROM的大小和速度受到很大限制,并且写入周期数有限。这不是解决数据限制的好方法,但它可能足以减轻闪存或SRAM的负担。还可以与类似的外部存储设备(例如SD卡)接口。
扩展
如果您用尽了所有其他选项,则可能会扩展。不幸的是,无法扩展闪存来增加程序空间。但是,它是可以扩展SRAM。这意味着您可以重构草图以减少代码大小,但要增加数据大小。
获得更多的SRAM实际上相当简单。一种选择是使用一个或多个23K256芯片。可通过SPI访问它们,并且有SpiRAM库可帮助您使用它们。请注意,它们的工作电压为3.3V 而不是 5V!
如果您使用的是Mega,则可以从Lagrangian Point或Rugged Circuits获得SRAM扩展屏蔽。
当您将代码上传到Arduino时,例如说一个Uno,它将告诉您在可用的32K内存中占用了多少字节。那就是你有多少闪存(想想电脑硬盘)。当您的程序运行时,它使用的是SRAM,并且可用的更少。
有时,您会注意到您的程序在一段时间内甚至没有被触及到的时候就表现得很奇怪。您最近的更改可能导致其内存不足(SRAM)。这里有一些关于如何释放一些SRAM的技巧。
我见过的最常见的事情之一是芯片用完了内存,因为有太多的长字符串。
使用F()
字符串时,请使用该函数,以便将它们存储在Flash中而不是SRAM中,因为您有更多的可用空间。
Serial.println(F("This string will be stored in flash memory"));
您可以通过将int
(2字节)切换为byte
(1字节)来保存一个字节。无符号字节将为您提供0-255,因此,如果您的数字不超过255,请保存一个字节!
通常,您会观察到您的程序表现异常,并想知道出了什么问题...您没有在混乱的点上更改代码中的任何内容,那么有什么用呢?它的内存不足。
有几个函数可以告诉您您有多少可用内存。
F()
功能是Arduino特定功能还是AVR库中的功能?您也可以考虑提一下PROGMEM const ...
。
除了别人所说的(我完全同意)之外,我建议您阅读这篇有关记忆力的adafruit文章。它写得很好,解释了很多有关内存的内容,并提供了有关如何优化内存的提示。
在阅读的结尾,我想您会得到一个非常完整的答案。
总结起来,您有2个可能的优化目标(取决于内存问题所在的位置):
F()
宏的常量字符串)还介绍了减少SRAM使用的另一种方法(但很少使用,因为它在编码时有点笨重,效率不高),它包含使用EEPROM存储程序所构建的数据,但直到某些情况下才使用可以从EEPROM装回数据时发生。
如果存储空间不足,则有两件事要做:
在线上有很多关于如何进行第一个操作的技巧(对于大多数人使用Arduino所做的事情,“优化”之后,内置存储绰绰有余)。因此,我将专注于第二个:
用完闪存或SRAM的东西有3种。每个人都需要一种略有不同的方法来添加存储:
变量存储:如sachleen所指出的,可以扩展SRAM。 SRAM,FRAM和NVSRAM都适用于快速变化的变量。(虽然原则上可以使用Flash存储变量,但是您必须担心Flash磨损)。SPI(串行协议)最容易连接到Arduino。该SpiRAM图书馆工作与Microchip 23K256串行SRAM芯片。Ramtron FM25W256串行FRAM芯片(赛普拉斯现在拥有)也使用SPI。赛普拉斯CY14B101 NVSRAM也使用SPI。等等。
下次上电时仍需要保持不变的数据:这几乎与扩展SRAM一样简单。有许多可用的外部EEPROM,FRAM,NVSRAM和FLASH存储设备。当前,每MB成本最低的是SD闪存卡(可通过SPI访问)。Ramtron FM25W256(参见上文),赛普拉斯CY14B101(参见上文)等也可以存储常数数据。许多扩展挡板都包括SD卡插槽,并且一些库和教程支持对(闪存)SD卡的读写。(我们不能为此使用SRAM,因为SRAM断电会忘记所有内容)。
可执行代码:不幸的是,扩展Arduino的闪存以增加程序空间是不可能的。但是,程序员始终可以重构草图以减小代码大小,但要以增加数据大小和使其运行稍慢为代价。(从理论上讲,您可以将整个草图翻译成某种解释语言,然后将您的草图版本存储在SD卡上,然后为在Arduino上运行的该语言编写一个解释器,以从中获取并执行指令。 SD卡-Arduino上的Forth,BASIC解释器,Tom Napier Picaro解释器,某些特定于应用程序的语言等)。