Atmega8 comunicacion serial + EEPROM

#1
Hola gente, recurro nuevamente a ustedes para aclarar una duda y quizas puedan ayudarme.
Estoy comunicando dos Atmega8 de forma serial, el TX tiene conectado un pulsador a PC0 y otro a PC1, que al ser presionados envian un dato por el puerto serial.
El RX tiene conectado a PD7 un pulsador que mientras este presionado y llegue un dato por el puerto serial, lo graba en la EEPROM del mismo uC. Si el dato ya fue grabado, desactivo PD7 y cuando presiono algun pulsador de TX, este envia un dato que es comparado en el RX con el almacenado en su EEPROM y asi poder activar o desactivar el parpadeo de unos leds conectados al puerto C del RX.
Mi problema es que solo me esta permitiendo grabar el dato de un solo pulsador y asi puedo activar pero no puedo grabar el otro pulsador para desactivar la funcion del parpadeo.
Adjunto los codigos fuente y el esquema electrico mas archivos para aquel que pueda ayudarme u orientarme a resolverlo. Desde ya, muchas gracias.




Codigo TX:

PHP:
/*
	Nombre: TX.c
	
*/ 

/* Configuraciones Generales */
#define  F_CPU 8000000UL 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usart.h"

/* Variables a Utilizar */				
uint8_t encender = 'a';			
uint8_t apagar = 'b';			

int main(void)
{	
	// Comunicacion serial a 4800 baudios
	SerialBegin(103);	
								
	// PC0 y PC1 como entrada
	DDRC = (0<<PC1)|(0<<PC0);
	
	// PORTB como entrada
	DDRB = 0x00;

	// Limpiamos PORTC
	PORTC = 0x00;			
	
	//Habilito las interrupciones globales
	sei();							
	
    while(1)
    {
		// Testeo estado de PC0
		if (bit_is_clear(PINC,PC0) == 0)
		{
			// Retardo para verificar la pulsacion
			_delay_ms(50);
		
					// Testeo nuevamente PC0			
			if (bit_is_clear(PINC,PC0) == 0)
			{
				// Envio el dato "encender"
				SerialWrite(encender);	
			}				
		}
		// Testeo estado de PC1
		if (bit_is_clear(PINC,PC1) == 0)
		{
			// Retardo para verificar la pulsacion
			_delay_ms(50);

			// Testeo nuevamente PC1
			if (bit_is_clear(PINC,PC1) == 0)
			{
				// Envio el dato "apagar"
				SerialWrite(apagar);
			}			
		}		
    }
}
Codigo RX:

PHP:
/*
	Nombre: RX.c

 */ 

/* Configuraciones Generales */
#define F_CPU 8000000UL 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include "usart.h"

/* Variables a utilizar */
uint8_t dato_serial;
uint8_t contador;

ISR (TIMER0_OVF_vect)
{
	// Incrementa variable	
	contador++;

	// Pregunto si el contador es igual a 7
	if (contador == 7)
	{
		// Invierte el valor de PORTC
		PORTC = ~ PORTC;  

		// Pone contador a 0
		contador = 0 ;	
	}
}

ISR(USART_RXC_vect)
{
	// Almaceno el dato recibido por puerto serial en la variable "dato_serial"
	dato_serial = SerialRead();
	// Pregunto si el pulsador de prog. esta presionado y el codigo recibido en "dato_serial"...
	if ((dato_serial) && (bit_is_clear(PIND,PD7) == 0))
	{
		// Lo almacenado en la EEPROM del microcontrolador 
		eeprom_write_byte((uint8_t*)0,dato_serial);
	// Si el codigo ya fue grabado...
	}else{
		// Compara el codigo recibido, con el codigo almacenado en la EEPROM, y si coinciden...
		if ((dato_serial) == eeprom_read_byte((uint8_t*)0))
		{
			// Habilita la interrupcion por overflow - Parpadeo de leds
			TIMSK = (1<<TOIE0);
		}
	}
}

void InterrupOvf(void)
{
	// Prescaler = FCPU/1024
	TCCR0 = (1<<CS02)|(1<<CS00);
	// Inicio interrupcion por Overflow deshabilitada
	TIMSK = (0<<TOIE0);
	// Inicializo contador en 0
	TCNT0 = 0;
}

