{"id":1267,"date":"2020-01-30T16:44:59","date_gmt":"2020-01-30T16:44:59","guid":{"rendered":"http:\/\/robots-argentina.com.ar\/didactica\/?p=1267"},"modified":"2020-11-02T00:56:36","modified_gmt":"2020-11-02T00:56:36","slug":"arduino-alojar-datos-en-la-memoria-de-programa-con-progmem","status":"publish","type":"post","link":"https:\/\/robots-argentina.com.ar\/didactica\/arduino-alojar-datos-en-la-memoria-de-programa-con-progmem\/","title":{"rendered":"Arduino: alojar datos en la memoria de programa con PROGMEM"},"content":{"rendered":"

Almacene los datos en la memoria FLASH<\/strong> (memoria de programa) en lugar de RAM<\/strong> (a veces nombrada como SRAM<\/strong>, del ingl\u00e9s STATIC RAM = RAM est\u00e1tica).<\/p>\n

Los distintos tipos de memoria disponibles en una placa Arduino son:<\/p>\n

\u25a0 FLASH<\/strong>: Esta memoria almacena el c\u00f3digo del programa.<\/p>\n

\u25a0 RAM<\/strong>: Esta memoria almacena los datos del programa y es vol\u00e1til. Cuando se apaga Arduino se pierden los datos.<\/p>\n

\u25a0 EEPROM<\/strong>: Esta memoria almacena datos que son persistentes. Esta memoria puede ser utilizada para guardar configuraci\u00f3n o cualquier otro dato que podamos utilizar si se apaga y enciende Arduino.<\/p>\n

\"\"<\/a><\/p>\n

La palabra clave PROGMEM<\/strong> es un modificador de variable. Se debe usar solo con los tipos de datos definidos en la biblioteca pgmspace.h<\/a><\/strong>.<\/p>\n

PROGMEM<\/strong> le dice al compilador \u00abponga esta informaci\u00f3n en la memoria flash\u00bb, en lugar de en la RAM<\/strong>, donde ir\u00eda normalmente.<\/p>\n

PROGMEM<\/strong> es parte de la biblioteca pgmspace.h<\/strong>. \u00c9sta se incluye autom\u00e1ticamente en las versiones modernas del IDE<\/strong>. Sin embargo, si estuviese utilizando una versi\u00f3n IDE por debajo de 1.0 (2011), primero deber\u00e1 incluir la biblioteca en la parte superior de su programa, con esta declaraci\u00f3n:<\/p>\n

\r\n#include \r\n<\/pre>\n

Sintaxis<\/strong><\/p>\n

\r\nconst tipoDato nombreVariable[] PROGMEM = {dato0, dato1, dato2\u2026};\r\n<\/pre>\n

tipoDato<\/strong> \u2013 alg\u00fan tipo de variable
\nnombreVariable<\/strong> \u2013 el nombre de su matriz de datos<\/p>\n

Tenga en cuenta que debido a que la indicaci\u00f3n PROGMEM<\/strong> es un modificador de variable, no existe una regla estricta sobre d\u00f3nde debe ir, por lo que el compilador acepta diversas posiciones, que tambi\u00e9n pueden ser sin\u00f3nimos. Sin embargo, las pruebas han indicado que, en varias versiones de Arduino, PROGMEM<\/strong> puede funcionar en una ubicaci\u00f3n y no en otra.<\/p>\n

Los siguientes ejemplos se han probado y funcionan con los IDE<\/strong> actuales. Las versiones anteriores del IDE pueden funcionar mejor si la palabra PROGMEM<\/strong> se incluye despu\u00e9s del nombre de la variable.<\/p>\n

\r\nconst dataType variableName[] PROGMEM = {}; \/\/ use esta sintaxis\r\nconst PROGMEM dataType variableName[] = {}; \/\/ no esta\r\nconst dataType PROGMEM variableName[] = {}; \/\/ ni esta\r\n<\/pre>\n

Tenga en cuenta que dado que PROGMEM<\/strong> se podr\u00eda usar con una sola variable, pero en realidad solo vale la pena si tiene un bloque de datos m\u00e1s grande para almacenar, que por lo general es m\u00e1s f\u00e1cil de poner en una matriz (o en otra estructura de datos C++, algo que queda m\u00e1s all\u00e1 de la presente discusi\u00f3n).<\/p>\n

Usar PROGMEM<\/strong> es un procedimiento de dos pasos. Despu\u00e9s de tener los datos que est\u00e1n en la memoria Flash, se requieren m\u00e9todos especiales (funciones), tambi\u00e9n definidos en la biblioteca pgmspace.h<\/strong>, para volcar los datos de la memoria de programa a la RAM<\/strong>, de modo de que podamos trabajar con ellos.<\/p>\n

C\u00f3digo de ejemplo<\/strong><\/p>\n

Las siguientes secciones de c\u00f3digo ilustran c\u00f3mo leer y escribir unsigned char<\/strong> (bytes) e int<\/strong> (2 bytes) en PROGMEM<\/strong>.<\/p>\n

\r\n\/\/ guardar algunos enteros sin signo (unsigned int)\r\n#include <avr\/pgmspace.h>\r\n\r\nconst uint16_t grupoNumeros[] PROGMEM = { 65000, 32796, 16843, 10, 11234};\r\n\r\n\/\/ guardar algunos caracteres (char)\r\nconst char Mensaje[] PROGMEM  = {\"ESTOS CARACTERES SE UTILIZAR\u00c1N, M\u00c1S ADELANTE, EN ALGUN LUGAR\"};\r\n\r\nunsigned int intParaMostrar;\r\nint k; \/\/ variable para contar\r\nchar miCaracter;\r\n\r\nvoid setup() {\r\n  Serial.begin(9600);\r\n  while (!Serial);  \/\/ esperar que el puerto serie se conecte\r\n\r\n  \/\/ Su c\u00f3digo va aqu\u00ed, en inicializaci\u00f3n, para que corra una sola vez\r\n  \/\/ Lee enteros de 2 bytes y los muestra\r\n  for (k = 0; k < 5; k++)\r\n  {\r\n    intParaMostrar = pgm_read_word_near(grupoNumeros + k);\r\n    Serial.println(intParaMostrar);\r\n  }\r\n  Serial.println();\r\n\r\n  \/\/ Leer caracteres y mostrarlos\r\n  for (k = 0; k < strlen_P(Mensaje); k++)\r\n  {\r\n    miCaracter =  pgm_read_byte_near(Mensaje + k);\r\n    Serial.print(miCaracter);\r\n  }\r\n  Serial.println();\r\n}\r\n\r\nvoid loop() {\r\n  \/\/ ponga aqu\u00ed su c\u00f3digo, donde correr\u00e1 continuamente\r\n}\r\n<\/pre>\n

Matrices de cadenas de caracteres<\/strong><\/p>\n

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.<\/p>\n

Debido a que las cadenas en s\u00ed son matrices, en realidad este es un ejemplo de una matriz bidimensional.<\/p>\n

Estas tienden a ser grandes estructuras, por lo que por lo general es deseable colocarlas en la memoria del programa.<\/p>\n

El siguiente c\u00f3digo ilustra la idea.<\/p>\n

\/*\r\n PROGMEM - demostraci\u00f3n con cadenas de caracteres\r\n C\u00f3mo guardar una tabla de cadenas en memoria de programa (flash),\r\n y recuperarlas para su uso.\r\n\r\n Informaci\u00f3n resumida de:\r\n http:\/\/www.nongnu.org\/avr-libc\/user-manual\/pgmspace.html\r\n \r\n Configurar una tabla (matriz) de cadenas en la memoria \r\n del programa es un poco complicado, pero aqu\u00ed hay \r\n un buen ejemplo para tomar como base.\r\n\r\n Configurar las cadenas es un proceso de dos pasos. \r\n Primero se definen las cadenas de caracteres.\r\n*\/\r\n\r\n#include <avr\/pgmspace.h>\r\n\/\/ cadenas a guardar \u2013 coloque los textos que necesite\r\nconst char texto_0[] PROGMEM = \"La vaca es un animal\";\r\nconst char texto_1[] PROGMEM = \"todo forrado de cuero,\";\r\nconst char texto_2[] PROGMEM = \"tiene las patas tan largas\";\r\nconst char texto_3[] PROGMEM = \"que le llegan hasta el suelo\";\r\nconst char texto_4[] PROGMEM = \"Cancionero\";\r\nconst char texto_5[] PROGMEM = \"popular.\";\r\n\r\n\/\/ A continuaci\u00f3n, configure una tabla para referirse a sus cadenas.\r\n\r\nconst char* const tabla_textos[] PROGMEM = {texto_0, texto_1, texto_2, texto_3, texto_4, texto_5};\r\n\r\nchar buffer[30];  \/\/ que sea suficiente para la mayor cadena que debe contener\r\n\r\nvoid setup()\r\n{\r\n  Serial.begin(9600);\r\n  while(!Serial); \/\/ espere a que el puerto serie se conecte\r\n}\r\n\r\nvoid loop()\r\n{\r\n\r\n\/* El uso de una tabla de cadenas en la memoria de programa requiere el uso \r\nde funciones especiales para recuperar los datos. La funci\u00f3n strcpy_P copia\r\nuna cadena del espacio de programa a una cadena en la RAM (\"buffer\").\r\nAseg\u00farese de que su cadena de recepci\u00f3n en la RAM sea lo suficientemente\r\ngrande como para contener lo que sea que est\u00e1 trayendo del espacio de\r\nprograma. *\/\r\n\r\n  for (int i = 0; i < 6; i++)\r\n  {\r\n    strcpy_P(buffer, (char*)pgm_read_word(&(tabla_textos[i])));\r\n    \/\/ si este c\u00f3digo de arriba le resulta complejo, simplemente copie\r\n    Serial.println(buffer);\r\n    delay( 500 );\r\n  }\r\n}\r\n<\/pre>\n

Notas y advertencias<\/strong><\/p>\n

Tenga en cuenta que las variables deben estar definidas globalmente, o definidas con la palabra clave static, para poder trabajar con PROGMEM<\/strong>.<\/p>\n

El siguiente c\u00f3digo NO<\/strong> funcionar\u00e1 cuando est\u00e9 dentro de una funci\u00f3n:<\/p>\n

\r\nconst char long_str[] PROGMEM = \"Hola, me gustar\u00eda contarles un poco sobre m\u00ed.\\n\";\r\n<\/pre>\n

El siguiente c\u00f3digo funcionar\u00e1, incluso si est\u00e1 definido localmente dentro de una funci\u00f3n:<\/p>\n

\r\nconst static char long_str[] PROGMEM = \"Hola, me gustar\u00eda contarles un poco sobre m\u00ed.\\n\"\r\n<\/pre>\n

La macro F()<\/strong><\/p>\n

Cuando se utiliza una instrucci\u00f3n como:<\/p>\n

\r\nSerial.print(\"Escribir algo en el Monitor Serie\");\r\n<\/pre>\n

La cadena a imprimir normalmente se guarda en la memoria RAM<\/strong>. Si su programa imprime muchas cosas en el Monitor Serie, puede acabar llenando la RAM<\/strong>. Si tiene espacio libre en la memoria FLASH<\/strong>, puede indicar f\u00e1cilmente que la cadena debe guardarse en FLASH<\/strong>, usando la sintaxis:<\/p>\n

\r\nSerial.print(F(\"Escribir algo en el Monitor Serie\"));\r\n<\/pre>\n


\n