Manipulacion EEPROM Atmega 32

buen dia para todos
me gustaria saber si alguien por aqui tiene idea de las rutinas y/o instrucciones para manipular la memoria eeprom de un atmega 32, necesito almacenar como minimo un registro de 4 eventos por proceso, entre cada evento transcurre al menos media hora, entonces debo almacenar la información de cada proceso. y lo que se me viene a la mente es la memoria eeprom, por favor cualquier orientacion sera bien recibida, tambien he trabajado con el paquete avrlib pero no doy con lo que necesito...

muchas gracias
 
con AVRLIB tienes que usar la libreria avr/eeprom.h, declarar variables de tipo EEPROM y los comandos eeprom_read_byte, eeprom_read_word, eeprom_write_byte y eeprom_write_byte

Código:
#include <avr/eeprom.h>
....
....
uint8_t  EEMEM EEPROM_REG=1;

int main (void){
....
VARIABLE=eeprom_read_byte(&EEPROM_REG);
....
....
}

puedes encontrar mas información aqui:

http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html
 
Saludos al foro, hace tiempo que no me aparecía con una duda pero hoy me encontré con un problemilla:

Verán, ando haciendo mis "pininos" en lenguaje C para los AVR en AtmelStudio y para ello me dispuse traducir algunas aplicaciones que tengo en ASM a C, todo va bien salvo el tema de guardar datos en la EEPROM interna del micro.

El problema que tengo es con la iniciación de algunos arreglos en la memoria, tengo esto:

Código:
char EEMEM EEP_CARRO_ACT[4] = {"AAAA"};            //Activación de carro
char EEMEM EEP_CARRO_DES[4] = {"BBBB"};            //Desactivación de carro
char EEMEM EEP_PLATINA_ACT[4] = {"CCCC"};        //Activación de platina
char EEMEM EEP_PLATINA_DES[4] = {"DDDD"};        //Desactivación de platina
char EEMEM EEP_CUCHILLA_ACT[4] = {"EEEE"};        //Activación de cuchilla
char EEMEM EEP_CUCHILLA_DES[4] = {"FFFF"};        //Desactivación de cuchilla
char EEMEM EEP_SOPLO_ACT[4] = {"GGGG"};            //Activación de soplo
char EEMEM EEP_SOPLO_DES[4] = {"HHHH"};            //Desactivación de soplo
char EEMEM EEP_INYECTOR_ACT[4] = {"IIII"};        //Activación de inyector
char EEMEM EEP_INYECTOR_DES[4] = {"jjjj"};        //Desactivación de inyector
char EEMEM EEP_AUXILIAR_ACT[4] = {"KKKK"};        //Activación de auxiliar
char EEMEM EEP_AUXILIAR_DES[4] = {"LLLL"};        //Desactivación de auxiliar
char EEMEM EEP_TIEMPO_DUMMY[4] = {"MMMM"};        //Inicio del temporizador, (no modificable)
char EEMEM EEP_TIEMPO_TOTAL[4] = {"NNNN"};        //Tiempo total del proceso

y para acceder a ellos utilizo la función eeprom_read_block() provista en la librería <avr/eeprom.h> pero sucede que los datos salen al revés, me explico:

La sección del programa que hace uso de la función de lectura pasa como parámetro un apuntador que va direccionando desde 0 hasta el último arreglo de 4 bytes "TIEMPO_TOTAL" pero al momento de ejecutar el programa ese último arreglo lo toma como "CARRO_ACT". Haciendo algunas pruebas observo que el compilador pone en orden inverso a la declaración los datos en la EEPROM ¿cómo puedo modificar esto?. La solución trivial es declarar los arreglos en orden inverso y listo, pero quisiero saber si hay otra alternativa.

Observé este comportamiento en el archivo .EPP que se genera al compilar:

Código:
:100000004E4E4E4E4D4D4D4D4C4C4C4C4B4B4B4B28
:100010006A6A6A6A494949494848484847474747D8
:100020004646464645454545444444444343434388
:080030004242424241414141BC

Ahí se aprecia el sentido inverso de los ASCII's que definí en los arreglos.:confused:
 
Última edición por un moderador:
...
utilizo la función eeprom_read_block() provista en la librería <avr/eeprom.h> pero sucede que los datos salen al revés, me explico:

