Diferencia entre revisiones de «PROGMEM»

De Proyectos
Ir a la navegación Ir a la búsqueda
Sin resumen de edición
 
(No se muestran 24 ediciones intermedias del mismo usuario)
Línea 4: Línea 4:
*Las variables PROGMEM tienen espacio asignado en la memoria que no cambia durante la ejecución del programa.
*Las variables PROGMEM tienen espacio asignado en la memoria que no cambia durante la ejecución del programa.


= AVR =
= Declaración de variables =
== Declaración de variables ==
Para alojar una variable constante en la memoria Flash se hace asi:
Para alojar una variable constante en la memoria Flash se hace asi:
  PROGMEM const tipoVariable nombreVariable = valoresIniciales;
  PROGMEM const tipoVariable nombreVariable = valoresIniciales;
Línea 73: Línea 72:
|}
|}


== Funciones de lectura ==
== Comportamiento extraño(en Arduino IDE) ==
=== Primer caso ===
<pre>
const uint8_t a[] PROGMEM = {1,23,34,45};
 
void buffer(const uint8_t b[] PROGMEM){
  uint8_t g;
 
  for(int i=0; i<4;i++){
    g = pgm_read_byte(b++);
    Serial.print(g);Serial.print(" - ");
  }
}
 
void setup(){
  Serial.begin(9600);
 
  buffer(a);
}
void loop(){}
</pre>
*En este caso la salida por serial será: 1 - 23 - 34 - 45
 
 
<pre>
const prog_uint8_t a[] = {1,23,34,45};
 
void buffer(const uint8_t b[] PROGMEM){
  uint8_t g;
 
  for(int i=0; i<4;i++){
    g = pgm_read_byte(b++);
    Serial.print(g);Serial.print(" - ");
  }
}
 
void setup(){
  Serial.begin(9600);
 
  buffer(a);
}
void loop(){}
</pre>
*En este caso la salida por serial no será correcta
 
*No entiendo porque pasa si se supone que prog_uint8_t es equivalente a uint8_t PROGMEM
 
 
=== Segundo caso ===
 
<pre>
const uint8_t a[] PROGMEM = {1,23,42,3};
uint8_t b[4];
 
void setup(){
  Serial.begin(9600);
  for(int i=0; i<4;i++){
    b[i] = pgm_read_byte(a++);
    Serial.print(b[i]);
  }
}
void loop(){}
</pre>
 
*Aqui recibo un error: increment of read-only location '(const uint8_t*)(&a)'. Cuando en los ejemplos de arriba no pasa al poner una función de por medio.
 
 
= Funciones de lectura =
<pre>
<pre>
pgm_read_byte() //Lee un byte de la memoria Flash
pgm_read_byte() //Lee un byte de la memoria Flash
Línea 80: Línea 146:
pgm_read_float() //Lee un número de coma flotante de 4 bytes
pgm_read_float() //Lee un número de coma flotante de 4 bytes
</pre>
</pre>
*A cada una de estas funciones se le pasa una dirección de 16 bits que indican donde reside el elemento de la variable
*A cada una de estas funciones se le pasa una dirección de 16 bits que indican donde reside la variable.
=== Ejemplos ===
 
 
== Ejemplos ==
<pre>
<pre>
const prog_uint8_t a[]={1,2,3,32,55,12};
const prog_uint8_t a[]={1,2,3,32,55,12};


int b;
uint8_t b;
b = pgm_read_byte(&a[0]);
b = pgm_read_byte(&a[0]);
</pre>
</pre>


*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
*''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''


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


int b;
uint8_t b;
b = pgm_read_byte(&a++);
b = pgm_read_byte(a+1);
</pre>
</pre>


*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]
*''a+1'' es equivalente a ''a[1]'', como vimos en [[Punteros#Operaciones_con_Vectores|Operaciones con Vectores]]


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


int b[5];
uint8_t b[6];
 
for(int i=0;i<6;i++){
for(int i=0;i<6;i++){
   b[i] = pgm_read_byte(&a++);
   b[i] = pgm_read_byte(a+i);
}
}
</pre>
</pre>


*En el array b he copiado el array a entero
*En este ejemplo he copiado todo el contenido de la variable ''a'' en la variable ''b''
== Globales y Locales ==
 
= Funciones =
 
 
= Globales y Locales =
*Cuando declaramos una variable PROGMEM globalmente se hace como hemos dicho antes
*Cuando declaramos una variable PROGMEM globalmente se hace como hemos dicho antes
  const prog_uint8_t a = 1;
  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
*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;
  static const porg_uint8_t a =1;


= Arduino =  
= Punteros a variables en flash =
== Declaración de variables ==
== PGM_P ==
La declaración que explico arriba, que es para los avr, no funciona en el arduino ide. No he encontrado una explicación, de momento, pero pongo aqui como declarar variables PROGMEM que si que me ha funcionado en el arduino ide
*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 ===
<pre>
#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;


static const int a[] PROGMEM = {1,2,3,4};
    p = (PGM_P)b;                                                //Se esta haciendo un casting para que no haya problemas con el compilador
static const int b PROGMEM = 1;
    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]
static const char c PROGMEM = 'a';
                                                                //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
}
</pre>
*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


