Diferencia entre revisiones de «PROGMEM»

De Proyectos
Ir a la navegación Ir a la búsqueda
Línea 153: Línea 153:
*Son punteros neutros
*Son punteros neutros
*Los punteros PGM_P void se definen:
*Los punteros PGM_P void se definen:
  const void PROGMEM *
  #define const void PROGMEM * PGM_VOID_P





Revisión del 14:38 21 mar 2013

¿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};

uint8_t 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};

uint8_t b;
b = pgm_read_byte(a+1);
const prog_uint8_t a[]={1,2,3,32,55,12};

uint8_t b[6];

for(int i=0;i<6;i++){
  b[i] = pgm_read_byte(a+i);
}
  • En este ejemplo he copiado todo el contenido de la variable a en la variable b

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;

Punteros a variables en flash

PGM_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 un casting 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;                                                //Aqui vuelvo ha hacer lo mismo en la primera linea que en el array b
    n = pgm_read_word((PROGMEM const unsigned int *)p + 3);      //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


PGM_P void

  • Son punteros neutros
  • Los punteros PGM_P void se definen:
#define const void PROGMEM * PGM_VOID_P


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_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 lo que hacemos es convertir la variable a y b a puntero neutro, asi podemos asignar a p la dirección de memoria de estas variables.
  • Luego par apoder acceder a la variable que apunta p tenemos que convertirla al tipo que la función necesita. En el primer caso a una variable de 8 bits(char) y en el segundo caso a una variable de 2 bytes(int)