32- Trabajando con memoria din谩mica Arduino


Asignaci贸n de memoria en Arduino.

En esta lecci贸n, aprenderemos a trabajar con la memoria din谩mica de Arduino.聽En primer lugar, debe familiarizarse con la asignaci贸n de memoria y comprender c贸mo funciona y qu茅 haremos generalmente.聽Aqu铆 hay un diagrama de la asignaci贸n de memoria en el microcontrolador AVR, que est谩 en el Arduino:

Asignaci贸n de memoria en arduino
Asignaci贸n de memoria en arduino

La memoria es esencialmente una gran matriz, cada celda de la cual tiene su propia direcci贸n, la direcci贸n crece de izquierda a derecha (como se muestra en la imagen de arriba).聽La primera es Flash, tambi茅n es la memoria de programa, la memoria en la que se almacena el c贸digo en s铆.聽Este c贸digo no cambia mientras el programa se est谩 ejecutando (hay formas de hacerlo, pero este no es el tema de la lecci贸n).聽Hoy nos interesa la memoria din谩mica, SRAM en arduino, que en el diagrama est谩 representada por una combinaci贸n de 谩reas azules, verdes, rosas y naranjas, as铆 como una zona blanca con flechas entre rosa y naranja.聽Miremos m谩s de cerca:

  • Globales (azul y verde): esta 谩rea es donde viven las variables globales y est谩ticas. El tama帽o de esta 谩rea se conoce en el momento en que se inicia el programa y no cambia durante su ejecuci贸n, ya que se declaran variables globales y est谩ticas, se conoce su tama帽o y n煤mero.
  • Heap (rosa), desde esta 谩rea podemos asignar memoria para nuestras necesidades. El tama帽o de esta 谩rea puede cambiar durante la ejecuci贸n del programa, el mont贸n 芦crece禄 en la direcci贸n creciente, de izquierda a derecha, lo que se muestra con la flecha en el diagrama. En esta 谩rea, podemos asignar memoria de forma independiente para nuestras necesidades y liberarla, nuevamente por nuestra cuenta. Importante: el procesador no le permitir谩 asignar un 谩rea si no hay suficiente memoria libre para ello, es decir, es muy poco probable que la zona Heap y la Pila se solapen.
  • Stack, tambi茅n conocido como Pila: en esta 谩rea viven variables y par谩metros locales que se pasan a funciones (variables formales). El tama帽o de esta 谩rea cambia durante la ejecuci贸n del programa, la pila crece desde el final del 谩rea de memoria hacia direcciones decrecientes, hacia el Heap (ver la flecha en el diagrama). Las variables que viven aqu铆 se llaman autom谩ticas: el programa en s铆 asigna la memoria (al crear una variable local al ingresar a la funci贸n), y libera esta memoria despu茅s (la variable local se borra cuando sale de la funci贸n). Importante: el procesador no controla el tama帽o de la pila, es decir, durante la ejecuci贸n, la pila puede chocar con el heap y sobrescribir los datos que se encuentran all铆. Si es que pones datos all铆.
  • Disponible: memoria libre disponible. Tan pronto como esta se termine, el heap y la pila chocan: lo m谩s probable es que el programa se cuelgue. Si la pila controla su tama帽o de forma independiente, entonces debe tener cuidado con la memoria din谩mica: no olvide liberarla si ya no la necesita.

Asignaci贸n de memoria.

Tenemos dos funciones para asignar y liberar la memoria del Heap: malloc() y free() respectivamente. Tambi茅n hay operadores new y delete que hacen lo mismo. Al asignar memoria, obtenemos la direcci贸n del primer byte del 谩rea asignada, por lo que recomiendo leer la lecci贸n sobre direcciones y punteros .

  • malloc ( cantidad )– asigna el n煤mero de bytes de memoria din谩mica (del heap) y devuelve la direcci贸n al primer byte del 谩rea asignada. Si no hay suficiente memoria libre para la asignaci贸n, devuelve un 芦puntero nulo禄 –NULL.
  • free( ptr ) – libera la memoria apuntada por ptr, de vuelta al heap. Obtenemos la direcci贸n como resultado de malloc () durante la asignaci贸n. Solo se puede liberar la memoria asignada mediante funciones malloc (), realloc () o calloc (). El 谩rea asignada almacena el tama帽o de esta 谩rea (+2 bytes), y cuando se libera, la funci贸n free () sabe qu茅 tama帽o liberar.
  • new y delete – t茅cnicamente igual, diferencia en la aplicaci贸n (ver ejemplo a continuaci贸n)

Un punto importante: necesita liberar memoria en el orden inverso de su asignaci贸n para evitar la fragmentaci贸n de la memoria, es decir, cuando hay espacios vac铆os entre 谩reas ocupadas.

Veamos un ejemplo de asignaci贸n y liberaci贸n de memoria usando malloc / free y new / delete. Los ejemplos son absolutamente iguales desde el punto de vista de lo que est谩 sucediendo, difieren solo en funciones:

mallloc / free

