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 8 ediciones intermedias del mismo usuario)
Línea 71: Línea 71:
|unsigned long long int
|unsigned long long int
|}
|}
== 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 =
= Funciones de lectura =
Línea 80: Línea 147:
</pre>
</pre>
*A cada una de estas funciones se le pasa una dirección de 16 bits que indican donde reside 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>
const prog_uint8_t a[]={1,2,3,32,55,12};
uint8_t b;
b = pgm_read_byte(a+1);
</pre>
*''a+1'' es equivalente a ''a[1]'', como vimos en [[Punteros#Operaciones_con_Vectores|Operaciones con Vectores]]
<pre>
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);
}
</pre>
*En este ejemplo he copiado todo el contenido de la variable ''a'' en la variable ''b''


= Funciones =
= Funciones =
Línea 132: Línea 222:
*Son punteros neutros
*Son punteros neutros
*Los punteros PGM_P void se definen:
*Los punteros PGM_P void se definen:
  const void PROGMEM *
  #define PGM_VOID_P const void PROGMEM *




Línea 148: Línea 238:
     unsigned int n;
     unsigned int n;


     p = (PGM_VOID_P)b;
     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);
     c = pgm_read_byte((PROGMEM char*)p + 1);
      
      
     p = (PGM_VOID_P)a;
     p = (PGM_VOID_P)a;                             //Lo mismo aquí
     n = pgm_read_word((PROGMEM int*)p + 3);
     n = pgm_read_word((PROGMEM int*)p + 3);
}
}
</pre>
</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.
*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)
*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