int main(void)
{					
	// Comunicacion serial a 4800 baudios
	SerialBegin(103);	
	
	// Funcion de interrupcion por Overflow
	InterrupOvf();
				
	// Puerto C [3,2,1,0] como salida
	DDRC = 0x0F ;

    // Limpio PORTC
	PORTC = 0x00;	

	// Habilito interrupciones globales
	sei();

    while(1)
    {
		//...
    }
}
 

Adjuntos

#2
Tips genéricos.
Nunca uses delays para nada.
Nunca uses uart software. Si además tienes uarts hardware ya es que no se sostiene.
 
#3
hola
Tips genéricos.
Nunca uses delays para nada.
Nunca uses uart software. Si además tienes uarts hardware ya es que no se sostiene.
los ATMEGAS no son iguales que los PIC
., las UART"s trabajan todas por software​
lo de los delay"s tenes razon mejor timer (aunque nesesitaria antirrebotes en los pulsadores)​
y el dato importante es que deben tener cristal externo (aparte ., todos los simuladores dan error ., lo mejor es probar en lo fisico)​
 
Última edición:
#4
Editado.

La verdad es que fijándome detenidamente si parece usar la uart hard. En la primera lectura me pareció que no.



Hay un puntero por ahí que no entiendo. El uint_8* ese asterisco no me cuadra
 
Última edición:
#5
hola
Editado.

La verdad es que fijándome detenidamente si parece usar la uart hard. En la primera lectura me pareció que no.



Hay un puntero por ahí que no entiendo. El uint_8* ese asterisco no me cuadra
siiiii tenes razon scooter ., pero hay otro problema​
de RX a TX no hay nada que lo cambie .,dentro del codigo ., y es una funcion por vez o sea RX ., o TX ., o EEPROM (no cambian solas jejejejejejeje)​
 
#6
Hola Muchachos! Gracias por responder nuevamente!
El puntero uint_8* es una variable declarada dentro de la misma funcion, de otra forma,
la podria declarar al inicio del programa de esta forma:

uint8_t EEMEM dato;

Tome el ejemplo de uso de unas de las paginas que me recomendaron con anterioridad!
Aqui el link: http://microcontroladores-mrelberni.com/eeprom-avr-memoria-interna/

--------------------------------------------------------

eeprom_write_byte((uint8_t*)0,dato_serial);

eeprom_write_byte((variable_eeprom)direccion_eeprom,dato_a_guardar);

Algo asi seria la funcion de la libreria!



Edito:

No entendi esto

pero hay otro problema
de RX a TX no hay nada que lo cambie .,dentro del codigo ., y es una funcion por vez o sea RX ., o TX ., o EEPROM
 
Última edición:
#7
hola
Hola Muchachos! Gracias por responder nuevamente!
El puntero uint_8* es una variable declarada dentro de la misma funcion, de otra forma,
la podria declarar al inicio del programa de esta forma:

uint8_t EEMEM dato;

Tome el ejemplo de uso de unas de las paginas que me recomendaron con anterioridad!
Aqui el link: http://microcontroladores-mrelberni.com/eeprom-avr-memoria-interna/

--------------------------------------------------------

eeprom_write_byte((uint8_t*)0,dato_serial);

eeprom_write_byte((variable_eeprom)direccion_a_guardar,dato_a_guardar);

Algo asi seria la funcion de la libreria!



Edito:

No entendi esto
estas simulando y alli es el problema ., las USART tiene que probarce en lo fisico​
porque los errores de codigo son mas comunes de lo que se cree​
aca te subo unos ejemplos de atmel (AVRlib) USART y agunas configuraciones usadas​
una usart trasmite y recibe ., pero hace una cosa por vez ., y las configuraciones para transmicion y recepcion cambian en el codigo​
 

Adjuntos

