Search code examples
cavruartatmega

Understanding UART under an ATMEGA168A


I am trying to create a C program which receives a char via UART, "prints" the correspondent binary by turning on 8 leds in my breadboard and send the char back to the transmitter.

Here is the code I am using:

//CPU clock
#define F_CPU 1000000UL
//Baud
#define BAUD 9600
//Baud rate
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)

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

//Communication Parameters: 
//8 bits of data
//1 bit stop
//No parity
void uart_init(void){

    //Bit 7 - RXCIEn: RX complete interrupt enable
    //Bit 6 - TXCIEn: TX complete interrupt enable
    //Bit 5 - UDRIE: USART data register empty interrupt enable
    //Bit 4 - RXENn: Receiver enable
    //Bit 3 - TXENn: Transmitter enable
    UCSR0B = 0b10011000;

    //Bit 7 - RXCn: USART receive complete.
    //Bit 6 - TXCn: USART transmit complete
    //Bit 5 - UDREn: USART data register empty
    UCSR0A = 0b00000000;


    //Bit 11:0 – UBRR11:0: USART baud rate register
    //Whereas H are the higher bits and L the lower bits
    //It comes from the setbaud.h
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;

    //Bit 7:6 - UMSELn1:0: USART mode select
        //00    Asynchronous USART
        //01    Synchronous USART
        //11    Master SPI
    //Bit 5:3 - Reserved bits in MSPI mode
    //Bit 2 - UDORDn: Data order
    //Bit 1 - UCPHAn: Clock phase
    //Bit 0 - UCPOLn: Clock polarity
    UCSR0C = 0b10000110;
}

// function to send data
void uart_transmit (uint8_t data)
{
    while (!( UCSR0A & (1<<UDRE0)));            // wait while register is free
    UDR0 = data;                             // load data in the register
}

int main (void)
{
    //Starts UART
    uart_init();
    //All led GPIOs as output
    DDRB = 0xFF;
    DDRC = 0x01;
    //Enabling interrupts
    sei();

    while(1)
    {
        ;
    }

    return 0;
}

ISR(USART_RX_vect)
{
    //Variable to hold the incoming char
    uint8_t received_bit = UDR0;
    PORTC ^= 0x01;
    PORTB = 0x00;
    PORTB = received_bit;
    uart_transmit(received_bit);
}

When I flash it to the chip and start using it, I get a weird behaviour. I am sending a "U" which is a nice binary 01010101 to compare with. However I am getting weird answers back from my chip:

enter image description here

My questions regarding UART under an ATMEGA168a are the following:

  • When setting the F_CPU am I supposed to stay with the 1MHZ used by the ATMEGA168a or do I have to use the one of my transmitter (Intel i7)? Could it be the problem?
  • When does the UDR0 gets "updated"? Whenever I hit the enter to send the character to chip via Terminal?
  • What could be generating this issue?

Solution

  • In the function uart_init() you set bits 7:6 to 10 which is a reserved state according to the ATMega 168A manual. To get the desired asynchronous UART functionality, set them to 00:

    UCSR0C = 0b00000110;
    

    The other reason why your example was not working was the baudrate settings, as explained in my comment below.

    You already included the <util/setbaud.h> header file, which contains macros to make UART setup easier. Look here for the documentation. These macros take the input provided by you in F_CPU and BAUDRATE and calculate the settings for the UART configuration registers (UBRRH_VALUE and UBRRL_VALUE).

    You used it almost correctly, however to take advantage of the UART baudrate doubling feature of the ATmega, add the following code after setting the UBRR0H/L value:

    #if USE_2X
    UCSR0A |= (1 << U2X0);
    #else
    UCSR0A &= ~(1 << U2X0);
    #endif
    

    This sets or clears the U2X0 bit dependent on the calculations of the setbaud macros.

    Also, I believe you can remove the line

    #define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
    

    because that's exactly what setbaud.h does.