Serial Communication Using the UART


In this tutorial, serial communication using the UART module of the ATMega32 is explained. For other MCUs of the ATMega series, the general structure will remain the same except for the names of the registers used to configure and control the UART. Serial communication is a method of sending data one bit at a time. It is achieved (with a PC or any other device) using the UART module omnipresent in all versions of the ATMega series of microcontrollers. UART(Universal Asynchronous Receiver Transmitter) translates data between serial and parallel forms. The UART module is first configured and then used for data transfer. Registers are provided which enables the programmer to effectively configure the UART and then transmit and receive data.

Registers (as seen on the ATMega32) are as follows -

  • USART I/O Data Register [UDR]
  • USART Control and Status Register(s) [UCSR(A/B/C)]
  • USART Baud Rate Registers [UBRRH and UBRRL]

The USART Transmit Data buffer register (TXB) and the USART Receive Data buffer register (RXB) share the same I/O address referred to as the USART Data Register [UDR]. The TXB is the destination for data written to the UDR and reading the UDR return the contents of the RXB.

The USART Control and Status register(s) configure the UART. The interrupts, flags, etc., and configuration of the receiver and the transmitter are done using the control registers.

The USART Baud Rate Registers [UBRRH and UBRRL] are used to set the baud rate of the serial connection.

The Baud Rate value can be set between 2400 bps to 250 kbps. The corresponding values for the UBRRH and UBRRL for various oscillators can be found in the table (Examples of UBRR Settings for Commonly Used Oscillator Frequencies) given in the datasheet under USART.

In this tutorial, the frequency of the oscillator used is 11.0592 MHz (for information related to setting fuse bytes for connecting external oscillator, click here), and the BAUD rate of the serial connection set at 115.2 kbps (by assigning the UBRR value to 5).

For transmission of data, the byte to be sent is placed in the UDR.

For receiving data, the RX Complete Interrupt Enable [RXCIE] bit in the UCSRB register is set which enables the interrupt on reception of data. Then in the interrupt handler, the byte is read from the UDR.

In the interrupt service routines (ISR), a global variable cannot be accessed. The global variables need to be prefixed with the volatile keyword. This tells the two threads (the main execution loop and the ISR code) to not cache the value in a register, but always retrieve it from memory.

C Code for the UART module


/*  UART implementation using the ATMEL ATMega32 MCU
    By - Nandan Banerjee (08/CSE/15)
         NIT Durgapur
         16/06/2010
*/

// UART Baud rate - 115200 bps
#define UBRR 5                 

void uart_init()
{
    UBRRH = (uint8_t)(UBRR >> 8);
    UBRRL = (uint8_t)(UBRR);
    UCSRB = ((1<<RXEN) | (1<<TXEN) | (1<<RXCIE));   // Enable Receiver, Transmitter, Receive Interrupt
    UCSRC = ((1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0));     // 8N1 data frame
}

void uart_putc(unsigned char c)
{
    // wait until UDR ready
    while(!(UCSRA & (1 << UDRE)));
    UDR = c;    // send character
}

void uart_puts(const char *s)
{
    //  loop until *s != NULL
    while(pgm_read_byte(s) != 0x00)
        uart_putc(pgm_read_byte(s++));
}

void uart_write_int16(int16_t in)
{
    uint8_t started = 0;
    uint16_t pow = 10000;
   
    while (pow >= 1)
    {
        if (in/pow > 0 || started || pow == 1)
        {
            uart_putc((uint8_t) (in/pow) + '0');
            started = 1;
            in = in % pow;
        }
        pow = pow / 10;
    }
}

ISR(USART_RXC_vect)
{
    // USART RX interrupt
    unsigned char c = UDR;
   
    // User code to handle the interrupt goes here.
}