Última edición:
#8
hola
estas simulando y alli es el problema ., las USART tiene que probarce en lo fisico​
porque los errores de codigo son mas comunes de lo que se cree​
aca te subo unos ejemplos de atmel (AVRlib) USART y agunas configuraciones usadas​
una usart trasmite y recibe ., pero hace una cosa por vez ., y las configuraciones para transmicion y recepcion cambian en el codigo​
Hola locodelafonola, te agradezco mucho por los archivos! Los voy a estar mirando en estos dias.
Te comento que pude solucionar el problema de una forma sencilla por asi decirlo. Lo que hice fue crear una especie de "protocolo" de comunicacion un poco precario, pero con eso dio resultado!
Agregue un pulsador mas que se conecta a PC2 en el atmega TX y envia una clave, cuando el pulsador PD7 del atmga RX esta presionado, recibe por puerto serial la clave de PC2 y este la almacena en la EEPROM.
Esto me permite realizar una comparacion con IF anidados, donde pregunto en el RX si el codigo recibido es igual al almacenado en la EEPROM y asi poder leer el siguiente dato que recibe por puerto serial para poder encender/apagar el parpadeo de leds!
Lo que quiero ver ahora y si puedo realizar por medio de una funcion, es que me avise cuando se graba el codigo en la EEPROM con un led aparte y tambien poder borrar la EEPROM con otro pulsador conectado en el RX.
Les adjunto los codigos y el esquematico!





Codigo TX:

PHP:
/*
	Nombre: TX.c
	
*/ 

/* Configuraciones Generales */
#define  F_CPU 8000000UL 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usart.h"

/* Variables a Utilizar */				
uint8_t encender = 'a';			
uint8_t apagar = 'b';			
uint8_t clave = 100;

int main(void)
{	
	// Comunicacion serial a 4800 baudios
	SerialBegin(103);	
								
	// PC0, PC1 y PC2 como entrada
	DDRC = (0<<PC2)|(0<<PC1)|(0<<PC0);
	
	// PORTB como entrada
	DDRB = 0x00;

	// Limpiamos PORTC
	PORTC = 0x00;			
	
	//Habilito las interrupciones globales
	sei();							
	
    while(1)
    {
		// Testeo estado de PC0
		if (bit_is_clear(PINC,PC0) == 0)
		{
			// Retardo para verificar la pulsacion
			_delay_ms(50);
		
					// Testeo nuevamente PC0			
			if (bit_is_clear(PINC,PC0) == 0)
			{
				// Envio el dato "clave"
				SerialWrite(clave);
				// Espero 10ms
				_delay_ms(10);
				// Envio el dato "encender"
				SerialWrite(encender);	
			}				
		}
		// Testeo estado de PC1
		if (bit_is_clear(PINC,PC1) == 0)
		{
			// Retardo para verificar la pulsacion
			_delay_ms(50);

			// Testeo nuevamente PC1
			if (bit_is_clear(PINC,PC1) == 0)
			{
				// Envio el dato "clave"
				SerialWrite(clave);
				// Espero 10ms
				_delay_ms(10);
				// Envio el dato "apagar"
				SerialWrite(apagar);
			}			
		}		
		// Testeo estado de PC2
		if (bit_is_clear(PINC,PC2) == 0)
		{
			// Retardo para verificar la pulsacion
			_delay_ms(50);

			// Testeo nuevamente PC2
			if (bit_is_clear(PINC,PC2) == 0)
			{
				// Envio el dato "clave"
				SerialWrite(clave);
			}
		}
    }
}

Codigo RX:

PHP:
/*
	Nombre: RX.c

 */ 

/* Configuraciones Generales */
#define F_CPU 8000000UL 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include "usart.h"

/* Variables a utilizar */
uint8_t dato_serial;
uint8_t contador;
uint8_t encender = 'a';
uint8_t apagar = 'b';

ISR (TIMER0_OVF_vect)
{
	// Incrementa variable	
	contador++;

	// Pregunto si el contador es igual a 7
	if (contador == 7)
	{
		// Invierte el valor de PORTC
		PORTC = ~ PORTC;  

		// Pone contador a 0
		contador = 0 ;	
	}
}

ISR(USART_RXC_vect)
{
	// Almaceno el dato recibido por puerto serial en la variable "dato_serial"
	dato_serial = SerialRead();
	// Pregunto si el pulsador de prog. esta presionado y el codigo recibido en "dato_serial"...
	if ((dato_serial) && (bit_is_clear(PIND,PD7) == 0))
	{
		// Y Lo almaceno en la EEPROM del microcontrolador 
		eeprom_write_byte((uint8_t*)0,dato_serial);
	// Si el codigo ya fue grabado...
	}else{
		// Compara el codigo recibido, con el codigo almacenado en la EEPROM, y si coinciden...
		if ((dato_serial) == eeprom_read_byte((uint8_t*)0))
		{
			// Almaceno el siguente dato recibido del puerto serial en "dato_serial"
			dato_serial = SerialRead();
			_delay_ms(10);
			// Si "dato_serial" es igual a encender...
			if (dato_serial == encender)
			{
				// Habilita la interrupcion por overflow - Activo Parpadeo de leds
				TIMSK = (1<<TOIE0);
			}
			// Si "dato_serial" es igual a apagar...
			if (dato_serial == apagar)
			{
				// Deshabilito la interrupcion por overflow - Desactivo parpadeo de leds
				TIMSK = (0<<TOIE0);
				// Limpio PORTC
				PORTC = 0x00;
			}
		}
	}
}