La sección del programa que hace uso de la función de lectura pasa como parámetro un apuntador que va direccionando desde 0 hasta el último arreglo de 4 bytes "TIEMPO_TOTAL" pero al momento de ejecutar el programa ese último arreglo lo toma como "CARRO_ACT". Haciendo algunas pruebas observo que el compilador pone en orden inverso a la declaración los datos en la EEPROM ¿cómo puedo modificar esto?. La solución trivial es declarar los arreglos en orden inverso y listo, pero quisiero saber si hay otra alternativa.

...Ahí se aprecia el sentido inverso de los ASCII's que definí en los arreglos.:confused:

Hola.
Al declarar variables en C no hay ninguna garantía de que el compilador va a respetar el orden en que se declaran las variables para guardarlas en memoria. Si queremos que queden en una dirección en particular hay que indicárselo de alguna memoria (con algún atributo de variable, no sé cual porque no he utilizado micros atmel demasiado).
Referencias sobre eeprom en micros Atmel (más para mí que otra cosa :D):
http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__eeprom.html
http://teslabs.com/openplayer/docs/docs/prognotes/EEPROM%20Tutorial.pdf
http://www.embedds.com/accessing-avr-eeprom-memory-in-avrgcc/

¿Cómo es el ćodigo que lee la memoria eeprom?.
¿Estas copiando primero a ram usando eeprom_read_block?. Sino, ¿estás chequeando los valores con un puntero?, no se como declararlo, quizás como:
const char *punteroRom; //esto no marca diferencia entre EEPROM y Flash
o
typedef char EEMEM eepromChar;
eepromChar *punteroEepromChar;
y luego haciendo algo como
if ( punteroEepromChar[0] == algo ) {
HacerAlgo();
} else {
HacerOtroAlgo();
}
 
Última edición:
Hola Ardogan, eso es precisamente lo que busco, algún atributo de declaración que force una asignación de direcciones de los arreglos.

El código donde definí los datos es el que pegué anteriormente, ahora muestro parte del código donde se accede a esos arreglos:

Código:
EEPUNT = ((OFFSET+LOOPY)*8);		//Calcula dirección de donde se tomará la temporización de activación
				OLED_Com(FLECHDIR[LOOPY+1]-11);		//Situa cursor en pantalla para mostrar datos de temporización guardados
				eeprom_read_block((void*)&TEMP_BUFF, (const void*)EEPUNT, 4);	//Lee datos de activación
				OLED_Car(TEMP_BUFF[3]);
				OLED_Car(TEMP_BUFF[2]);
				OLED_Car(TEMP_BUFF[1]);
				OLED_Car('.');
				OLED_Car(TEMP_BUFF[0]);				//Muestra temporizador de activación

El puntero al que me refiero en el post anterior es EEPUNT. Básicamente lo que hago es calcular en que posición de un módulo OLED se mostrará la información que tiene el arreglo en EEPROM, para ello situo el cursor en pantalla, después llamo a la función

eeprom_read_block((void*)&TEMP_BUFF, (const void*)EEPUNT, 4)

TEMP_BUFF está previamente declarada como una arreglo en RAM igual de 4 bytes como pide la función.

En si no está el problema en la lógica del algoritmo si no en el orden en que el compilador pone los arreglos. :unsure:
 

Ok, se puede aprovechar que los string son todos del mismo tamaño y probar con algo como:

Código:
typedef enum {
    EEP_CARRO_ACT = 0, EEP_CARRO_DES,    //etc
}eepromStringIndex_t;
char EEMEM eepStrings[][4] = {"AAAA", "BBBB", "CCCC" /*etc*/};        //Inicio del temporizador, (no modificable)

//Luego en tu codigo se puede leer con eepStrings[EEP_CARRO_ACT] 
//o con eepStrings[variable] con variable de tipo eepromStringIndex_t, 
//o de tipo unsigned int también sirve si no te gustan los enums
Pero ahora que lo escribo, ahí hay un error nuevo. Estamos usando como inicializadores cadenas con "" que son terminadas en nulo.
Es decir "AAAA" = {'A','A','A','A', 0}

Entonces si querés dejar los inicializadores como están va
char EEMEM eepStrings[][5] = {"AAAA", "BBBB", "CCCC" /*etc*/};

Si querés que los arrays no incluyan el último elemento se puede usar

char EEMEM eepStrings[][4] = {{'A','A','A','A'}, {'B','B','B','B'}};

