Diferencia entre revisiones de «PROGMEM»
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: | ||
#define const void PROGMEM * | #define PGM_VOID_P const void PROGMEM * | ||
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:
| 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);
- a+1 es equivalente a a[1], como vimos en Operaciones con Vectores
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 PGM_VOID_P const void PROGMEM *
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)