void setup() {
 // asigna memoria para variables de 10 bytes (10 bytes)
  byte *by = malloc(10);
  // asigna memoria para 20 variables de tipo int (40 bytes)
  int *in = malloc(20 * sizeof(int));
  // asigna memoria para 1 variable de tipo uint32_t (4 bytes)
  uint32_t *ui = malloc(4);
  //  trabaja con la matriz
  by[0] = 50;
  by[1] = 60;
  by[2] = 90;
  uart.println(by[0]);
  uart.println(by[1]);
  uart.println(by[2]);
  // con una variable regular
  *ui = 123456;
  free(ui); // liberamos
  free(in); // liberamos
  free(by); // liberamos
  // aqu铆 * 隆ui es cero! Lo "borramos"
}
void loop() {}

new / delete

void setup() {
  // asigna memoria para variables de 10 bytes (10 bytes)
  byte *by = new byte [10];
  // asigna memoria para 20 variables de tipo int (40 bytes)
  int *in = new int [20];
  // asigna memoria para 1 variable de tipo uint32_t (4 bytes)
  uint32_t *ui = new uint32_t;
  // trabajar con la matriz
  by[0] = 50;
  by[1] = 60;
  by[2] = 90;
  // con una variable regular
  *ui = 123456;
  delete ui;    // liberamos
  delete [] in; //liberamos (indica que se trata de una matriz)
  delete [] by; // liberamos (indica que se trata de una matriz)
  // 隆aqu铆 * ui y otros son iguales a cero! Los "borramos"
}
void loop() {}

Por lo tanto, hemos asignado memoria, podemos interactuar con esta memoria (como con una variable ordinaria de arduino) y luego liberarla.聽Perm铆tanme recordarles que es muy deseable liberar en orden inverso, para que la memoria se libere secuencialmente sin dejar huecos.

Hay dos funciones m谩s: calloc () y realloc ():

  • calloc ( cantidad, tama帽o )– asigna memoria para el n煤mero de elementos con el tama帽o de cada uno (en bytes). El mismo que malloc(), pero es un poco m谩s conveniente de usar: en el ejemplo anterior, multiplicamos para obtener la cantidad requerida de bytes para el almacenamiento int malloc ( 20 * sizeof( int )) , pero podr铆as llamar calloc ( 20, sizeof( int )) ; – reemplazando el signo de multiplicaci贸n con una coma.
  • realloc ( ptr, tama帽o )– cambia la cantidad de memoria asignada apuntada por ptr por un nuevo valor especificado por el par谩metro de tama帽o. El tama帽o se especifica en bytes y puede ser mayor o menor que el original. Se devuelve un puntero a un bloque de memoria porque puede ser necesario mover el bloque a medida que aumenta su tama帽o. En este caso, el contenido del bloque antiguo se copia en el bloque nuevo y no se pierde informaci贸n.

Gesti贸n de memoria por lotes.

Entonces, aprendimos c贸mo asignar y liberar memoria, ahora consideraremos varias herramientas para trabajar adecuadamente con memoria din谩mica arduino.

  • memset ( ptr, value, num)– llena el n煤mero de piezas (num) de memoria apuntada por ptr con bytes de valor (value). A menudo se utiliza para establecer los valores iniciales de un 谩rea de memoria asignada. 隆Atenci贸n! solo con bytes, 0-255.
  • memcpy ( ptr1, ptr2, num)– sobrescribe los bytes del 谩rea ptr2 a ptr1 en la cantidad de (num). En t茅rminos generales, reescribe una matriz en otra. 隆Atenci贸n! 隆Funciona con bytes!
// === memset ===
// asigna 50 bytes
byte * buf = malloc ( 50 ) ;
// los llena con un valor de 10
memset ( buf, 10, 50 ) ;
// === memcpy ===
// una matriz
byte data1 [] = { 1, 2, 3, 4, 5 } ;
// y otra matriz
// byte data2 [5]; // se puede asignar desde la "pila"
byte * datos2 = malloc ( 5 ) ; // puede ser del "heap"
// reescribir data1 en data2
memcpy ( datos2, datos1, 5 ) ;
// data2 ahora es 1 2 3 4 5

Hemos aprendido a gestionar la memoria din谩mica del microcontrolador de arduino.聽隆Felicidades!聽Pero, 驴qu茅 tan 煤til es esta herramienta?聽El tema es controvertido.聽Me encontr茅 trabajando con memoria din谩mica solo en bibliotecas de pantallas y tiras de LED direccionables, es decir,聽all铆 se cre贸 un b煤fer en el heap, en el que se almacenaron los bytes de datos.聽Tambi茅n es posible hacer solo una matriz en la pila o en el alcance de las variables globales.

Tambi茅n puede ocurrir: el IDE de Arduino puede estimar aproximadamente el tama帽o de la memoria SRAM ocupada en la etapa de compilaci贸n, las variables globales, pero las matrices creadas durante la ejecuci贸n es dif铆cil predecir 芦como se almacenar谩n all铆 鈥. Debe trabajar con cuidado con la memoria din谩mica, recuerde limpiarla y recordar la fragmentaci贸n. La memoria din谩mica es conveniente cuando necesita poner cierta cantidad de datos en un b煤fer que tiene un alcance grande, a diferencia de una variable local. Puede interactuar con este b煤fer desde diferentes partes del programa (pasando su direcci贸n) y luego liberar memoria. Por lo tanto, es muy probable que trabajar con memoria din谩mica no le resulte 煤til, pero es bueno recordar esta herramienta.


Deja un comentario