Por último si uno quiera usar strings de distinto tamaño, el caso general sería almacenar los strings y además los punteros a los strings. Algo como:

char EEMEM string1[] = "string1";
char EEMEM string2[] = "string2maslargo";
char EEMEM string3[] = "string3m";
char EEMEM string4[] = "string4largotonote";

char *stringList[] = {string1, string2, string3, string4};

entonces si hago stringList[0] tengo el puntero a "string1" y sucesivamente.
Notar que la lista de punteros a string stringList está almacenada en ram.
También se podría almacenar en memoria flash (supongo) con:

char * const stringList[] = {string1, string2, string3, string4};

Y tal vez en memoria eeprom (supongo nuevamente):
char * EEMEM stringList[] = {string1, string2, string3, string4};

/////////////////////////////////////////
Nota sobre gustos: no utilizaría la memoria eeprom para guardar cadenas de texto que no van a cambiar. Para eso utilizaría la memoria flash del micro usando
const char * const cadenas[] = {"cadena1", "cadena2", "etc"};
Ahí tengo las cadenas almacenadas en flash, y los punteros a las cadenas también. Y mi programa lo puede utilizar sin llamar a funciones de lectura de eeprom, es decir, con cadena[0] me devuelva "cadena1" y así...

La memoria eeprom la utilizaría para constantes de calibración de algún tipo, algo que normalmente es constante pero que puede cambiar frente a una calibración.
Ejemplo: calibración del oscilador del micro, de la referencia de tensión interna del micro, conjunto de puntos para caracterizar una tensión medida con el conversor AD generada por un sensor (ejemplo: temperatura vs tensión de un termistor simple), baud rate predeterminado para una comunicación uart (junto con paridad, bits de stop...), etc.

Sí almacenaría cadenas en eeprom si van a ser modificadas de alguna manera por el usuario del sistema en su funcionamiento normalmente (ejemplo típico: cartelito de leds donde de acuerdo al mensaje que quiera mostrar el usuario se guarda la cadena misma + algo de información que indique como se muestra el mensaje).
 
Última edición:
Hola hola

mm un arreglo de arreglos, no había pensado en eso, pero lo intenté y dio el resultado esperado
IMG_1778.JPG

creo que me quedaré mientras con esta solución que me parece más sencilla que declarar otro arreglo con las direcciones de los arreglos como te creo entender.

Lo implemente así sin cambiar nada más

Código:
char EEMEM TEMPOS[][4] = 
{
	"AAAA","BBBB","CCCC","DDDD","EEEE",
	"FFFF","GGGG","HHHH","IIII","JJJJ",
	"KKKK","LLLL","MMMM","NNNN"   	
};

Ahh sobre el tipo de datos, en la aplicación real son valores numéricos que el usuario puede configurar a su elección; estos por predeterminado toman el valor de 0's pero para poder ver que se muestren en el sentido correcto les pongo momentáneamente letras ;).

Gracias por el apoyo Ardogan, en verdad que esto de pasarme a C me está gustando pero si me está costando algo de trabajo.
 
Hola hola

mm un arreglo de arreglos, no había pensado en eso, pero lo intenté y dio el resultado esperado
Bien!! :)

creo que me quedaré mientras con esta solución que me parece más sencilla que declarar otro arreglo con las direcciones de los arreglos como te creo entender.
Claro, el arreglo de direcciones sirve para cuando las cadenas de caracteres son de distinta longitud. Si las cadenas tienen la misma longitud y se conoce de antemano, coincido totalmente, más sencillo = mejor.

Lo implemente así sin cambiar nada más

Código:
char EEMEM TEMPOS[][4] = 
{
    "AAAA","BBBB","CCCC","DDDD","EEEE",
    "FFFF","GGGG","HHHH","IIII","JJJJ",
    "KKKK","LLLL","MMMM","NNNN"       
};
Mmmmm, ¿tu compilador no se queja de algo con esa definición?.
Es por eso de que "AAAA" es en realidad {'A','A','A','A','\0'}, es decir, los caracteres escrito más la terminación en nulo, que son 5 bytes en vez de 4.
Si no importa ahorrar ese byte la declaración debería ser
char EEMEM TEMPOS[][5] = ...

