Almacene los datos en la memoria FLASH (memoria de programa) en lugar de RAM (a veces nombrada como SRAM, del inglés STATIC RAM = RAM estática).
Los distintos tipos de memoria disponibles en una placa Arduino son:
¦ FLASH: Esta memoria almacena el código del programa.
¦ RAM: Esta memoria almacena los datos del programa y es volátil. Cuando se apaga Arduino se pierden los datos.
¦ EEPROM: Esta memoria almacena datos que son persistentes. Esta memoria puede ser utilizada para guardar configuración o cualquier otro dato que podamos utilizar si se apaga y enciende Arduino.
La palabra clave PROGMEM es un modificador de variable. Se debe usar solo con los tipos de datos definidos en la biblioteca pgmspace.h.
PROGMEM le dice al compilador «ponga esta información en la memoria flash», en lugar de en la RAM, donde iría normalmente.
PROGMEM es parte de la biblioteca pgmspace.h. Ésta se incluye automáticamente en las versiones modernas del IDE. Sin embargo, si estuviese utilizando una versión IDE por debajo de 1.0 (2011), primero deberá incluir la biblioteca en la parte superior de su programa, con esta declaración:
1 |
#include <avr/pgmspace.h> |
Sintaxis
1 |
const tipoDato nombreVariable[] PROGMEM = {dato0, dato1, dato2…}; |
tipoDato – algún tipo de variable
nombreVariable – el nombre de su matriz de datos
Tenga en cuenta que debido a que la indicación PROGMEM es un modificador de variable, no existe una regla estricta sobre dónde debe ir, por lo que el compilador acepta diversas posiciones, que también pueden ser sinónimos. Sin embargo, las pruebas han indicado que, en varias versiones de Arduino, PROGMEM puede funcionar en una ubicación y no en otra.
Los siguientes ejemplos se han probado y funcionan con los IDE actuales. Las versiones anteriores del IDE pueden funcionar mejor si la palabra PROGMEM se incluye después del nombre de la variable.
1 2 3 |
const dataType variableName[] PROGMEM = {}; // use esta sintaxis const PROGMEM dataType variableName[] = {}; // no esta const dataType PROGMEM variableName[] = {}; // ni esta |
Tenga en cuenta que dado que PROGMEM se podría usar con una sola variable, pero en realidad solo vale la pena si tiene un bloque de datos más grande para almacenar, que por lo general es más fácil de poner en una matriz (o en otra estructura de datos C++, algo que queda más allá de la presente discusión).
Usar PROGMEM es un procedimiento de dos pasos. Después de tener los datos que están en la memoria Flash, se requieren métodos especiales (funciones), también definidos en la biblioteca pgmspace.h, para volcar los datos de la memoria de programa a la RAM, de modo de que podamos trabajar con ellos.
Código de ejemplo
Las siguientes secciones de código ilustran cómo leer y escribir unsigned char (bytes) e int (2 bytes) en PROGMEM.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// guardar algunos enteros sin signo (unsigned int) #include <avr/pgmspace.h> const uint16_t grupoNumeros[] PROGMEM = { 65000, 32796, 16843, 10, 11234}; // guardar algunos caracteres (char) const char Mensaje[] PROGMEM = {"ESTOS CARACTERES SE UTILIZARÁN, MÁS ADELANTE, EN ALGUN LUGAR"}; unsigned int intParaMostrar; int k; // variable para contar char miCaracter; void setup() { Serial.begin(9600); while (!Serial); // esperar que el puerto serie se conecte // Su código va aquí, en inicialización, para que corra una sola vez // Lee enteros de 2 bytes y los muestra for (k = 0; k < 5; k++) { intParaMostrar = pgm_read_word_near(grupoNumeros + k); Serial.println(intParaMostrar); } Serial.println(); // Leer caracteres y mostrarlos for (k = 0; k < strlen_P(Mensaje); k++) { miCaracter = pgm_read_byte_near(Mensaje + k); Serial.print(miCaracter); } Serial.println(); } void loop() { // ponga aquí su código, donde correrá continuamente } |
Matrices de cadenas de caracteres
A menudo es conveniente configurar una serie de cadenas de caracteres cuando se trabaja con grandes cantidades de texto, como por ejemplo en un proyecto con una pantalla LCD.
Debido a que las cadenas en sí son matrices, en realidad este es un ejemplo de una matriz bidimensional.
Estas tienden a ser grandes estructuras, por lo que por lo general es deseable colocarlas en la memoria del programa.
El siguiente código ilustra la idea.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/* PROGMEM - demostración con cadenas de caracteres Cómo guardar una tabla de cadenas en memoria de programa (flash), y recuperarlas para su uso. Información resumida de: http://www.nongnu.org/avr-libc/user-manual/pgmspace.html Configurar una tabla (matriz) de cadenas en la memoria del programa es un poco complicado, pero aquí hay un buen ejemplo para tomar como base. Configurar las cadenas es un proceso de dos pasos. Primero se definen las cadenas de caracteres. */ #include <avr/pgmspace.h> // cadenas a guardar – coloque los textos que necesite const char texto_0[] PROGMEM = "La vaca es un animal"; const char texto_1[] PROGMEM = "todo forrado de cuero,"; const char texto_2[] PROGMEM = "tiene las patas tan largas"; const char texto_3[] PROGMEM = "que le llegan hasta el suelo"; const char texto_4[] PROGMEM = "Cancionero"; const char texto_5[] PROGMEM = "popular."; // A continuación, configure una tabla para referirse a sus cadenas. const char* const tabla_textos[] PROGMEM = {texto_0, texto_1, texto_2, texto_3, texto_4, texto_5}; char buffer[30]; // que sea suficiente para la mayor cadena que debe contener void setup() { Serial.begin(9600); while(!Serial); // espere a que el puerto serie se conecte } void loop() { /* El uso de una tabla de cadenas en la memoria de programa requiere el uso de funciones especiales para recuperar los datos. La función strcpy_P copia una cadena del espacio de programa a una cadena en la RAM ("buffer"). Asegúrese de que su cadena de recepción en la RAM sea lo suficientemente grande como para contener lo que sea que está trayendo del espacio de programa. */ for (int i = 0; i < 6; i++) { strcpy_P(buffer, (char*)pgm_read_word(&(tabla_textos[i]))); // si este código de arriba le resulta complejo, simplemente copie Serial.println(buffer); delay( 500 ); } } |
Notas y advertencias
Tenga en cuenta que las variables deben estar definidas globalmente, o definidas con la palabra clave static, para poder trabajar con PROGMEM.
El siguiente código NO funcionará cuando esté dentro de una función:
1 |
const char long_str[] PROGMEM = "Hola, me gustaría contarles un poco sobre mí.\n"; |
El siguiente código funcionará, incluso si está definido localmente dentro de una función:
1 |
const static char long_str[] PROGMEM = "Hola, me gustaría contarles un poco sobre mí.\n" |
La macro F()
Cuando se utiliza una instrucción como:
1 |
Serial.print("Escribir algo en el Monitor Serie"); |
La cadena a imprimir normalmente se guarda en la memoria RAM. Si su programa imprime muchas cosas en el Monitor Serie, puede acabar llenando la RAM. Si tiene espacio libre en la memoria FLASH, puede indicar fácilmente que la cadena debe guardarse en FLASH, usando la sintaxis:
1 |
Serial.print(F("Escribir algo en el Monitor Serie")); |