Search code examples
cavrusartbaud-rate

How to set ATMEGA64a-au USART speed to 115200?


As the title impplies, I've ATMEGA64a-au and 12mhz crystal, I set the baud rate to 9600 by these codes:

#define F_CPU 12000000UL

#define FOSC 12000000 // Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1

..

void USART0_Init( unsigned int ubrr )
{
    UBRR0H = (unsigned char) (ubrr >> 8);
    UBRR0L = (unsigned char) ubrr;
    UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
    // Set frame format: 8data, 1stop bit
    UCSR0C = (1 << USBS0) | (3 << UCSZ00);

}

void USART1_Init( unsigned int ubrr )
{
    UBRR1H = (unsigned char) (ubrr >> 8);
    UBRR1L = (unsigned char) ubrr;
    UCSR1B = (1 << RXEN1) | (1 << TXEN1) | (1 << RXCIE1);
    // Set frame format: 8data, 1stop bit 
    UCSR1C = (1 << USBS1) | (3 << UCSZ10);
}

..

USART0_Init(MYUBRR);
USART1_Init(MYUBRR);

It is fine when I work with that baud rate but I've recently come a cross a 115200bd device and I need too change it to that, but when I change this line :

#define BAUD 115200

The AVR Micro controller stops communicating, and I've no idea how to fix it. is it too fast for that device or I'm missing something?


Solution

  • #define MYUBRR FOSC/16/BAUD-1

    First of all there is an issue with this formula: it rounds always downwards. You need to round it to near integer, e.g. (FOSC / 16 + BAUD / 2) / BAUD-1

    By the way why it is FOSC and not F_CPU?

    Anyway, you can calculate the error when formula FOSC/16/BAUD-1 is used:

    MYUBRR = 12000000/16/115200-1 = 5 (5.51 rounded downwards)

    Actual UART speed will be:

    F_CPU / 16 / (MYUBRR + 1) = 12000000 / 16 / (5 + 1) = 125000

    which is 8.5% higher than required. Communication with receiver-transmitter baud rate difference more than 4.5% is not possible.

    But you can set Double Speed Operation mode (refer to section 25.3.2 of the datasheet)

    It can be done by setting bit U2X bit in UCSRA.

    In that case UART clock will be 1/8 of the CPU clock, thus allowing higher UBRR settings:

    // divide by 8, not 16
    #define MYUBRR ((FOSC / 8 + BAUD / 2) / BAUD - 1)
    
    ...
    
    void USART0_Init( unsigned int ubrr )
    {
        // you can just assign the value to UBRR, compiler will handle all the thighs
        UBRR0 = ubrr;
        UCSR0A = (1 << U2X0); // enable 2x mode
        UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
        // Set frame format: 8data, 1stop bit
        UCSR0C = (1 << USBS0) | (3 << UCSZ00);
    }
    

    In this case MYUBRR value will be:

    (12000000 / 8 + 115200 / 2) / 115200 - 1 = 12

    and actual UART speed will be

    12000000 / 8 / (12 + 1) = 115384,6

    which gives you less than 0.2% error