Haz una pregunta
  Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos
Foros Registrarse ¿Olvidaste tu contraseña?

Temas similares

01/04/2016 #1

Avatar de gustavo

Atmega8 comunicacion serial + EEPROM
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:

Código 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:

Código 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 ;    
    }
}

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)
    {
        
//...
    
}

01/04/2016 #2

Avatar de Scooter

Tips genéricos.
Nunca uses delays para nada.
Nunca uses uart software. Si además tienes uarts hardware ya es que no se sostiene.
01/04/2016 #3

Avatar de locodelafonola

hola
Scooter dijo: Ver Mensaje
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)
01/04/2016 #4

Avatar de Scooter

Editado.

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

---------- Actualizado después de 10 minutos ----------

Hay un puntero por ahí que no entiendo. El uint_8* ese asterisco no me cuadra
01/04/2016 #5

Avatar de locodelafonola

hola
Scooter dijo: Ver Mensaje
Editado.

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

---------- Actualizado después de 10 minutos ----------

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)
01/04/2016 #6

Avatar de gustavo

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....moria-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
01/04/2016 #7

Avatar de locodelafonola

hola
gustavo dijo: Ver Mensaje
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....moria-interna/

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

eeprom_write_byte((uint8_t*)0,dato_serial);

eeprom_write_byte((variable_eeprom)direccion_a_gua rdar,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
Archivos Adjuntos
Tipo de Archivo: rar AVRlib.rar (17,5 KB (Kilobytes), 1 visitas)
Tipo de Archivo: rar conf.rar (2,5 KB (Kilobytes), 1 visitas)
02/04/2016 #8

Avatar de gustavo

locodelafonola dijo: Ver Mensaje
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:

Código 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:

Código 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 ;    
    }
}

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)
    {
        
//...
    
}

03/04/2016 #9

Avatar de cosmefulanito04

Fijate si te sirven estas funciones que implementé en su momento:

Configuración de interrupciones:

Código 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:

Código 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:

Código 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=0flag_rx=0flag_tx=1dato_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:

Código PHP:
eeprom_write_byte ((uint8_t *)direccion,(uint8_t)(dato));
dato=eeprom_read_byte ((const uint8_t *)direccion); 
03/04/2016 #10

Avatar de locodelafonola

Hola.
cosmefulanito04 dijo: Ver Mensaje
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!
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)
03/04/2016 #11

Avatar de gustavo

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!

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
03/04/2016 #12

Avatar de locodelafonola

Hola.
gustavo dijo: Ver Mensaje
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!

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
cosmefulanito04 dijo: Ver Mensaje

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¡
Respuesta
¿Tienes una mejor respuesta a este tema? ¿Quieres hacerle una pregunta a nuestra comunidad y sus expertos? Registrate

Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO ©2011, Crawlability, Inc.