¿Qué puedo hacer si me quedo sin memoria Flash o SRAM?

28

Según la documentación de Arduino, el ATmega328 tiene 32 KB de memoria Flash para el cargador de arranque + boceto cargado, y solo 2 KB de SRAM para datos de tiempo de ejecución. El ATmega2560 tiene bastante más, totalizando 256 KB y 8 KB respectivamente.

En cualquier caso, esos límites parecen bastante pequeños, especialmente cuando se comparan con dispositivos de consumo de tamaño similar, como los teléfonos inteligentes.

¿Qué puedes hacer si se te acaba? Por ejemplo, si su boceto es demasiado grande o si necesita procesar una gran cantidad de datos (como cadenas) en tiempo de ejecución? ¿Hay alguna forma de expandir Flash o SRAM?

Peter Bloomfield
fuente
Intenta usar alguna optimización variable. Aquí
TheDoctor
1
Utilice los ámbitos adecuados para sus variables, si aún no lo ha hecho, el compilador optimizará el uso de RAM cuando las variables no se usen.
jippie
1
En realidad, podría usar un Teensy 3.2 (con el software Teensyduino), que sería comparable a un Arduino. Dado que se está quedando sin RAM / PROGMEM, significaría que también es probable que lo que intente hacer sea empujar su Arduino. Un Teensy 3.2 no es mucho más caro. pero tiene: 32bit, 72MHz (wtf !?) 256KB FLASH y 64KB RAM y 3x UART. No más memoria de programa como el ATmega2560, pero la RAM y el aumento de velocidad deberían ser suficientes.
Paul

Respuestas:

30

Optimización
La programación de bajo nivel para sistemas integrados es bastante diferente de la programación para dispositivos de uso general, como computadoras y teléfonos celulares. La eficiencia (en términos de velocidad y espacio) es mucho más importante porque los recursos son muy importantes. Eso significa que lo primero que debe hacer si se queda sin espacio es mirar qué partes de su código puede optimizar.

En términos de reducir el uso del espacio del programa (Flash), el tamaño del código puede ser bastante difícil de optimizar si no tiene experiencia o si está más acostumbrado a programar para computadoras de escritorio que no tienden a necesitar esa habilidad. Desafortunadamente, no existe un enfoque de 'bala mágica' que funcione para todas las situaciones, aunque ayuda si considera seriamente lo que realmente necesita tener su boceto . Si no se necesita una función, elimínela.

A veces también es útil identificar dónde varias partes de su código son iguales (o muy similares). Es posible que pueda condensarlos en funciones reutilizables que se pueden llamar desde varios lugares. Sin embargo, tenga en cuenta que a veces tratar de hacer que el código sea demasiado reutilizable en realidad termina haciéndolo más detallado. Es un equilibrio difícil de alcanzar que tiende a venir con la práctica. Dedicar un tiempo a observar cómo los cambios en el código afectan la salida del compilador puede ayudar.

La optimización de datos de tiempo de ejecución (SRAM) tiende a ser un poco más fácil cuando estás acostumbrado. Una trampa muy común para los programadores principiantes es utilizar demasiados datos globales. Todo lo declarado a nivel mundial existirá durante toda la vida útil del boceto, y eso no siempre es necesario. Si una variable solo se usa dentro de una función, y no necesita persistir entre llamadas, entonces conviértala en una variable local. Si un valor necesita ser compartido entre funciones, considere si puede pasarlo como un parámetro en lugar de hacerlo global. De esa manera, solo usará SRAM para esas variables cuando realmente lo necesite.

Otro asesino para el uso de SRAM es el procesamiento de texto (por ejemplo, usando la Stringclase). En términos generales, debe evitar realizar operaciones de cadena si es posible. Son enormes cerdos de memoria. Por ejemplo, si está generando una gran cantidad de texto en serie, use varias llamadas a en Serial.print()lugar de usar la concatenación de cadenas. También trate de reducir el número de literales de cadena en su código si es posible.

Evite la recursividad si es posible también. Cada vez que se realiza una llamada recursiva, lleva a la pila un nivel más profundo. Refactorice sus funciones recursivas para que sean iterativas.

Usar EEPROM
EEPROM se usa para el almacenamiento a largo plazo de cosas que solo cambian ocasionalmente. Si necesita utilizar listas grandes o tablas de búsqueda de datos fijos, considere almacenarlos en EEPROM por adelantado y solo extraiga lo que necesite cuando sea necesario.

