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:
The code is this:
// ------- Preamble -------- //
#include
#include
#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
#include "USART.h"
#include
#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');
}
Answer
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:
- atmega328p
- working through chapter Chapter 5 (USART) in the book Make AVR Programming (the source of the code sample provided by @secs360)
- 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.
No comments:
Post a Comment