Ahh sobre el tipo de datos, en la aplicación real son valores numéricos que el usuario puede configurar a su elección; estos por predeterminado toman el valor de 0's pero para poder ver que se muestren en el sentido correcto les pongo momentáneamente letras ;).
Ah bien, si es modificable por el usuario en uso normal entonces la EEPROM es para eso.

Gracias por el apoyo Ardogan, en verdad que esto de pasarme a C me está gustando pero si me está costando algo de trabajo.

De nada compadre, no te creas que yo tampoco acerté con la definición del arreglo de arreglo en memoria eeprom a la primera, tuve que intentar un par de veces primero :rolleyes:.
Manejar C es como sacar una licencia de conducir todo terreno. Con assembler manejas un solo tipo de vehículo de un solo fabricante (si bien son parecidos entre ellos). Con basic/pascal u otro lenguaje estructurado no C vas a manejar un rango limitado de vehículos (solo motos, o solo camiones). Con C vas a manejar camiones, coches, motos, cuatriciclo.... No todo (no barcos y aviones por ejemplo), pero la mayoría (micros de 8,16,32 bits, soft para PC, kernel de Linux, Android, ARM, programación de videojuegos...)

//////////////////
Y digo más, si hay que elegir una familia de compiladores la mayoría de los fabricantes usa un derivado/adaptado de gcc. Conocer algo de sus detalles permite reconocer errores que serían imposibles de otra forma.

Y más todavía, como entorno de desarrollo/IDE dale una oportunidad del Eclipse CDT (del que derivan los entornos gráficos de muchos fabricantes como Microchip, Texas Instruments, y no sé si Freescale). A veces se pone pesado (java VM) y están los que directamente prescinden de un entorno gráfico (las batallas vi vs emacs llevan ríos de sangre internetiana, guerra santa).
 
Última edición:
Mmmmm, ¿tu compilador no se queja de algo con esa definición?.
Es por eso de que "AAAA" es en realidad {'A','A','A','A','\0'}, es decir, los caracteres escrito más la terminación en nulo, que son 5 bytes en vez de 4.
Si no importa ahorrar ese byte la declaración debería ser
char EEMEM TEMPOS[][5] = ...

No para nada, pensé que igual habría problemas en ello pero no. El compilador no introduce el ASCII de nulo. Al momento de ver los resultado de la compilación dice que se han usado 56 bytes de EEPROM, osease los [4]*14 arreglos que puse en EEPROM. :)

jajaja "sangre internetiana", naa me incliné a usar GCC para iniciarme en el alto nivel pues hace poco me hice del JTAG-ICE para la familia ARM SAM de Atmel, quise meterles mano así sin más, ERROR!! jaja, no entendí ni pio de los ejemplos que propone ASF (Atmel Software Framework), entonces me fui un paso atrás a intentar con los AVR's que, si bien ya tengo idea de su arquitectura por programarlos en ASM, opté por dar el siguiente paso.

Algo así pues :LOL:

CDR70_kW4AAgTPQ.jpg
 
Yo en su momento para los AVR de 8 bits usé las funciones "eeprom_write_byte" y "eeprom_read_byte". En base a esas funciones básicas hice el resto.

Los prototipos de esas funciones son:

PHP:
eeprom_write_byte ((uint8_t *)(dirección), (uint8_t)(dato));

unsigned char eeprom_read_byte ((const uint8_t *)(dirección));

Por ej. si quisiera grabar el caracter 'c' en la dirección 10 (o 0x0A) de la memoria EEPROM, debería hacer esto:

PHP:
eeprom_write_byte ((uint8_t *)(10), (uint8_t)('c'));

Ahora, si quisieramos pasar todo un string, se podría hacer una función:

PHP:
int8_t CopiarStringAEEPROM(uint8_t *string, uint16_t dir_inicial, uint16_t tamanio)
{
	uint16_t cont;
	
	if((dir_inicial+tamanio)>TAMANIO_MAX_EEPROM)
		return -1;
		
	for(cont=0; cont<tamanio; cont++)
		eeprom_write_byte ((uint8_t *)(dir_inicial+cont),(uint8_t)(*(string+cont)));
		
	return 1;
}

Y su contrapartida de lectura podría ser:

PHP:
int8_t LeerStringEEPROM(uint8_t *string, uint16_t dir_inicial, uint16_t tamanio)
{
	uint16_t cont;
	
	if((dir_inicial+tamanio)>TAMANIO_MAX_EEPROM)
		return -1;
	
	for(cont=0; cont<tamanio; cont++)
		*(string+cont)=(uint8_t)eeprom_read_byte ((const uint8_t *)(dir_inicial+cont));
		
	return 1;
}

