I'm trying to learn how to use the USART interface on an Atmega328p. For now, I just try to send a 'c' character bytes every 500 ms and read it on my computer using a PL2303 USB-serie TTL converter.
Data are transmitted, but I don't get the result I want on the receiving side.
Here is what I have programmed in my microcontroller:
#define BAUDRATE 9600
#define BAUD (((F_CPU / (BAUDRATE * 16UL))) - 1)
void uart_init()
{
UBRR0 = BAUD;
/* 8 data bites, no parity, one stop bit */
UCSR0C |= _BV(UCSZ01) | _BV(UCSZ00);
/* Enable UART input and output */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0);
}
int main()
{
uart_init();
for (;;)
{
while ((UCSR0A & _BV(UDRE0)) == 0);
UDR0 = 'c';
_delay_ms(500);
}
}
I read the data in python using pyserial:
import serial
with serial.Serial(
'/dev/ttyUSB0',
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE) as port:
while True:
print(port.read())
I somehow have the correct result. I have some correct bytes 'c', but about one of two is '\xe3':
b'\xe3'
b'c'
b'\xe3'
b'c'
b'\xe3'
b'\xe3'
b'\xe3'
b'c'
b'c'
b'c'
b'\xe3'
It seems that I have a random high order byte that appears from time to time.
I can't manage to find where is my problem. I check on the scope, and the data sent are always the same. So I think I have something wrong on the receiving side. But can't find what it can be. I have double checked F_CPU, which is at 1Mhz.
-
1\$\begingroup\$ You cant do that combination of F_CPU, baud rate & prescaler. The math just doesn't work. Your actual baud rate (which you'll see if you measure it on a 'scope) is about 10416, not 9600 - so you're often seeing the stop-bit as the last data bit (the MSb). \$\endgroup\$brhans– brhans2017年05月01日 19:44:28 +00:00Commented May 1, 2017 at 19:44
1 Answer 1
There is too much error in your baud-rate clock. (1MHz/9600*16) = 6.51. This gets rounded down to 6 so the frequency error is (6.51/6)-1 = 8.5%.
UART baud rate needs to have better than 5% accuracy to avoid receive errors. Why? The receiver starts timing when the Start bit goes low, then samples the signal in the middle of each bit. If the frequency is wrong then the sampling point will progressively slip relative to the middle of each bit. After 9 bits at 5% error the sample point is just within the correct bit period, but at 8.5% error it is in the previous bit (so the Stop bit is detected as bit 7).
The UART at the sending end also has some timing error. If the errors are in the same direction at both ends then they will partially cancel and you might get away with>5% error. But if they are in opposite directions then it will be worse. So in order to assure that any two random systems can communicate without error their baud rates must be accurate to better than +-2.5%. Other effects such as timing and sampling jitter, noise and uneven slew rates may reduce the margin even more.
For an ATmega328 running at 1MHz you can set the U2X0
bit in UCSR0A
and make UBRR0
= 12, then you should get 9615.38 Baud which is only 0.2% error (plus any error in system clock frequency).