Obviamente, EEPROM es bastante limitado en tamaño y velocidad, y tiene un número limitado de ciclos de escritura. No es una gran solución para las limitaciones de datos, pero podría ser suficiente para aliviar la carga de Flash o SRAM. También es bastante posible interactuar con un almacenamiento externo similar, como una tarjeta SD.

Expansión
Si ha agotado todas las demás opciones, la expansión puede ser una posibilidad. Desafortunadamente, no es posible expandir la memoria Flash para aumentar el espacio del programa. Sin embargo, es posible expandir SRAM. Esto significa que puede refactorizar su boceto para reducir el tamaño del código a expensas de aumentar el tamaño de los datos.

Obtener más SRAM es bastante sencillo. Una opción es usar uno o más chips 23K256 . Se accede a ellos a través de SPI, y existe la biblioteca SpiRAM para ayudarlo a usarlos. ¡Solo tenga en cuenta que funcionan a 3.3V, no a 5V!

Si está utilizando el Mega, también puede obtener escudos de expansión SRAM de Lagrangian Point o Rugged Circuits .

Peter Bloomfield
fuente
1
También puede almacenar datos constantes en la memoria del programa, en lugar de SRAM, si tiene problemas de espacio SRAM y memoria de programa libre. Ver aquí o aquí
Connor Wolf
1
Otra gran alternativa a EEPROM es una tarjeta SD. Ocupa algunos puertos IO, pero si necesita una gran cantidad de espacio para, digamos datos de mapas o similares, puede ser fácil intercambiar y editar con un programa personalizado en una PC.
Anonymous Penguin
1
No se debe alentar a las personas a usar SPI SRAM o expansiones de RAM, si se están quedando sin memoria. Eso es solo una pérdida de dinero. Elegir un MCU más grande sería más barato. Además, el rendimiento podría ser muy pobre. Primero se debe hacer una estimación aproximada: si el uso estimado de RAM está demasiado cerca del límite, entonces está eligiendo la placa / microcontrolador / plataforma de desarrollo incorrecta. Claro, el buen uso (almacenamiento de cadenas en flash) y la optimización (evitando el uso de algunas bibliotecas) pueden ser verdaderos cambios en el juego. Sin embargo, en este punto no veo beneficios de usar la plataforma Arduino Software.
next-hack el
24

Cuando cargue su código en su Arduino, diga un Uno, por ejemplo, le dirá cuántos bytes usa de los 32K disponibles. Esa es la cantidad de memoria flash que tiene (piense en el disco duro de la computadora). Mientras su programa se está ejecutando, está utilizando lo que se llama SRAM, y hay mucho menos de eso disponible.

A veces notarás que tu programa se comporta de manera extraña en un punto que ni siquiera has tocado en mucho tiempo. Podría ser que sus cambios más recientes causen que se quede sin memoria (SRAM). Aquí hay algunos consejos sobre cómo liberar algo de SRAM.

Almacenar cadenas en Flash en lugar de SRAM.

Una de las cosas más comunes que he visto es que el chip se está quedando sin memoria porque hay demasiadas cadenas largas.

Use la F()función cuando use cadenas para que se almacenen en Flash en lugar de SRAM, ya que tiene mucho más de eso disponible.

Serial.println(F("This string will be stored in flash memory"));

Use los tipos de datos correctos

Puede guardar un byte cambiando de int(2 bytes) a byte(1 byte). Un byte sin signo le dará 0-255, así que si tiene números que no superan los 255, ¡guarde un byte!

¿Cómo sé que me estoy quedando sin memoria?

Por lo general, observará que su programa se comporta de manera extraña y se preguntará qué salió mal ... No cambió nada en el código cerca del punto en el que está en mal estado, entonces, ¿qué le pasa? Se está quedando sin memoria.

Hay un par de funciones para decirle cuánta memoria disponible tiene.

Memoria disponible

Sachleen
fuente
¿Sabes si la F()cosa es una función específica de Arduino o está en las bibliotecas AVR? Podría considerar mencionar PROGMEM const ...también.
jippie
También puede usar estructuras de bits para reducir aún más el espacio utilizado por sus variables 5eg si trata con muchos valores booleanos).
jfpoilpret
17

Además de lo que otros han dicho (en lo que estoy totalmente de acuerdo), recomendaría leer este artículo de adafruit sobre la memoria; Está bien escrito, explica muchas cosas sobre la memoria y proporciona pistas sobre cómo optimizarla.

Al final de la lectura, creo que obtendría una respuesta bastante completa a su pregunta.

