Memoria Arduino
Arduino y todos los microcontroladores tienen varios tipos de memoria integradas en el mismo chip,
en el caso de Arduino y los microcontroladores AVR de Atmel usan tres tipos de memorias:
SRAM (static random access memory): Variables locales, datos parciales. Usualmente se trata
como banco de registros y memoria volátil. Es la zona de memoria donde el sketch crea y
manipula las variables cuando se ejecuta. Es un recurso limitado y debemos supervisar su uso
para evitar agotarlo.
EEPROM: Memoria no volátil para mantener datos después de un reset. Se puede grabar desde
el programa del microcontrolador, usualmente, constantes de programa. Las EEPROMs tienen
un número limitado de lecturas/escrituras, tener en cuenta a la hora de usarla. Esta memoria solo
puede leerse byte a byte y su uso puede se un poco incómodo. También es algo más lenta que la
SRAM. La vida útil de la EEPROM es de unos 100.000 ciclos de escritura
Flash: Memoria de programa. Usualmente desde 1 Kb a 4 Mb (controladores de familias
grandes). Es donde se guarda el sketch ya compilado. Sería el equivalente al disco duro de un
ordenador. En la memoria flash también se almacena del bootloader. Se puede ejecutar un
programa desde la memoria flash, pero no es posible modificar los datos, sino que es necesario
copiar los datos en la SRAM para modificarlos.
La memoria flash usa la misma tecnología que las tarjetas SD, los pen drives o algunos tipos de
SSD, esta memoria tiene una vida útil de unos 100.000 ciclos de escritura, así que cargando 10
programas al día durante 27 años podríamos dañar la memoria flash.
Memorias en Arduino:
La memoria flash y la EEPROM son no volátiles, es decir, la información persiste tras el apagado del
Arduino.
Memoria de Arduino UNO:
Flash 32k bytes (of which 0.5k is used for the bootloader)
SRAM 2k bytes
EEPROM 1k byte
Memoria de Arduino MEGA:
Flash 256k bytes (of which 8k is used for the bootloader)
SRAM 8k bytes
EEPROM 4k byte
Memoria de Arduino MKR1000:
Flash 256k bytes
SRAM 32k bytes
EEPROM no. Dispone de EEPROM emulation en la memoria flash (ver documentación del
microcontrolador)
Memoria ESP8266:
64 KiB of instruction RAM, 96 KiB of data RAM
External QSPI flash – 512 KiB to 4 MiB (no dispone de memoria Externa)
La memoria SRAM es un recurso escaso que debe gestionarse, especialmente si se usan los strings o
cadenas de caracteres de forma intensiva. Si un Arduino se queda sin memoria SRAM, el sketch
compilará bien y se cargará en el Arduino sin problema, pero se producirán efectos inesperados.
En caso de usar muchos strings, una técnica para evitar agotar la memoria SRAM es guardar en la
memoria flash los strings que no se modifiquen en tiempo de ejecución, usando PROGMEM:
[Link]
Desde la versión 1.0 del IDE de Arduino, se introdujo la macro F(). Esta sintaxis se usa para almacenar
strings en la memoria flash en lugar de en la memoria SRAM. No es necesario cargar ninguna librería
para usar la macro F().
[Link](“This string will be stored in flash memory”); //este print ocupará 42 bytes de
memoria SRAM con el contenido de la constante string
[Link](F(“This string will be stored in flash memory”)); //el string dentro de del println no
se carga en la SRAM y se lee de la flash
En el caso que el sketch ocupe más memoria flash, el IDE te avisa de que no puede cargarlo en Arduino.
Desde las últimas versiones del IDE de Arduino tras compilar el sketch, aparece un resumen de la
memoria flash que ocupa el programa y la memoria ocupada por las variables globales en la SRAM y el
espacio que queda para las variables locales. Como recomendación, si se supera el 70%-75% de
la SRAM con las variables globales es muy probable que Arduino se quede sin memoria RAM.
Recordar que al incluir una librería, estoy añadiendo variables y tamaño al programa, lo que aumentará
el uso de memoria SRAM y flash. Algunas librerías hacen un uso grande de la memoria SRAM y flash.
Memoria en Arduino:
[Link]
[Link]
Un buen tutorial para aprender como funcionan las memorias de Arduino:
[Link]
when-dot-dot-dot
Toda la información de las memoria del ATMega328p está en la página 34 del datasheet
[Link]
328P_datasheet.pdf
Más información de las memorias de Arduino
MCU vs CPU en funcion de la arquitectura de la memoria: [Link]
of-an-arduino/arduino-memory-architecture
Como medir la memoria libre que tenemos en Arduino: [Link]
of-an-arduino/measuring-free-memory
Como saber cuando me he quedado sin memoria:
[Link]
Consumidores de Memoria en Arduino: [Link]
arduino/large-memory-consumers
Como optimizar la memoria flash: [Link]
arduino/optimizing-program-memory
Cómo optimizar la memoria SRAM: [Link]
arduino/optimizing-sram
Para profundizar sobre el uso de memoria en los microcontroladores AVR de Atmel, memoria dinámica,
reserva y liberar memoria, visitar los siguientes enlaces:
[Link]
[Link]
Memoria SRAM
Al ser el recurso más escaso en Arduino hay que entender bien cómo funciona. La memoria SRAM puede
ser leída y escrita desde el programa en ejecución.
La memoria SRAM es usada para varios propósitos:
Static Data: Este bloque de memoria reservado en la SRAM para todas las variables globales y
estáticas. Para variables con valores iniciales, el sistema copia el valor inicial desde la flash al
iniciar el programa.
Heap: Es usado para las variables o elementos que asignan memoria dinámicamente. Crece
desde el final de la zona de Static Data a medida que la memoria es asignada. Usada por
elementos como los objetos y los Strings.
Stack: Es usado por las variables locales y para mantener un registro de las interrupciones y las
llamadas a funciones. La pila crece desde la zona más alta de memoria hacia el Heap. Cada
interrupción, llamada de una función o llamada de una variable local produce el crecimiento de
la memoria.
La mayor parte de los problemas ocurren cuando la pila y el Heap colisionan. Cuando esto ocurre
una o ambas zonas de memoria se corrompen con resultados impredecibles. En uno casos se
produce un “cuelgue” del programa y en otros casos la corrupción de memoria puede notarse
un tiempo después.
A partir de la versión 1.6 del IDE al compilar un sketch nos da el tamaño que va a ocupar en la flash el
proyecto y el espacio que va a ocupar en la SRAM las variables globales, es decir, la zona de static data.
Como ya se ha visto anteriormente, en la memoria SRAM también se encuentran los registros que ocupan
las primeras 256 direcciones de memoria. Por lo tanto la SRAM empieza a partir de la dirección 0x0100.
Los registros al estar en la SRAM son volátiles y no conservan su valor después de un reset. Mirando la
documentación del microcontrolador se puede ver cuales son los valores por defecto de los registros.
Si vemos a fondo la memoria SRAM de Arduino a partir de la dirección 0x0100:
.data variables is the first RAM section and it is used to store program static data, such as strings,
initialized structures and global variables.
.bss variables is the memory allocated for uninitialized global and static variables.
heap is the dynamic memory area, and this is the playground area for malloc (and alike). The
heap can grow (when new allocation is made) or “possibly” decrease in size (when memory is
released, as for example when using free) based on the requirements.
stack is the memory area located at the end of the RAM and it grows towards the heap area.
The stack area is used for function calls, storing values for local variables. Memory occupied by
local variables is reclaimed when the function call finished.
external RAM is only available to some of the MCUs and it means that it is possible to add RAM
in a kind of similar way that we do for a PC. Usually this is expensive (a few KB of external RAM
costs in general more than the MCU) and requires also advanced hardware and software skills.
free available memory is the area between heap and stack and this is what we need to measure
in order to detect problems caused by not enough RAM [Link] this area is either too
small for the required tasks, or is missing at all (heap meets stack), our MCU starts to missbehave
or to restart itself.
Más información sobre heap y stack:
[Link]
[Link]
Calcular Memoria SRAM Libre
El siguiente código permite calcular la memoria libre en bytes para un Arduino y funciona tanto con el
IDE de Arduino como con Atmel Studio:
1 extern unsigned int __bss_end;
2 extern unsigned int __heap_start;
3 extern void *__brkval;
4
5 uint16_t getFreeSram() {
6 uint8_t newVariable;
7 // heap is empty, use bss as start memory address
8 if ((uint16_t)__brkval == 0)
9 return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
10 // use heap end as the start of the memory address
11 else
12 return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
};
13
Explicación del código:
extern en un cualificador de variables que indica al compilador de la existencia de variables
globales definidas en otros fichero de cabecera que hemos incluido (#include) y no es necesario
definirlas en nuestro fichero.
extern int x; le dice al compilador que un objeto llamado x de tipo int existe en algún sitio.
La función getFreeRam define una nueva variable (llamada NewVariable), que se se almacenará
una variable local de una función en la pila (stack). Debido a que el área de memoria de pila
crece hacia el heap, la dirección de memoria de esta nueva variable es la última dirección de
memoria utilizada por la pila en el momento de llamar a este método.
El *__brkval es un puntero a la última dirección de memoria (hacia la pila) utilizado por el heap.
No tenemos que preocuparnos acerca de la gestión de __brkval ya que esto se hace
internamente.
También tenemos que estar seguros de que heap no está vacío, porque entonces __brkval no
se puede utilizar (que es un puntero NULL). Si el heap está vacío, entonces usamos __bss_end
que es una variable definida internamente, y se almacena en la última parte de la zona de
memoria RAM variables de .bss
La cantidad de memoria libre de RAM es la diferencia entre las direcciones de memoria usada
por la nueva variable newVariable y dirección referenciada por __brkval o la dirección de
_bss_end si la zona de heap está vacía.
El resultado es el número de bytes en MCUs de 8 bits como en Arduino uno. En el caso de MCUs
de 32 bits como el Arduino DUE, que son bloques de 32 bits.
Más información: [Link]
Este código está disponible en la librería: [Link]
Y la versión revisada: [Link]
En el playground de Arduino hay más información sobre el cálculo de memoria disponible:
[Link]
Un código más simple de calcular la memoria libre:
1int freeRam () {
extern int __heap_start, *__brkval;
2
int v;
3
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int)
4__brkval);
5}
Códigos para cálculo de memoria libre SRAM
[Link]
[Link]
[Link]
[Link]
Como medir a memoria libre flash, SRAM y EEPROM: [Link]
arduino/measuring-free-memory
I/O Memory
El espacio de memoria I/O de los registros son un conjunto de registros adiciona a los registros de
propósito general.
Los 32 (32x8bits) registros de propósito general están directamente conectados con la ALU.
Todas las I/Os (Inputs/Oputputs) y periféricos están localizados en el espacio de memoria I/O. Todas las
localizaciones de los registros I/O pueden ser accedidos mediante las instrucciones LD/LDS/LDD y
ST/STS/STD, transfiriendo datos entre los registros de propósito general y el espacio I/O.
Instrucción LDS (Load Direct from Data Space):
[Link]
Instrucción SBI (Set Bit in I/O Register):
[Link]
Instrucción CBI (CBI – Clear Bit in I/O Register):
[Link]
Lista de instrucciones en
ensamblador: [Link]
Los registros I/O en el rango 0x00-0x1F son accesible directamente mediante las funciones SBI y
CBI. En estos registros el valor de los bits puede ser leído con las instrucciones SBIS y SBIC.
Memoria Flash
La memoria flash —derivada de las siglas EEPROM— permite la lectura y escritura de múltiples
posiciones de memoria en la misma operación. Gracias a ello, la tecnología flash, siempre mediante
impulsos eléctricos, permite velocidades de funcionamiento muy superiores frente a la tecnología
EEPROM primigenia, que sólo permitía actuar sobre una única celda de memoria en cada operación de
programación. Se trata de la tecnología empleada en los dispositivos denominados memoria USB.
Más sobre la memoria flash.
[Link]
[Link]
En Arduino la memoria flash o espacio de programa es donde el sketch de arduino en binario es
almacenado.
La memoria flash en Arduino está dividida o particionada en dos zonas una para el bootloader y otra
para almacenar el sketch.
El bootloader se trata de un programa especial y puede leer datos de una fuente externa como UART,
I2C, CAN, etc… para reescribir el programa guardado en la memoria flash del microcontrolador. El
bootloader es un programa que se ejecuta inmediatamente antes de ejecutar el programa que hay en la
memoria flash al que cede el control cuando finaliza su ejecución.
Más información sobre el bootloader en:
[Link]
Incluso se podría particionar la memoria flash para dejar una zona con un sistema de ficheros donde se
podrían almacenar archivos, como ya se hace en algunos casos con el ESP8266.
PROGMEM
PROGMEM se usa para guardar en memoria flash en lugar de en la SRAM y ahorrar espacio en la
SRAM, especialmente cuando se usa gran cantidad de cadenas de caracteres. La palabra PROGMEM en
un modificador de variable que debe usarse solo con los tipos de datos definidos en pgmspace.h. Al
usarlo le dice al compilador que ponga la información de la variable en la memoria flash en lugar de la
SRAM, donde iría normalmente.
PROGMEM es parte de la librería pgmspace.h [Link]
manual/group__avr__pgmspace.html que solo está disponible para la arquitectura AVR, así que para
usarlo hay que inclirla al principio del sketch con #include <avr/pgmspace.h>
Más información:
sizeof() – devuelve el número de bytes en una variable o el número de bytes ocupados por un
array.
PROGMEM
En muchos casos, una gran cantidad de RAM es ocupada por la memoria estática, como resultado del
uso de variables globales (tales como cadenas o números). Siempre que estos datos no se vayan a
cambiar, puede ser fácilmente almacenado en la llamada PROGMEM (memoria de programa o flash).
Esto ocuparía un trozo de la memoria flash, y es bueno saber que, en general, la memoria flash es mucho
más grande que la memoria RAM (por ejemplo, Atmega2560 tiene 8 KB de RAM y flash de 256 KB).
Una desventaja de usar PROGMEM podría ser la velocidad de lectura, que es más lento en comparación
con la lectura de los mismos datos de la RAM, aunque la diferencia de velocidad no es mucha.
La verdadera utilidad de PROGMEM es en bloques de datos grandes que necesitan ser almacenados en
la flash. El uso de PROGMEM se hace en dos pasos, después de hacer que la variable se guarde en la
flash, necesitamos de varios métodos definidos en la librería pgmspace.h, para leer los datos de la flash
y cargarlos en la SRAM.
IMPORTANTE: para el uso de PROGMEM, las variables deben ser o bien definidas de forma global o
definidas como static.
La sintaxis de uso de PROGMEM es:
1const dataType variableName[] PROGMEM = {}; // use this form
2 const PROGMEM dataType variableName[] = {}; // or this form
3const dataType PROGMEM variableName[] = {}; // not this one
Más información sobre PROGMEM en Arduino:
[Link]
[Link]
[Link]
La macro F() se puede usar para facilitar el manejo de PROGMEM en las instrucciones print, de forma
que todo el texto a imprimir (ya sea en Serial, Ethernet u otra librería) se lea de la Flash y no ocupando
tamaño en la SRAM. Esta macro está incluida en el core de Arduino.
Cuando un sketch tiene problemas de memoria SRAM, el primer y más sencillo paso a aplicar es poner
todos los print con la macro F.
Sintaxis:
[Link](F("Write something on the Serial Monitor that is stored
1
in FLASH"));
Más información sobre la macro F y consideraciones a tener en cuenta al usarla:
[Link]
Ver librería Flash que facilita el uso de PROGMEM: [Link]
Fuses & Lock Bits
Ya sabemos lo que es la flash, EEPROM y RAM como parte de la MCU, pero no se ha mencionado
anteriormente que hay 3 bytes de memoria permanente llamados fuses.
Los fuses determinan cómo se va a comportar el microcontrolador, si tiene bootloader, a que velocidad
y voltaje va a funcionar la MCU, etc… Notar que a pesar de llamarse fuses (fusibles) puede configurarse
una y otra vez y no tienen nada que ver con la protección de sobrecorrientes.
Los tres bytes que conforman los fuses en el ATMega328p:
También hay un cuarto byte que se usa para programar los lock bits. Los lock bits pueden ser usados para
restringir la lectura/escritura en la memoria de programa (flash), la zona de boot y la de aplicación tienen
sus propios lock bits y es posible controlar el acceso a ellos de forma separada.
Para cambiar la configuración de los fuses, puede usarse el [Link] para cambiar la
configuración de los fuses: [Link]
Atmega328p-8bit-mic/
Arduino default fuse settings: [Link]
settings/
Calculadora de Fuses:
Para ATMega328p: [Link]
[Link]
[Link]
Más información:
[Link]
[Link]
[Link]
on-mac/
[Link]
Práctica: Uso de Memoria en Arduino
Para entender el uso de la memoria, hagamos una práctica añadiendo y quitando elementos del sketch y
viendo la ocupación de memoria.
Función para calcular memoria libre en Arduino:
[Link]
MemoryFree: [Link]
MemoryFree revisada: [Link]
Función freeRam:
1int freeRam () {
extern int __heap_start, *__brkval;
2
int v;
3
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int)
4__brkval);
5}
Paso 1: Calcula memoria RAM y Flash en Arduino UNO de un programa que solo ejecute la función
freeRam en el loop cada 30 segundos y muestre el dato por el monitor serie.
Solución Ejercicio14_1: [Link]
Curso_Arduino_Avanzado_2017/tree/master/Ejercicio14-Memoria_1
Paso 2: Calcula memoria RAM y Flash en Arduino UNO de un programa que imprima por el monitor
serie en cada loop cada 30 segundos el siguiente texto:
“Arduino es una plataforma de hardware libre, basada en una placa con un microcontrolador y un
entorno de desarrollo, diseñada para facilitar el uso de la electrónica en proyectos multidisciplinares.
El hardware consiste en una placa con un microcontrolador Atmel AVR y puertos de entrada/salida. Los
microcontroladores más usados son el Atmega168, Atmega328, Atmega1280, ATmega8 por su sencillez
y bajo coste que permiten el desarrollo de múltiples diseños. Por otro lado el software consiste en un
entorno de desarrollo que implementa el lenguaje de programación Processing/Wiring y el cargador de
arranque que es ejecutado en la [Link] octubre de 2012, Arduino se usa también con
microcontroladoras CortexM3 de ARM de 32 bits,5 que coexistirán con las más limitadas, pero también
económicas AVR de 8 bits. ARM y AVR no son plataformas compatibles a nivel binario , pero se pueden
programar con el mismo IDE de Arduino y hacerse programas que compilen sin cambios en las dos
plataformas. Eso sí, las microcontroladoras CortexM3 usan 3,3V, a diferencia de la mayoría de las
placas con AVR que generalmente usan 5V. Sin embargo ya anteriormente se lanzaron placas Arduino
con Atmel AVR a 3,3V como la Arduino Fio y existen compatibles de Arduino Nano y Pro como Meduino
en que se puede conmutar el voltaje.”
Solución Ejercicio14_2: [Link]
Curso_Arduino_Avanzado_2017/tree/master/Ejercicio14-Memoria_2
Paso 3: Calcula memoria RAM y Flash en Arduino UNO del programa del paso 2 pero usando la macro
F en el [Link].
Solución Ejercicio14_3: [Link]
Curso_Arduino_Avanzado_2017/tree/master/Ejercicio14-Memoria_3
Práctica: Velocidad Memoria en Arduino
Hacer un programa para hacer una comparativa de la velocidad de lectura de la memoria SRAM, Flass
y EEPROM.
[Link]