PROGMEM

De Proyectos
Ir a la navegación Ir a la búsqueda

¿Para que sirve?

  • PROGMEM hace que una variable constante a la que se le aplica resida en la memoria flash en vez de la RAM. Asi podemos ahorrar RAM para las demás variables.
  • Si tenemos muchos strings que no se tienen que modificar (por ejemplo strings de menus) vale la pena guardarlos en la flash para que no ocupen RAM
  • Las variables PROGMEM tienen espacio asignado en la memoria que no cambia durante la ejecución del programa.

Declaración de variables

Para alojar una variable constante en la memoria Flash se hace asi:

PROGMEM const tipoVariable nombreVariable = valoresIniciales;

Por ejemplo:

PROGMEM const int a = 5;
PROGMEM const int a[] = {0,3,4,2};

También puede hacerse asi:

const int a[] PROGMEM = {0,4,3,2};

O de un modo más abreviado:

const prog_uint16_t a[] = {0,4,3,2);//Esto equivale al ejemplo de encima
-----
  '----> se mantiene const

Esta tabla muestra las equivalencias:

avr/pgmspace.h de AVR-GCC
escritura normal escritura abrebiada stdint.h
char PROGMEM prog_char
unsigned char PROGMEM prog_uchar
int8_t PROGMEM prog_int8_t signed char
uint8_t PROGMEM prog_uint8_t unsigned char
int16_t PROGMEM prog_int16_t signed int
uint16_t PROGMEM prog_uint16_t unsigned int
int32_t PROGMEM prog_int32_t signed long int
uint32_t PROGMEM prog_uint32_t unsigned long int
int64_t PROGMEM prog_int64_t signed long long int
uint64_t PROGMEM prog_uint64_t unsigned long long int

Funciones de lectura

pgm_read_byte() //Lee un byte de la memoria Flash
pgm_read_word() //Lee 2 bytes de la memoria Flash
pgm_read_dword() //Lee 4 bytes de la memoria Flash
pgm_read_float() //Lee un número de coma flotante de 4 bytes
  • A cada una de estas funciones se le pasa una dirección de 16 bits que indican donde reside la variable.

Ejemplos

const prog_uint8_t a[]={1,2,3,32,55,12};

int b;
b = pgm_read_byte(&a[0]);
  • b es igual a 1. Lo que le paso a pgm_read_byte es la dirección de la primera posición del array a
const prog_uint8_t a[]={1,2,3,32,55,12};

int b;
b = pgm_read_byte(&a++);
  • b es igual a 2. Como a realmente es un puntero, apunta al primer elemento del array. Cuando hago a++ paso al siguiente elemento, que es a[1]
const prog_uint8_t a[]={1,2,3,32,55,12};

int b[5];
for(int i=0;i<6;i++){
  b[i] = pgm_read_byte(&a++);
}
  • En el array b he copiado el array a entero

Funciones

Globales y Locales

  • Cuando declaramos una variable PROGMEM globalmente se hace como hemos dicho antes
const prog_uint8_t a = 1;
  • Cuando es una variable local de una función en los compiladores AVR IAR C y AVR GCC se tiene que poner static.La función setup y loop de Arduino son funciones, por tanto si declaramos variables PROGMEM tenemos que hacerlo como sigue
static const porg_uint8_t a =1;

PGM_P y PGM_VOID_P

  • PGM_P define un tipo puntero que apunta a variables de la memoria flash de 8bits
  • Su definición en pgmspace.h de avr-gcc es
#define PGM_P const PROGMEM char *
  • Ejemplo
#include "avr_compiler.h"

PROGMEM const unsigned int a[] = {1,2,3,4,5};
PROGMEM const unsigned char b[]  = {23,42,54,2};
    
int main(void)
{
    PGM_P p;
    unsigned char b1;
    unsigned int a1;

    p = (PGM_P)b;  //Se esta haciendo una conversión de tipo para que no haya problemas con el compilador
    c = pgm_read_byte(p + 1);
    //En el ejemplo de arriba p apunta al primer elemento de b. Con pgm_read_byte(p+1) leemos b[1]
    //Esto es lo mismo que hago en la sección Ejemplos de Funciones de Lectura, solo que en este caso no le paso &p+1, porque p ya contiene dircciones

    
    p = (PGM_P)a;
    n = pgm_read_word((PROGMEM const unsigned int *)p + 3);
    //Aqui vuelvo ha hacer lo mismo en la primera linea que en el array b
    //Como pgm_read_word() necesita que le pasen una dirección de variable de 2bytes (word) se hace la conversión de p.
    //También se puede poner (PROGMEM int *) porque lo que cuenta es que se convierta a un tipo de 2 bytes
}
  • En el ejemplo anterior estamos utilizando el puntero PGM_P para acceder a varaibles de 2 bytes. Para ello estamos utilizando conversiones y realmente lo mejor seria utilizar punteros neutros, PGM_P void
  • Los punteros PGM_P void se definen:
    • const void PROGMEM *
#include "avr_compiler.h"

PROGMEM const unsigned int a[] = {1,2,3,4,5};
PROGMEM const unsigned char b[]  = {23,42,54,2};
    
int main(void)
{
    PGM_VOID_P p;
    unsigned char c;
    unsigned int n;

    p = (PGM_VOID_P)b;
    c = pgm_read_byte((PROGMEM char*)p + 1);
    
    p = (PGM_VOID_P)a;
    n = pgm_read_word((PROGMEM int*)p + 3);
}
  • Aqui convertimos el puntero neutro PGM_P al tipo correspondiente según la variable a tratar. Para el array a de enteros se convierte a PROGMEM int * y para el array b de caracteres se convierte a PROGMEM char *