Para resumir, tiene 2 posibles objetivos de optimización (dependiendo de dónde se encuentren los problemas de memoria):

  • Flash (es decir, memoria de programa); para esto, puedes:
    • eliminar el código muerto (p. ej., cualquier código que se incluya pero no se use) y las variables no utilizadas (eso también ayuda con SRAM)
    • factorizar código duplicado
    • elimine el cargador de arranque por completo (puede ganar entre 0.5K para UNO y 2 o 4K para otros modelos Arduino); aunque esto tiene algunas desventajas
  • SRAM (es decir, pila, montón y datos estáticos); para esto puedes:
    • eliminar variables no utilizadas
    • optimice el tamaño de cada variable (por ejemplo, no use long -4 bytes, si solo necesita int -2 bytes)
    • use el alcance correcto para sus variables (y prefiera apilar a datos estáticos cuando sea posible)
    • reducir el tamaño de los tampones al mínimo estricto
    • mover datos constantes a PROGMEM (es decir, sus datos estáticos permanecerán en la memoria Flash y no se copiarán a SRAM al inicio del programa); eso también se aplica a cadenas constantes para las que puede usar F()macro)
    • evite la asignación dinámica si no es absolutamente necesario; evitará un montón fragmentado que puede no reducirse incluso después de liberar memoria

También se describe un enfoque adicional para reducir el uso de SRAM (pero rara vez se usa, porque es un poco pesado al codificar y no es muy eficiente), consiste en usar EEPROM para almacenar datos construidos por su programa, pero no se usa hasta más tarde cuando algunas condiciones ocurrir, cuando los datos se pueden volver a cargar desde EEPROM.

jfpoilpret
fuente
1
Eliminar el código muerto (el compilador es realmente bueno para manejar esto por usted), no habrá ninguna diferencia si tiene un montón de código que nunca se llama. Si accidentalmente llama a un código que no necesita, entonces eso es diferente, por supuesto.
dethSwatch
9

Hay dos cosas que hacer si se queda sin almacenamiento:

  • De alguna manera, "optimice" su código para que necesite menos almacenamiento; o al menos usa menos del tipo particular de almacenamiento que se le acabó (y usa más del tipo de almacenamiento que aún tiene). O,
  • Agrega más espacio de almacenamiento.

Hay muchos consejos en línea sobre cómo hacer lo primero (y para la gran mayoría de las cosas que las personas hacen con el Arduino, el almacenamiento incorporado es más que suficiente después de "optimizar"). Entonces me enfocaré en el segundo:

Hay 3 cosas que usan flash o SRAM; cada uno necesita un enfoque ligeramente diferente para agregar almacenamiento:

  • almacenamiento variable: es posible expandir SRAM, como ya ha señalado sachleen. SRAM, FRAM y NVSRAM son todos apropiados para variables que cambian rápidamente. (Si bien, en principio, podría usar flash para almacenar variables, entonces debe preocuparse por el desgaste del flash). SPI (un protocolo en serie) es el más fácil de conectar al Arduino. La biblioteca SpiRAM funciona con el chip SRAM serie MicroKip 23K256 . El chip FRAM serie FM25W256 de Ramtron (ahora propiedad de Cypress) también usa SPI. El Cypress CY14B101 NVSRAM también usa SPI. Etc.

  • datos constantes que aún deben estar allí la próxima vez que se active la alimentación: esto es casi tan simple como expandir la SRAM. Hay muchos dispositivos externos de almacenamiento EEPROM, FRAM, NVSRAM y FLASH disponibles. Actualmente, el costo más bajo por MB son las tarjetas flash SD (a las que se puede acceder a través de SPI). El Ramtron FM25W256 (ver arriba), el Cypress CY14B101 (ver arriba), etc. también pueden almacenar datos constantes. Muchos protectores de expansión incluyen una ranura para tarjeta SD, y varias bibliotecas y tutoriales admiten lectura y escritura en tarjetas SD (flash). (No podemos usar SRAM para esto, porque SRAM olvida todo cuando se va la luz).

  • código ejecutable: Desafortunadamente, no es posible expandir la memoria Flash de Arduino para aumentar el espacio del programa. Sin embargo, un programador siempre puede refactorizar un boceto para reducir el tamaño del código a expensas de aumentar el tamaño de los datos y hacer que se ejecute un poco más lento. (En teoría, podría ir tan lejos como traducir todo su boceto a algún idioma interpretado, almacenar esa versión de su boceto en una tarjeta SD y luego escribir un intérprete para ese idioma que se ejecuta en el Arduino para buscar y ejecutar instrucciones del Tarjeta SD: adelante en Arduino , un intérprete BÁSICO, un intérprete Tom Napier Picaro, un lenguaje específico de la aplicación, etc.).

David Cary
fuente