AVR Serial Auto baud Algorithm

An attempt to make a fast AVR Autobauding algorithm for the AVR UART peripheral  

There have been many attempts to make fast Auto-bauding algorithms.
The basic requirement arises from the bootloaders and customized User interfaces using the Serial Port of the Microcontroller.
Here is my attempt to do the same.

The Algorithm is optimized to have the smallest foot print yet deliver the Best Performance.

Here it goes:

1. The "calb_uart" routine looks for the First lower edge on the PD0 line and then
   start Timer1 in Normal
mode. This is basically the Start Bit of RS-232
   Communication.

2. The next step is to stop the timer as and when the first rising edge of the Data
   bit 0 from the 0x55 pattern sent. Then Stops the Timer1.

3. The Timer count is loaded into a temporary location say Temp1.
     The calculation for the actual baud rate to be loaded into the baud rate
     generator register UBRR is done
     by the following calculation:

     We know that,
     Fbaud = Fosc / (16 * (UBRR + 1)) ->In Normal Async Mode --EQ1

     Also for the Timer Value in Temp1 for the Start Bit Duration,
     Tbit = (1/Fosc) * Temp1 * (Prescaler Value) --EQ2

     However the Prescalar used in this case is 1.
     Hence,
     Fosc = Temp1 / Tbit [From EQ2] --EQ3

     Now,
     Fbaud = 1/Tbit [By definition of Baud Rate] --EQ4

     Substituting in EQ3: - Assuming that we have go the correct timing
     Fosc = Temp1 * Fbaud --EQ5

     Now substituting in EQ1,
     Fbaud = Fbaud * Temp1 / (16 * (UBRR + 1))
 or, Temp1 = 16 * (UBRR + 1)

     Hence We have::

      UBRR = (Temp1 / 16) - 1 [Our Final Relation]

    So now we know how the Timer Value Temp1 is related to the UBRR.
    But in practice when all these calculations are done there is some inherent
    delay that is compensated
using the following Equation to get the final UBRR
    value.


    UBRR = ((Temp1 + 3) >> 4) - 1;

4. Now this UBRR value is loaded to the Baud Rate Registers

5. Check for the Reception of a proper 'U' for 4 times

6. If it is there then Exit
    Else If Dual Speed mode Feature is Enabled continue
    Else Goto Step 1 and Reinit all the parameter try again.

7. Calculate UBRR value as: UBRR = ((Temp1 + 3) >> 3) - 1;

8. Set the DOUBLE SPEED ENABLE bit U2X and load the UBRR register

9. Check for the Reception of a proper 'U' for 4 times

10. If it is there then Exit
      Else goto Step1

The CODE for the above algorithm has been tested on:

  • ATmega8@8MHz Internal 
  • ATmega16@12 MHz External

The code is provided in the page itself has the Double speed option disabled please enable it if required.

The code is enclosed alternatively you can download it - a_serial.c :

// ***********************************************************
// Project: Autobaud
// Author: Boseji
// Module description: Speed Autobaud
// ***********************************************************

#include <avr\io.h>              // Most basic include files
//Macros to Stop and Start Timer1
#define stop_timer     TCCR1B &=~(1<<CS10)
#define start_timer     TCCR1B |=(1<<CS10)
//#define __DOUBLE_BAUD_SEARCH__

typedef union{
    struct{
    unsigned char l; //Timer Storage Variable
    unsigned char h;
    }b;
    unsigned int v;
}timer_val;

// ***********************************************************
// Function Declarations
//
void calib_uart(void);
unsigned char rx_uart( void );
void tx_uart( unsigned char data );
// ***********************************************************
// Main program
//
int main(void)
{
    unsigned char data;
    PORTD|=(1<<PD0); //Enable Pull-up
   
    //// In Case of bootloader
    /*
    if(PIND&(1<<PD0))
    {
        PORTD&=~(1<<PD0);
        ( (void(*)(void)) (0x0000) )();
    }*/            
   
    calib_uart();
    while(1)
     {
         PORTD^=(1<<PD4);
         data = rx_uart();
         tx_uart(data);
     }
}
// ***********************************************************
// UART Calibration module
//
void calib_uart()
{
    timer_val tm,tm1;
    unsigned char ctr; //Bit Counter
    unsigned char data;//Data Received

    do
     {
         //Init Timer
        TCNT1H = 0;
        TCNT1L = 0; //Clear The Timer
        TCCR1B = 0; //Set to Normal Mode
        stop_timer; // Stop Timer
       
        while(PIND&(1<<PD0)); //Wait till the Break comes

        //Start Timer
        start_timer;
   
        while(!(PIND&(1<<PD0))); //Wait till the Break is over
      
        //Stop Timer
        stop_timer;

        //Save the Timer Values
        tm.b.l = TCNT1L;
        tm.b.h = TCNT1H;
        //adjust for proper reception
        tm.v+=3;
        //Let the Full Byte to be Received
        /*
        ctr = 4;
        do{
        //Cy1
        while( (PIND&(1<<PD0))); //Wait till the positive cycle is over
        while( (PIND&(1<<PD0))); //Wait till the positive cycle is over   
        while(!(PIND&(1<<PD0))); //Wait till the negative cycle is over
        while(!(PIND&(1<<PD0))); //Wait till the negative cycle is over
        }while(--ctr>0);
        */
        //Calculate the Time constant for Serial Port
        tm1.v=tm.v;
        tm1.v>>=4; //Div by 16
        --tm1.v; //Subtract 1
        //Load the UART constant Baud
        UBRRH = tm1.b.h;
        UBRRL = tm1.b.l;
       
        //Configure UART
        UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
        UCSRB = (1<<RXEN)|(1<<TXEN);
        //tx_uart('S');
        //Wait for Corrent Data reception
        ctr = 4;
        data = 0;
        do{
            data = rx_uart();
            //if(data == 'U')break;
        }while(--ctr>0);
        //Break if the Correct character is received
        if(data == 'U')break;
        #if defined(__DOUBLE_BAUD_SEARCH__)
        //tx_uart('S');
        //Else Continue Auto Bauding
        UCSRB = 0;
        //Calculate the New Time constant for Serial Port
        tm1.v=tm.v;
        tm1.v>>=8; //Div by 8
        --tm1.v; //Subtract 1
        //Load the UART constant Baud
        UBRRH = tm1.b.h;
        UBRRL = tm1.b.l;
        //Configure UART in Double Speed Mode
        UCSRA = (1<<U2X);
        UCSRB = (1<<RXEN)|(1<<TXEN);
        //tx_uart('S');
        //Wait for Corrent Data reception
        ctr = 4;
        data = 0;
        do{
            data = rx_uart();
            //if(data == 'U')break;
        }while(--ctr>0);
        //Break if the Correct character is received
        if(data == 'U')break;
        #endif
        //Else Continue to Main Auto Bauding
        UCSRB = 0;
        PORTD|=(1<<PD0); //Enable Pull-up
    }while(1);
}

unsigned char rx_uart( void )
{
    unsigned char data;
    /* Wait for data to be received */
    while ( !(UCSRA & (1<<RXC)) )
    ;
    /* Get and return received data from buffer */
    data = UDR;
    return data;
}
void tx_uart( unsigned char data )
{
    /* Wait for empty transmit buffer */
    while ( !( UCSRA & (1<<UDRE)) )
    ;
    /* Put data into buffer, sends the data */
    UDR = data;
}

By -

BoseJi

from INDIA

Mera Bharat Mahan!!