void InterrupOvf(void)
{
	// Prescaler = FCPU/1024
	TCCR0 = (1<<CS02)|(1<<CS00);
	// Inicio interrupcion por Overflow deshabilitada
	TIMSK = (0<<TOIE0);
	// Inicializo contador en 0
	TCNT0 = 0;
}

int main(void)
{					
	// Comunicacion serial a 4800 baudios
	SerialBegin(103);	
	
	// Funcion de interrupcion por Overflow
	InterrupOvf();
				
	// Puerto C [3,2,1,0] como salida
	DDRC = 0x0F ;

    // Limpio PORTC
	PORTC = 0x00;	

	// Habilito interrupciones globales
	sei();

    while(1)
    {
		//...
    }
}
 
Última edición:
#9
Fijate si te sirven estas funciones que implementé en su momento:

Configuración de interrupciones:

PHP:
//----------------- Interrupciones y configuración ---------------//
void configura_interrupciones(u8 externa_config)
{
	GICR&=0x3f;		// Deshabilito IE0 e IE1
	GICR|=externa_config;		// Configuro IE0 e IE1
	SREG |=0x80;	// Habilito las interrupciones
}

ISR(USARTRXC_vect)
{
	//estado_paridad_uart=UCSRA&(1<<PE); //Guarda el bit de Paridad (0: sin error, 1: error)
	dato_rx=UDR;
	flag_rx=1;
}


ISR(USARTTXC_vect)
{
	flag_tx=1;
}

ISR(TIMER0_OVF_vect)
{
	flag_timer0=1;	
}
//----------------- Interrupciones y configuración ---------------//
Es muy probable que "USARTRXC_vect" y "USARTTXC_vect" sean distintas en el Atmega8 (esto está pensado para el 16). El timer se usa de time-out.

Funciones de Uart:

PHP:
void configura_timer0(u8 offset_contador,u8 preescaler)
{
	TCCR0=0x0;	//Deja de contar
	TCNT0=offset_contador;
	switch(preescaler)
		{
			case 1:	{TCCR0=0x01; break;} // clk-io
			case 2:	{TCCR0=0x02; break;} // clk-io/8
			case 3:	{TCCR0=0x03; break;} // clk-io/64
			case 4:	{TCCR0=0x04; break;} // clk-io/256
			case 5:	{TCCR0=0x05; break;} // clk-io/1024
			default:     {TCCR0=0x0;}		 // No cuenta
		}
	TIMSK |=0x01;	//Habilito mascara de interrupción del timmer0	
}

void configura_uart()
{								
	UBRRL=51;	//Elijo velocidad para fosc=8MHz => UBRR=fosc/(16*Baudios)-1 --- 9600bps
	UBRRH=0;
	UCSRB = (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN);  //Habilito Tx y Rx con sus respectivas INT
	//UCSRC = (1<<URSEL)|(1<<UPM1)|(1<<UPM0)|(1<<UCSZ1)|(1<<UCSZ0);	// Modo 8 bits y paridad impar
	UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);	// Modo 8 bits y sin bit de paridad
}

int recibe_dato_rs232()
{
	configura_timer0(0x00,5); //32mSeg de time-out --> 8MHz
	flag_timer0=0;
	
	while((!flag_rx)&&(!flag_timer0));	//Espera que el puerto este libre
	
	if(flag_rx)
		{
	    	flag_rx=0;
		/*
		if(estado_paridad_uart)	//Verifica que no haya error de paridad
			return -1;
		else*/
			return 1;
		}
	
	return -1;
}

