4
\$\begingroup\$

I am using a FTDI cable and connected it with my mac. I can connect successfully with my serial terminal on my mac via the cable and can input text on my keyboard to be transmitted to the AVR. When the program starts I expect a "Hello World" message to appear on my serial terminal. But instead I receive this output on the screen:

enter image description here The terminal settings are enter image description here

enter image description here enter image description here

enter image description here

The code is this:

// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include "pinDefines.h"
#include "USART.h"

int main(void) {
  char serialCharacter;

  // -------- Inits --------- //
  LED_DDR = 0xff;                            /* set up LEDs for output */
  initUSART();
  printString("Hello World!\r\n");                          /* to test */

  // ------ Event loop ------ //
  while (1) {

    serialCharacter = receiveByte();
    transmitByte(serialCharacter);
    LED_PORT = serialCharacter;
                           /* display ascii/numeric value of character */

  }                                                  /* End event loop */
  return 0;
}

The USART.c file contains:

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

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 */
}

void transmitByte(uint8_t data) {
                                     /* Wait for empty transmit buffer */
  loop_until_bit_is_set(UCSR0A, UDRE0);
  UDR0 = data;                                            /* send data */
}

uint8_t receiveByte(void) {
  loop_until_bit_is_set(UCSR0A, RXC0);       /* Wait for incoming data */
  return UDR0;                                /* return register value */
}

                       /* Here are a bunch of useful printing commands */

void printString(const char myString[]) {
  uint8_t i = 0;
  while (myString[i]) {
    transmitByte(myString[i]);
    i++;
  }
}

void readString(char myString[], uint8_t maxLength) {
  char response;
  uint8_t i;
  i = 0;
  while (i < (maxLength - 1)) {                   /* prevent over-runs */
    response = receiveByte();
    transmitByte(response);                                    /* echo */
    if (response == '\r') {                     /* enter marks the end */
      break;
    }
    else {
      myString[i] = response;                       /* add in a letter */
      i++;
    }
  }
  myString[i] = 0;                          /* terminal NULL character */
}

void printByte(uint8_t byte) {
              /* Converts a byte to a string of decimal text, sends it */
  transmitByte('0' + (byte / 100));                        /* Hundreds */
  transmitByte('0' + ((byte / 10) % 10));                      /* Tens */
  transmitByte('0' + (byte % 10));                             /* Ones */
}

void printWord(uint16_t word) {
  transmitByte('0' + (word / 10000));                 /* Ten-thousands */
  transmitByte('0' + ((word / 1000) % 10));               /* Thousands */
  transmitByte('0' + ((word / 100) % 10));                 /* Hundreds */
  transmitByte('0' + ((word / 10) % 10));                      /* Tens */
  transmitByte('0' + (word % 10));                             /* Ones */
}

void printBinaryByte(uint8_t byte) {
                       /* Prints out a byte as a series of 1's and 0's */
  uint8_t bit;
  for (bit = 7; bit < 255; bit--) {
    if (bit_is_set(byte, bit))
      transmitByte('1');
    else
      transmitByte('0');
  }
}

char nibbleToHexCharacter(uint8_t nibble) {
                                   /* Converts 4 bits into hexadecimal */
  if (nibble < 10) {
    return ('0' + nibble);
  }
  else {
    return ('A' + nibble - 10);
  }
}

void printHexByte(uint8_t byte) {
                        /* Prints a byte as its hexadecimal equivalent */
  uint8_t nibble;
  nibble = (byte & 0b11110000) >> 4;
  transmitByte(nibbleToHexCharacter(nibble));
  nibble = byte & 0b00001111;
  transmitByte(nibbleToHexCharacter(nibble));
}

uint8_t getNumber(void) {
  // Gets a numerical 0-255 from the serial port.
  // Converts from string to number.
  char hundreds = '0';
  char tens = '0';
  char ones = '0';
  char thisChar = '0';
  do {                                                   /* shift over */
    hundreds = tens;
    tens = ones;
    ones = thisChar;
    thisChar = receiveByte();                   /* get a new character */
    transmitByte(thisChar);                                    /* echo */
  } while (thisChar != '\r');                     /* until type return */
  return (100 * (hundreds - '0') + 10 * (tens - '0') + ones - '0');
}
\$\endgroup\$
20
  • 3
    \$\begingroup\$ Check you have matching Baud rates at both ends, and that the AVR clock frequency is as expected. \$\endgroup\$ Commented Nov 16, 2016 at 11:32
  • 1
    \$\begingroup\$ perhaps a baud rate is not equal? \$\endgroup\$ Commented Nov 16, 2016 at 11:32
  • \$\begingroup\$ The baud rate in the serial terminal is set to 9600. I thought 9600 is set in code as well? \$\endgroup\$
    – sesc360
    Commented Nov 16, 2016 at 11:35
  • 1
    \$\begingroup\$ Check the values of UBBRH_VALUE and UBBRL_VALUE variables and make sure they have correct values for 9600baud. Also, crystal frequency is very important on UART transmission. \$\endgroup\$ Commented Nov 16, 2016 at 11:39
  • 1
    \$\begingroup\$ Here is an example of setting BAUD of AVR: pastebin.com/L05SYB2C. Still recommended to read relevant part of datasheet and comments in the header to understand it correctly. \$\endgroup\$ Commented Nov 16, 2016 at 11:45