Un ejemplo de su uso:

PHP:
#include <avr/io.h>
#include <avr/eeprom.h>

#define TAMANIO_MAX_EEPROM 511

int8_t CopiarStringAEEPROM(uint8_t *string, uint16_t dir_inicial, uint16_t tamanio)
{
	uint16_t cont;
	
	if((dir_inicial+tamanio)>TAMANIO_MAX_EEPROM)
		return -1;
		
	for(cont=0; cont<tamanio; cont++)
		eeprom_write_byte ((uint8_t *)(dir_inicial+cont),(uint8_t)(*(string+cont)));
		
	return 1;
}

int8_t LeerStringEEPROM(uint8_t *string, uint16_t dir_inicial, uint16_t tamanio)
{
	uint16_t cont;
	
	if((dir_inicial+tamanio)>TAMANIO_MAX_EEPROM)
		return -1;
	
	for(cont=0; cont<tamanio; cont++)
		*(string+cont)=(uint8_t)eeprom_read_byte ((const uint8_t *)(dir_inicial+cont));
		
	return 1;
}

int main(void)
{
    int ret;
    uint8_t cadena[10]="Prueba";
    uint8_t cadena2[10];
	
    while(1)
    {
        //TODO:: Please write your application code 
	if(CopiarStringAEEPROM(cadena, 500, 6)<0)
	{
		//Tomar una decisión en caso de error!
		break;
	}
		
	if(LeerStringEEPROM(cadena2, 500, 6)<0)
	{
		//Tomar una decisión en caso de error!
		break;
	}
    }
}

Editado:

Del ejemplo, quitá la escritura del while para evitar quitarle vida útil a la memoria EEPROM.
 
Última edición:
hola
bueno aca subo un tutorial en PDF​
de AVR Freaks., traducido al español ., sobre el uso de la eeprom​
espero que les sirva de guia​
 

Adjuntos

  • Utilización de la memoria EEPROM en el AVR-GCC.pdf
    60.9 KB · Visitas: 11
Última edición:
Estuve probando las dos funciones que ofrece la librería en base al PDF que subió el loco y efectivamente funcionan sin problemas:

PHP:
int main(void)
{
    int ret;
	uint8_t cadena[10]="Prueba";
	uint8_t cadena2[10];
	void *pointer_eeprom=(void *)(500);
	
	eeprom_write_block ( cadena , pointer_eeprom , 6);
	
	while(1)
    {
        //TODO:: Please write your application code 
		
		eeprom_read_block ( cadena2 , pointer_eeprom , 6);
    }
}

En el AVR Studio 6, se pude ver como en esa posición de memoria se guarda el String.

Fotos:

Debug.jpg
 
Última edición:
hola
Estuve probando las dos funciones que ofrece la librería en base al PDF que subió el loco y efectivamente funcionan sin problemas:

PHP:
int main(void)
{
    int ret;
	uint8_t cadena[10]="Prueba";
	uint8_t cadena2[10];
	void *pointer_eeprom=(void *)(500);
	
	eeprom_write_block ( cadena , pointer_eeprom , 6);
	
	while(1)
    {
        //TODO:: Please write your application code 
		
		eeprom_read_block ( cadena2 , pointer_eeprom , 6);
    }
}

En el AVR Studio 6, se pude ver como en esa posición de memoria se guarda el String.

Fotos:

Ver el archivo adjunto 145456
bueno me alegra mucho que sirva., yo lo traduje a principio de año ., pero todavia no use nada de esto​
y perdonen si tiene errores de traduccion mi ingles es malo jajajajajajaja​
pero los ejemplos de codigo estan originales ( tal cual fueron publicados)​
 
Gracias por las demás sugerencias, hasta ahora he seguido con la mudanza hacia C pero aún no llego a la sección de escritura de EEPROM, ya con los ejemplos de cosme espero no trabarme de nuevo (y).

LocoDelafonola, amigo, gracias por el documento, ese foro de AVRFreaks ya lo tengo como biblia para mis consultas sobre temas de AVR.

Esta tarde estuve algo dedicado a otras chunches pero en un rato o mañana le seguiré con el desarrollo del programa. Gracias
 
Atrás
Arriba