Electrical – Setting baud rate for Atmega328p with setbaud.h

atmega328pbaudrateuart

I'm working with an Atmega328p, running off the internal 8MHz oscillator, divided by 8 to produce a 1MHz clock. I set the fuses according to the calculator here. Confirming this with avrdude:

avrdude -p m328p -c usbasp 

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)

avrdude: safemode: Fuses OK (E:FF, H:D9, L:62)

The timing seems to be correct – blinking an led on and off every 1000 ms takes ca. 1 second, as expected.

I am confused when it comes to setting up serial communication to my computer. No matter what I try, the connection is set to 1200. The program I'm using is:

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

int main(void) {
    DDRB |= 0b0000001;

    initUSART();

    while (1) {

        printString("\n\r==== Looping ====\n\r");

        PORTB = 0b00000001;
        _delay_ms(750);    

        PORTB = 0b00000000;
        _delay_ms(250);    
    }
  return (0);
}

Where USART is from Elliott Willams' book. It includes the following:

#include <avr/io.h>
#include "USART.h"
#include <util/setbaud.h>

void initUSART(void) {                                /* requires BAUD */
  UBRR0H = UBRRH_VALUE;                        /* defined in setbaud.h */
  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 */
}

// more code follows, not included here

My Makefile generates the following commands:

avr-gcc -Os -g -std=gnu99 -Wall -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -ffunction-sections -fdata-sections  -DF_CPU=1000000UL   -DBAUD=9600UL -I. -I../../AVR-Programming-Library -mmcu=atmega328p -c -o blinkLED.o blinkLED.c
avr-gcc -Wl,-Map,blinkLED.map  -Wl,--gc-sections  -mmcu=atmega328p blinkLED.o ../../AVR-Programming-Library/USART.o  -o blinkLED.elf
avr-objcopy -j .text -j .data -O ihex blinkLED.elf blinkLED.hex
avrdude -c usbasp -p atmega328p -P /dev/ttyACM0 -U flash:w:blinkLED.hex

Baud is set in the Makefile, in this case to 9600UL. If I set it to 19200UL, everything works the same. In either case, everything behaves as it should, but when I connect to the serial line from my computer (via screen /dev/ttyUSB0), the output is garbled unless I specify BAUD = 1200. (i.e., screen /dev/ttyUSB0 1200).

I've been struggling through a bunch of webpages, the Williams book, and the atmega datasheet, but I'm still not getting this. Is the baud rate fixed for this chip? If not, how do I set it to a rate higher than 1200?

At this point I actually need to set it any higher, but I'd like to understand how this is supposed to work, and what I'm getting wrong.

Update

Note that I have explicitly set BAUD and F_CPU with the avr-gcc flags: -DF_CPU=1000000UL -DBAUD=9600UL. Even if I set these macros in the code (as #define BAUD 9600UL and #define F_CPU 1000000UL as the first lines in the file, I'm still stuck with BAUD of 1200.

Update 2

I have isolated the problem to the setbaud.h file. If I set the baud rate 'by hand', that is, using the following code in place of the call to initUSART(); above, it works as expected – the BAUD is correctly set to 9600. This sets the prescaler to 12 (0b00001100) and enables USE_2X. So my problem must be that I'm using setbaud.h improperly, but I can't see how.

UBRR0H = 0;
UBRR0L = 0b00001100; 

UCSR0A |= (1 << U2X0);

/* Enable USART transmitter/receiver */
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);   /* 8 data bits, 1 stop bit */

Best Answer

As the comments suggest, you have to define BAUD in the setbaud.h file. Here it says you have to define F_CPU before including setbaud.h file. (That's because setbaud.h uses some macros to calculate UBR0 values based on your required baud rate and operating CPU speed)