4
\$\begingroup\$

I have prepared a setup for 2 Xbees one connected to the PC via XCTU and the other interfaced with Atmega 168. The terminal software will send 1 and the xbee with Atmega 168 will return 1.

The code is as follows.

#include<avr/io.h>
#include<avr/interrupt.h>
#include<util/delay.h>

    unsigned char data; //to store received data from UDR1



//Function To Initialize UART0
// desired baud rate:9600
// char size: 8 bit
// parity: Disabled
void uart0_init(void)
{
 UCSR0B = 0x00; //disable while setting baud rate
 UCSR0A = 0x00;
 UCSR0C = 0x06;
 UBRR0L = 0x06; //set baud rate lo
 UBRR0H = 0x00; //set baud rate hi
 UCSR0B = 0x98;
}


ISR(USART_RX_vect)      // ISR for receive complete interrupt
{
    data = UDR0;  //making copy of data from UDR0 in 'data' variable 
    if(data == 0x31)
    UDR0 = data;                //echo data back to PC

}

//Function To Initialize all The Devices
void init_devices()
{
 cli(); //Clears the global interrupts
 uart0_init(); //Initailize UART0 for serial communiaction
 sei();   //Enables the global interrupts
}

//Main Function
int main(void)
{
    init_devices();
    while(1);
}

This code works well and the following is the desired and the obtained output...

enter image description here

Now the problem is when I want to get reply 3 for a transmission of 3 from the PC. I am using avr studio 4 (yeah I know I am backdated) and Atmega 168PA-PU. The configuration for both cases is as shown in the image...

enter image description here

So I copied the previous program totally and just changed the line

ISR(USART_RX_vect)      // ISR for receive complete interrupt
{
    data = UDR0;  //making copy of data from UDR0 in 'data' variable 
    if(data == 0x31)
    UDR0 = data;                //echo data back to PC

}

to the following

ISR(USART_RX_vect)      // ISR for receive complete interrupt
{
    data = UDR0;  //making copy of data from UDR0 in 'data' variable 
    if(data == 0x33)
    UDR0 = data;                //echo data back to PC

}

But the obtained output is nowhere near what its supposed to be. The following image should clarify.

enter image description here

PS: We used separate (Atmega 168A-PU & Atmega 168PA-PU) for the two programs. It may so happen that there is some discrepancy in the internal oscillator. I have read somewhere that there can be a +/-10% inaccuracy.

\$\endgroup\$
  • \$\begingroup\$ do other characters have the problem? e.g. you could do data = UDR0; UDR0 = data; to echo any character \$\endgroup\$ – geometrikal Jan 21 '14 at 7:33
0
\$\begingroup\$

This problem is not regarding your software, because you ran the same program in both cases right? So it must be hardware. The irregularity in the 2nd case suggests that there is some baudrate mismatch. So I conclude that the problem is definitely because of the internal RC oscillators which are not accurate as you mentioned yourself.

Try using external quartz oscillator. Connect it with XTAL1 & XTAL2 with suitable capacitors. I personally use 12MHz with 22pF caps. Change the fuse setting before burning the program. Try Googling avrdude fuse calculator.

\$\endgroup\$
  • \$\begingroup\$ If it works let me know !! \$\endgroup\$ – Rambo partyush Jan 29 '14 at 0:39
2
\$\begingroup\$

I'm not 100% sure this will fix your problem but normally before sending data you'd want to check that the transmitter holding register is empty before sending a character. You could try this:

ISR(USART_RX_vect)      // ISR for receive complete interrupt
{
    data = UDR0;  //making copy of data from UDR0 in 'data' variable 
    if(data == 0x33)
    {
        while(!(UCSR0A & (1 << UDRE0))) // Wait for data register to be empty
            ;
        UDR0 = data;                //echo data back to PC
    }
}

But doing the wait inside the interrupt might cause you to miss received characters. Instead I'd normally do something more like this:

volatile unsigned char data, got_data;
unsigned char buffered_data;

ISR(USART_RX_vect)      // ISR for receive complete interrupt
{
    data = UDR0;  //making copy of data from UDR0 in 'data' variable 
    got_data = 1;
}

int main(void)
{
    init_devices();
    while (1)
    {
        if (got_data)
        {
            // Ideally you should disable interrupts here
            buffered_data = data;
            got_data = 0;
            // And re-enable interrupts here
            if (buffered_data == 0x33)
            {
                while(!(UCSR0A & (1 << UDRE0))) // Wait for data register to be empty
                    ;
                UDR0 = buffered_data;                //echo data back to PC
            }
       }
    }
}

I also just noticed your addition about the clock having a potential innacuracy of +/- 10% which is a bit large for reliable communications. While I normally aim for 2% accuracy when using serial communications you should aim for less than 5% otherwise you can about half a bit out by the end of 10 bits.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.