El método abreviado es el mismo para Arduino IDE


static const prog_uint16_t a[] = {1,2,3,4};
== PGM_P void ==
static const prog_uint16_t b = 1;
*Son punteros neutros
  static const pog_int8_t c = 'a';
*Los punteros PGM_P void se definen:
  #define PGM_VOID_P const void PROGMEM *


== Funciones de lectura ==
 
=== Ejemplo ===
<pre>
#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;                            //Realmente no haria falta convertir el array b a PGM_VOID_P, pero es mejor hacer las cosas bien hechas
    c = pgm_read_byte((PROGMEM char*)p + 1);
   
    p = (PGM_VOID_P)a;                            //Lo mismo aquí
    n = pgm_read_word((PROGMEM int*)p + 3);
}
</pre>
*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 para poder 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)
 
<pre>
PROGMEM const unsigned int a = 5;
 
int main(void){
  PGM_VOID_P p;
  unsigned int i;
 
  p = (PGM_VOID_P)&a;
  i = pgm_read_word((PROGMEM int *)p);
}
</pre>
*Si nos fijamos para asignar al puntero ''p'' la dirección de ''a'' lo hemos hecho de forma purista, convirtiendo antes la variable ''a'' al tipo que tiene ''p''. Esto realmente no es necesario en Arduino IDE, pero es mejor hacerlo bien para que quede todo claro
*Luego hacemos casting al puntero ''p'' porque la funcion pgm_read_word() necesita recibir una dirección de 2 bytes (tipo int) y ''p'' es de tipo PGM_VOID_P

Revisión actual - 18:20 21 mar 2013

¿Para que sirve?

[editar]
  • 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

[editar]

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

Comportamiento extraño(en Arduino IDE)

[editar]

Primer caso

[editar]
const uint8_t a[] PROGMEM = {1,23,34,45};

void buffer(const uint8_t b[] PROGMEM){
  uint8_t g;

  for(int i=0; i<4;i++){
    g = pgm_read_byte(b++);
    Serial.print(g);Serial.print(" - ");
  }
}

void setup(){
  Serial.begin(9600);

  buffer(a);
}
void loop(){}
  • En este caso la salida por serial será: 1 - 23 - 34 - 45


const prog_uint8_t a[] = {1,23,34,45};

void buffer(const uint8_t b[] PROGMEM){
  uint8_t g;

  for(int i=0; i<4;i++){
    g = pgm_read_byte(b++);
    Serial.print(g);Serial.print(" - ");
  }
}

void setup(){
  Serial.begin(9600);

  buffer(a);
}
void loop(){}
  • En este caso la salida por serial no será correcta
  • No entiendo porque pasa si se supone que prog_uint8_t es equivalente a uint8_t PROGMEM


Segundo caso

[editar]
const uint8_t a[] PROGMEM = {1,23,42,3};
uint8_t b[4];

void setup(){
  Serial.begin(9600);
  for(int i=0; i<4;i++){
    b[i] = pgm_read_byte(a++);
    Serial.print(b[i]);
  }
}
void loop(){}
  • Aqui recibo un error: increment of read-only location '(const uint8_t*)(&a)'. Cuando en los ejemplos de arriba no pasa al poner una función de por medio.


Funciones de lectura

[editar]
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

[editar]
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

[editar]

Globales y Locales

[editar]
  • 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

[editar]

PGM_P

[editar]
  • 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

[editar]
#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

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


Ejemplo

[editar]
#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;                             //Realmente no haria falta convertir el array b a PGM_VOID_P, pero es mejor hacer las cosas bien hechas
    c = pgm_read_byte((PROGMEM char*)p + 1);
    
    p = (PGM_VOID_P)a;                             //Lo mismo aquí
    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 para poder 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)
PROGMEM const unsigned int a = 5;

int main(void){
  PGM_VOID_P p;
  unsigned int i;

  p = (PGM_VOID_P)&a;
  i = pgm_read_word((PROGMEM int *)p);
}
  • Si nos fijamos para asignar al puntero p la dirección de a lo hemos hecho de forma purista, convirtiendo antes la variable a al tipo que tiene p. Esto realmente no es necesario en Arduino IDE, pero es mejor hacerlo bien para que quede todo claro
  • Luego hacemos casting al puntero p porque la funcion pgm_read_word() necesita recibir una dirección de 2 bytes (tipo int) y p es de tipo PGM_VOID_P