如果闪存或SRAM用完了怎么办?


28

根据Arduino文档,ATmega328具有用于引导程序+上传的草图的32KB闪存,以及用于运行时数据的2KB SRAM。ATmega2560具有更多功能,分别为256KB和8KB。

无论哪种情况,这些限制似乎都很小,尤其是与类似大小的消费设备(例如智能手机)相比时。

如果用尽了怎么办?例如,如果草图太大,或者需要在运行时处理大量数据(例如字符串)?有什么方法可以扩展Flash或SRAM?


尝试使用一些变量优化。这里
TheDoctor

1
如果尚未对变量使用适当的范围,则当变量未使用时,编译器将为您优化RAM的使用。
jippie 2014年

1
您实际上可能使用的是Teensy 3.2(带有Teensyduino软件),可以与Arduino媲美。鉴于您的RAM / PROGMEM即将用完,这意味着您也有可能尝试推动Arduino。Teensy 3.2并不贵。但具有:32位,72MHz(wtf !?)256KB闪存和64KB RAM和3个UART。没有比ATmega2560更多的程序存储器,但是RAM和速度的增加就足够了。
保罗

Answers:


30

优化
针对嵌入式系统的低级编程与针对通用设备(如计算机和手机)的编程大不相同。效率(在速度和空间方面)更为重要,因为资源非常宝贵。这意味着如果空间不足,要做的第一件事就是查看可以优化代码的哪些部分。

就减少程序空间(Flash)的使用而言,如果您没有经验,或者如果您更习惯于不需要使用该技能的台式计算机编程,则很难优化代码大小。不幸的是,虽然认真考虑草图的实际需求会有所帮助,但没有适用于所有情况的“魔术弹”方法。如果不需要某个功能,请将其取出。

有时,确定代码的多个部分相同(或非常相似)的位置也很有帮助。您可能可以将它们压缩为可重用的函数,可以从多个位置调用它们。但是,请注意,有时尝试使代码过于可重用实际上会使它变得更加冗长。实践中往往很难达成平衡。花一些时间查看代码更改如何影响编译器输出会有所帮助。

习惯了运行时数据(SRAM)优化后,它会变得容易一些。对于初学者来说,一个非常常见的陷阱是使用过多的全局数据。在草图的整个生命周期中都会存在在全局范围内声明的所有内容,而这并非总是必要的。如果变量仅在一个函数中使用,并且不需要在两次调用之间持久存在,则将其设置为局部变量。如果需要在函数之间共享值,请考虑是否可以将其作为参数传递而不是使其全局。这样,仅在实际需要时才将SRAM用于这些变量。

SRAM使用的另一个杀手是文本处理(例如,使用String类)。一般来说,如果可能,应避免执行String操作。他们是巨大的记忆猪。例如,如果您要向串行输出大量文本,请使用多个调用,Serial.print()而不要使用字符串串联。如果可能,还尝试减少代码中字符串文字的数量。

尽可能避免递归。每次进行递归调用时,它将使堆栈更深。将您的递归函数重构为迭代的。

使用EEPROM
EEPROM用于长期存储仅偶尔更改的事物。如果需要使用较大的列表或固定数据查找表,请考虑将其预先存储在EEPROM中,并仅在必要时取出所需的内容。

显然,EEPROM的大小和速度受到很大限制,并且写入周期数有限。这不是解决数据限制的好方法,但它可能足以减轻闪存或SRAM的负担。还可以与类似的外部存储设备(例如SD卡)接口。

扩展
如果您用尽了所有其他选项,则可能会扩展。不幸的是,无法扩展闪存来增加程序空间。但是,它可以扩展SRAM。这意味着您可以重构草图以减少代码大小,但要增加数据大小。

获得更多的SRAM实际上相当简单。一种选择是使用一个或多个23K256芯片。可通过SPI访问它们,并且有SpiRAM库可帮助您使用它们。请注意,它们的工作电压为3.3V 而不是 5V!

