0
\$\begingroup\$

I'm learning AVR programming using this book

I was able to run and get output using the code provided along with the book. I use Proteus to simulate the output, since I don't have controller in hand.

Today I tried to write a simple UART program by using the data sheet.

/*
 * uart.c
 *
 * Created: 05-09-2018 09:55:36 PM
 * Author : New User
 */ 

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);                       
    UBRR0L = 6;
    char data;
    while (1) 
    {
        while (!(UCSR0A & (1<<RXC0)));
        data = UDR0;
        while (!(UCSR0A & (1<<UDRE0)));
        UDR0 = data;

    }
}

When I simulate code in Proteus I get weird characters in the virtual terminal:

virtual terminal screenshot

First I thought it might be due to an error in baud rate.

As my understanding, default F_CPU of ATMega168 is 1MHz if I don't use any external crystal oscillator. So for a baudrate of 9600, U0BRR = 6 from this equation:

U0BRR = (F_CPU/(16 * 9600)) - 1 = (1000000/16*9600)-1 = 5.51 ~6

Also in the datasheet, U0BRR is 6 for a baud rate 9600 with 1MHz clock.

So what's happening here?

IDE: AtmelStudio 7

Here's my Proteus settings:

Proteus settings

Is there anything I should change in Proteus?


After checking the working code that came with the book, it uses a avr/setbaud.h header file.

If I add code from that header file, my current code will look like this(Instead of adding header file, I used entire code directly for better understanding)

#include <avr/io.h>
#define F_CPU 1000000UL

#ifndef BAUD                
#define BAUD  9600                     
#endif

/* Baud rate tolerance is 2 % unless previously defined */
#ifndef BAUD_TOL
#  define BAUD_TOL 2
#endif

#ifdef __ASSEMBLER__
#define UBRR_VALUE (((F_CPU) + 8 * (BAUD)) / (16 * (BAUD)) -1)
#else
#define UBRR_VALUE (((F_CPU) + 8UL * (BAUD)) / (16UL * (BAUD)) -1UL)
#endif

#if 100 * (F_CPU) > \
  (16 * ((UBRR_VALUE) + 1)) * (100 * (BAUD) + (BAUD) * (BAUD_TOL))
#  define USE_2X 1
#elif 100 * (F_CPU) < \
  (16 * ((UBRR_VALUE) + 1)) * (100 * (BAUD) - (BAUD) * (BAUD_TOL))
#  define USE_2X 1
#else
#  define USE_2X 0
#endif

#if USE_2X
/* U2X required, recalculate */
#undef UBRR_VALUE

#ifdef __ASSEMBLER__
#define UBRR_VALUE (((F_CPU) + 4 * (BAUD)) / (8 * (BAUD)) -1)
#else
#define UBRR_VALUE (((F_CPU) + 4UL * (BAUD)) / (8UL * (BAUD)) -1UL)
#endif

#if 100 * (F_CPU) > \
  (8 * ((UBRR_VALUE) + 1)) * (100 * (BAUD) + (BAUD) * (BAUD_TOL))
#  warning "Baud rate achieved is higher than allowed"
#endif

#if 100 * (F_CPU) < \
  (8 * ((UBRR_VALUE) + 1)) * (100 * (BAUD) - (BAUD) * (BAUD_TOL))
#  warning "Baud rate achieved is lower than allowed"
#endif

#endif /* USE_U2X */

#ifdef UBRR_VALUE
   /* Check for overflow */
#  if UBRR_VALUE >= (1 << 12)
#    warning "UBRR value overflow"
#  endif

#  define UBRRL_VALUE (UBRR_VALUE & 0xff)
#  define UBRRH_VALUE (UBRR_VALUE >> 8)
#endif


int main(void) {

    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
    #if USE_2X
    UCSR0A |= (1 << U2X0);
    #else
    UCSR0A &= ~(1 << U2X0);
    #endif
    /* Enable USART transmitter/receiver */
    UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);   /* 8 data bits, 1 stop bit */

    char data;

    // ------ Event loop ------ //
    while (1) {
        while (!(UCSR0A & (1<<RXC0)));
        data = UDR0;
        while (!(UCSR0A & (1<<UDRE0)));
        UDR0 = data;
    }                                                  /* End event loop */
    return 0;
}

There's a variable called BAUD_TOL which is given a value as allowed tolerance from specified baud rate i.e., 9600

First it will calculate U0BRR value using this equation

U0BRR = (F_CPU/(16 * 9600)) - 1
==>U0BRR = (1000000/(16*9600))-1

this is the portion where it calculates U0BRR #define UBRR_VALUE (((F_CPU) + 8UL * (BAUD)) / (16UL * (BAUD)) -1UL)

This will give U0BRR = 6 then uses this value to calculate baud rate(This is our calculated baud rate) and compare it with the baud rate wanted(9600). If difference is more than allowed one (BAUD_TOL), it will set USE_2X = 1.

As per datasheet

Bit 1 – U2Xn: Double the USART Transmission Speed This bit only has effect for the asynchronous operation. Write this bit to zero when using synchronous operation. Writing this bit to one will reduce the divisor of the baud rate divider from 16 to 8 effectively doubling the transfer rate for asynchronous communication.

Then recalculate U0BRR using

U0BRR = (F_CPU/(8 * 9600)) - 1
==>U0BRR = (1000000/(8*9600))-1

See 16 from previous equation changed to 8.

Then it will check whether it has a deviation of more than 2%. If there's it will trigger error or warning.

And in the main() function it will set U2X0 of UCSR0A to 1

So above code can be further reduced to

#include <avr/io.h>
#define F_CPU 1000000UL

#define BAUD  9600

#define UBRR_VALUE (((F_CPU) + 4UL * (BAUD)) / (8UL * (BAUD)) -1UL)

#define UBRRL_VALUE UBRR_VALUE

int main(void) {

    // -------- Inits --------- //
    UBRR0L = UBRRL_VALUE;
    UCSR0A |= (1 << U2X0);
    UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);

    // ------ Event loop ------ //
    char data;
    while (1) {

        while (!(UCSR0A & (1<<RXC0)));
        data = UDR0;
        while (!(UCSR0A & (1<<UDRE0)));     /* Wait for incoming data */
        UDR0 = data;
    }                                                  
    return 0;
}

I have some doubts about this. About the way the code check baud rate is with in the acceptable range and what is the final baud rate after enabling U2X I don't know whether I can ask it here.

\$\endgroup\$
  • 1
    \$\begingroup\$ Given that your equation calculated a baud rate divisor of 5.5, and you used 6, it might be that the baud rate has a little too much error for your serial port to decode correctly. Could you try using a lower baud rate, say 1200 and seeing if it works then? \$\endgroup\$ – C_Elegans Sep 5 '18 at 18:55
  • 1
    \$\begingroup\$ When you checked with 1200, have you changed in terminal settings as well? 7% is a large error. UARTS usually tolerate up to 2-3% \$\endgroup\$ – Eugene Sh. Sep 5 '18 at 19:24
  • 4
    \$\begingroup\$ Can you connect the scope? It will tell you much more then experimenting with values. \$\endgroup\$ – Eugene Sh. Sep 5 '18 at 19:53
  • 1
    \$\begingroup\$ As I said in the post, I don't have a controller or oscilloscope in hand \$\endgroup\$ – Athul Sep 6 '18 at 4:54
  • 2
    \$\begingroup\$ Can you post the screenshot? Usually, if looking at regular TTL-level uarts, idle is high and start bit is low \$\endgroup\$ – Eugene Sh. Sep 6 '18 at 18:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.