int envia_dato_rs232(u8 dato)
{
	configura_timer0(0x00,5); //32mSeg de time-out --> 8MHz
	flag_timer0=0;
	
	while((!flag_tx)&&(!flag_timer0));	//Espera que el puerto este libre
	
	if(flag_tx)
		{
		UDR=dato;
	    flag_tx=0;
		return 1;
		}		
		
	return -1;
}
Las funciones de recepción y transmisión son bloqueantes, es decir, lo ideal es llamarlas sabiendo previamente que el flag_tx o flag_rx están en 1. Sin embargo, se implementó un time-out de 32mSeg, al pasar ese tiempo, la función devuelve -1.

Luego en main simplemente se debe hacer esto:

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

//-------------- Definiciones de tipos de variables
#define u8 unsigned char
#define u16 unsigned int
//-------------- Definiciones de tipos de variables

//-------------- Variables globales de interrupciones
u8 flag_timer0=0, flag_rx=0, flag_tx=1, dato_rx=0;
//-------------- Variables globales de interrupciones

int main(void)
{
   u8 flag_respuesta=0;
   configura_interrupciones(0); //Se habilita las interrupciones generales sin interrupciones externas.
   configura_uart();
   //... tú inicialización.

   while(1)
   {
       //... tú código

       if(flag_rx)
       {
          if(recibe_dato_rs232()<0)
          {
              //..Error durante la recepción
          }
          else
          {
              if(dato_rx=0x30)
              {
                  dato_rx=0;
                  flag_respuesta=1;
              }
          }
       }

       if(flag_tx&&flag_respuesta)
       {
          if(envia_dato_rs232(0x31)<0)
          {
              //..Error durante el envío
          }
          else
              flag_respuesta=0;
       }       
   }
}
En este ejemplo, lo único que hace el programa principal es inicializar el puerto serie y las interrupciones, por último se queda esperando a recibir el dato "0x30" (el 0 en Ascii) y al hacerlo devuelve el dato "0x31" (el 1 en Ascii).

Después tengo dos funciones que usé para manejar el EEPROM propias de las librerías que te daba Atmel:

PHP:
eeprom_write_byte ((uint8_t *)direccion,(uint8_t)(dato));
dato=eeprom_read_byte ((const uint8_t *)direccion);
 
#10
Hola.
Fíjate si te sirven estas funciones que implementé en su momento:
Exacto Cosme. Eso era lo que trataba de decirle, aparte tenés razón sobre la USART.
Habría que fijarse en la hoja de datos del atmega8, si son iguales al del atmega16 (creo que no)

Si usa el Atmel Studio le va a "saltar" el error si no es así.
Pero claro, vos lo ejemplificaste como debe ser. ¡Genio total! :aplauso: :aplauso: :aplauso: :aplauso: :aplauso:
Aparte me gustó eso de la interrupción externa y también en la función de TX. ¡No hay retardos! Sino que usas FLAG (Banderas para el contador) (y) (y) (y) (y)
 
Última edición por un moderador:
#11
Excelente aporte cosmefulanito04, voy a ver si con esto puedo corregir ciertas cosas de mi codigo! Gracias a ambos por tomarse el tiempo en explicarme! :D

Edito:

Utilizo Atmel Studio 7 para programar y las funciones de USART para el mega8 son:

USART_RXC_vect
USART_RXC_vect_num

USART_TXC_vect
USART_TXC_vect_num

USART_UDRE_vect
USART_UDRE_vect_num
 
Última edición:
#12
Hola.
Excelente aporte cosmefulanito04, voy a ver si con esto puedo corregir ciertas cosas de mi código! Gracias a ambos por tomarse el tiempo en explicarme! :D

Edito:

Utilizo Atmel Studio 7 para programar y las funciones de USART para el mega8 son:

USART_RXC_vect
USART_RXC_vect_num

USART_TXC_vect
USART_TXC_vect_num

USART_UDRE_vect
USART_UDRE_vect_num
Es muy probable que "USARTRXC_vect" y "USARTTXC_vect" sean distintas en el Atmega8 (esto está pensado para el 16). El timer se usa de time-out.
Bueno, fíjate en las líneas de código que te pasó él y reemplazas por esa que decís de la hoja de datos.​
Y fíjate si funciona todo bien. Hazle caso a Cosme, es una de las personas que más sabe.
Yo aprendí muchísimo de él. ¡Un genio total¡​
:aplauso: :aplauso: :aplauso: :aplauso: :aplauso: :aplauso: :aplauso:
 
Última edición por un moderador:
Arriba