3 Answers 3

5
\$\begingroup\$

I ran into this same problem, and the answer provided by @bence_kaulics is what got me through it, with one additional point:

I am in the same situation as @secs360:

  1. atmega328p
  2. working through chapter Chapter 5 (USART) in the book Make AVR Programming (the source of the code sample provided by @secs360)
  3. I can program my chip (blink tests work), but the serial feedback loop responds with incorrect characters. Various combinations of BAUD settings in the code, or in the serial terminal, fail to resolve the issue.

Steps to fix:

First, confirm I have set the clock correctly:

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

Checking these against a fuse calculator, I see they are the default values: the MCU is set to use the internal RC oscillator at 1 MHz.

This means I should set the CPU speed (in the makefile for the chapter exercises in this case):

F_CPU = 1000000UL

I can also set the BAUD value in the same location. 9600 should work:

BAUD  = 9600UL

So far so good. However, the book uses a different formula for calculating the UBRRn register values. @bence_kaulics provides the formula, from the datasheet.

(Perhaps the difference is due to the book being written for atmega168 chips? I don't know. But whatever the source, we need to use the correct value here.)

There is one more piece of information! If we want to use 9600 BAUD, we will have an error of -7% with standard transmission speed, according to the datasheet. If we double the transmission speed, our error drops to 0.2%. To do this, we don't use the formula provided by @bence_kaulics, but instead use ((F_CPU)/(BAUD*8UL)-1), and set the U2X0 bit.

I did that by modifying the initUSART function in the USART.c file:

void initUSART(void) {                                /* requires BAUD */
    #define BAUDRATE ((F_CPU)/(BAUD*8UL)-1) // set baud rate value for UBRR
    UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
    UBRR0L = BAUDRATE;       // set baud rate

    UCSR0A |= (1 << U2X0);   // double transmission speed

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

The original version from the book uses logic in the setbaud.h file to determine whether or not to double the transmission speed. I don't understand all of it, and so I'm not sure if the problem is the formula used for the BAUDRATE, or USE_2X, or both. Whatever it is, the code above has finally got my atmega328p speaking properly over the serial interface.

\$\endgroup\$
1
  • \$\begingroup\$ I know this is old but thank you Tyler! I have the exact same book and I was struggling with this. \$\endgroup\$ Commented Nov 10, 2018 at 22:12
6
\$\begingroup\$

First step should be checking your clock configuration. You said, there is no external crystal, and you code definitely runs now so the CKSEL[3:0] fuse bits are certainly 0010, internal RC oscillator is selected. Tables taken from datasheet.

![enter image description here

Now, check the CKDIV8 fuse bit as well, to know if the internal RC oscillator is divided by 8 or not.

enter image description here

It is programmed by default, so if you have never ever touched the fuse bits yet, your MCU probably runs on 1 MHz.

If the CKDIV8 is 0, define your CPU frequency as follows:

#define  F_CPU 1000000UL

and if it is 1, then:

#define  F_CPU 8000000UL

Next step is calculating the value of the BAUD rate register, which can be done with the following macros.

#define BAUD 9600                        // desired baud
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1) // set baud rate value for UBRR

This will give you the correct value in BAUDRATE. Equation is given in the datasheet.

![enter image description here

In your UART init function pass this to the UBBRH and UBBRL registers.

UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
UBRR0L = BAUDRATE;       // set baud rate
\$\endgroup\$
3
  • \$\begingroup\$ I just ordered another FTDI cable (not from ebay this time but from Adafruit). lets see. \$\endgroup\$
    – sesc360
    Commented Nov 16, 2016 at 16:17
  • 2
    \$\begingroup\$ @sec360 could be, but I doubt the FTDI cable is at fault, If you plan on doing some more projects I would invest in a scope/logic analyzer. You can get cheap one on ebay but, I would recommend one from saleae.com , I have one and it has helped me loads in debugging. \$\endgroup\$ Commented Nov 16, 2016 at 16:40
  • \$\begingroup\$ I have saleae copy from ebay, served me well so far. \$\endgroup\$ Commented Nov 16, 2016 at 16:41
4
\$\begingroup\$

The solution will be anyone from the following

  1. Check the AVR board Clock Frequency, it should be the value which is taken by you to calculate the baud rate
  2. Check the baud rate register(UBRR) values, it should be 129(in decimal) for 9600 baud rate at 20MHz Clock Frequency. As well as check with formula available in datasheet
  3. Check the RS232 cable (Less chance ) and the converter (MAX 232 or any else)
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.