如果您使用的是Mega,则可以从Lagrangian PointRugged Circuits获得SRAM扩展屏蔽。


1
如果存在SRAM空间问题和可用程序存储器,则还可以将常量数据存储在程序存储器中,而不是SRAM中。看到这里这里
Connor Wolf

1
EEPROM的另一个很好的选择是SD卡。它确实占用了几个IO端口,但是如果您确实需要大量空间来存储例如地图数据或类似数据,则可以轻松地换出并使用PC上的自定义程序进行编辑。
匿名企鹅2014年

1
如果人们的内存不足,则不应该鼓励人们使用SPI SRAM或RAM扩展。那只是浪费钱。选择更大的MCU会更便宜。此外,性能可能很差。首先应该做出一个估算:如果估算的RAM使用率太接近极限,那么您选择的板/微控制器/开发平台是错误的。当然,良好的用法(将字符串存储在Flash中)和优化(避免使用某些库)可以真正改变游戏规则。但是在这一点上,我发现使用Arduino软件平台没有任何好处。
next-hack

24

当您将代码上传到Arduino时,例如说一个Uno,它将告诉您在可用的32K内存中占用了多少字节。那就是你有多少闪存(想想电脑硬盘)。当您的程序运行时,它使用的是SRAM,并且可用的更少。

有时,您会注意到您的程序在一段时间内甚至没有被触及到的时候就表现得很奇怪。您最近的更改可能导致其内存不足(SRAM)。这里有一些关于如何释放一些SRAM的技巧。

将字符串存储在Flash中而不是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 ...
jippie

同样,您可以使用位结构来进一步减少变量5eg使用的空间(例如,如果您处理大量的布尔值)。
jfpoilpret 2014年

17

除了别人所说的(我完全同意)之外,我建议您阅读这篇有关记忆力的adafruit文章。它写得很好,解释了很多有关内存的内容,并提供了有关如何优化内存的提示。

在阅读的结尾,我想您会得到一个非常完整的答案。

总结起来,您有2个可能的优化目标(取决于内存问题所在的位置):

  • 闪存(即程序存储器);为此,您可以:
    • 删除无效代码(例如,包含但未使用的任何代码)和未使用的变量(该代码也有助于SRAM)
    • 排除重复的代码
    • 完全删除引导加载程序(对于UNO,您可以获得0.5K,对于其他Arduino型号,您可以获得2或4K);这有一些缺点
  • SRAM(即堆栈,堆和静态数据);为此,您可以:
    • 删除未使用的变量
    • 优化每个变量的大小(例如,如果只需要int -2字节,则不要使用长-4字节)
    • 为变量使用正确的范围(并在可能的情况下优先使用堆栈而不是静态数据)
    • 将缓冲区大小减小到最小
    • 将常量数据移至PROGMEM(即,您的静态数据将保留在闪存中,并且在程序启动时不会复制到SRAM);这也适用于您可以使用F()宏的常量字符串)
    • 如果不是绝对必要的话,避免动态分配;您将避免即使在释放内存后也不会收缩的碎片堆

还介绍了减少SRAM使用的另一种方法(但很少使用,因为它在编码时有点笨重,效率不高),它包含使用EEPROM存储程序所构建的数据,但直到某些情况下才使用可以从EEPROM装回数据时发生。


1
删除无效代码-编译器确实可以为您解决这个问题-如果您有很多从未被调用过的代码,则不会有任何区别。如果您不小心调用了不需要的代码,那当然会有所不同。
dethSwatch

9

如果存储空间不足,则有两件事要做:

  • 以某种方式“优化”您的代码,因此需要更少的存储空间;或至少使用较少的特定存储类型(并使用更多的存储空间)。要么,
  • 添加更多存储空间。

在线上有很多关于如何进行第一个操作的技巧(对于大多数人使用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解释器,某些特定于应用程序的语言等)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.