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!!