Author Topic: UART 115200 on ATmega328p@16Mz  (Read 13738 times)

0 Members and 1 Guest are viewing this topic.

Offline Edwin NoorlanderTopic starter

  • Contributor
  • Posts: 15
UART 115200 on ATmega328p@16Mz
« on: January 02, 2015, 01:09:42 pm »
Hi,

i have a ATmega328p run at 16Mhz. I now get crap if I have a connection with 115200 BAUD?
This is my code;

Code: [Select]
#define F_CPU 16000000UL

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

#define USART_BAUDRATE 115200 //9600 default

#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

void init(void){
    // Set baud rate
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);
    UBRR0L = (uint8_t)UBRR_VALUE;
    // Set frame format to 8 data bits, no parity, 1 stop bit
    UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
    //enable transmission and reception
    UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
}

void sendByte(uint8_t u8Data){
    //wait while previous byte is completed
    while(!(UCSR0A&(1<<UDRE0))){};
    // Transmit data
    UDR0 = u8Data;
}

uint8_t ReceiveByte(){
    // Wait for byte to be received
    while(!(UCSR0A&(1<<RXC0))){};
    // Return received data
    return UDR0;
}

void write(const char* text){
    for(unsigned int nr = 0; nr < strlen(text); nr++){ // strlen <=> String.h
        sendByte(char(text[nr]));
    }
}

int main (void){
    uint8_t u8TempData;
    //Initialize USART0
    init();
   
    sendByte(0x0C);
   
    write("\nHallo World.\n\n");
   
    while(1){
        // Receive data
        u8TempData = ReceiveByte();
        //Send back to terminal
        sendByte(u8TempData);
    }
}

I think the problem is in this formula
Code: [Select]
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
 

Online Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: UART 115200 on ATmega328p@16Mz
« Reply #1 on: January 02, 2015, 02:02:55 pm »
Did you match your equation with Table 19-9. Examples of UBRRn Settings for Commonly Used Oscillator Frequencies in the datasheet?
 

Offline Edwin NoorlanderTopic starter

  • Contributor
  • Posts: 15
Re: UART 115200 on ATmega328p@16Mz
« Reply #2 on: January 02, 2015, 03:50:21 pm »
 Its a Arduino Nano cloon. http://www.banggood.com/ATmega328P-Arduino-Compatible-Nano-V3-Improved-Version-No-Cable-p-959231.html

Nou I used 8 according to the datasheet. :-+ But nou I get extra 0x00 after sending a byte? :palm:



 

Offline MrAureliusR

  • Supporter
  • ****
  • Posts: 373
  • Country: ca
Re: UART 115200 on ATmega328p@16Mz
« Reply #3 on: January 03, 2015, 09:23:16 am »
If I were you I'd take a closer look at the ATmega datasheet.

Also, if you can't see the frequency on the crystal, measure it with a scope to see what's actually there.
As a test, I would just have it loop sending 0x55 infinitely, that way you can tweak it and clearly see if there's a signal (0x55 looks like a clock signal, 0xAA works as well)

Also, if you actually do the math for the baud rate you selected, the answer is 7.680, so due to rounding there will be significant error in the baud clock, which is probably what the problem is.
I've always found it's better to initialize the UBRR registers with an integer value. Try 104 and then communicate at 9600 baud, see if that corrects it.
« Last Edit: January 03, 2015, 09:26:02 am by MrAureliusR »
--------------------------------------
Canadian hacker
 

Offline Edwin NoorlanderTopic starter

  • Contributor
  • Posts: 15
Re: UART 115200 on ATmega328p@16Mz
« Reply #4 on: January 03, 2015, 10:56:20 am »
 :-+ Thanks I will try.
 

Offline dgtl

  • Regular Contributor
  • *
  • Posts: 183
  • Country: ee
Re: UART 115200 on ATmega328p@16Mz
« Reply #5 on: January 04, 2015, 04:25:50 pm »
The clock is too far off to be reliable.
First, the code in your first post rounds always down at the prescaler division, this causes too large error. Either hard-code the result or fix the formula - add half the divider before dividing, ie #define UBRR_VALUE ((((F_CPU + (USART_BAUDRATE * 8UL)) / (USART_BAUDRATE * 16UL))) - 1). This small off-by-one gets worse as the baud rate is raised and the prescaler is reduced.

The real problem is that the baud rate*16 does not divide to clock rate. Looking at the AVR datasheet, the error at UBRRn=8 is -3.5% and this is too large to work reliably. It depends on the actual UART block used on both sides; better UARTs perform clock recovery and are able to cope with clock rate errors.
In case of simple UARTs:
* you have start bit + 8 data bits + stop bit = 10 bits
* simple UARTs are sampling the receive pin at the middle of the bit: you do not want more than half a bit of clock shift at the end of the tenth bit or you'll miss that
* thus the clock may be off by +-5%.
More advanced UARTs sample multiple times (ie AVR samples 3 times); then the boundaries get narrower. The AVR datasheet specifies the worst case as +-4.5%. Anyway, you must take into account that the other end may be off to the other side and things still get broken. Unless both ends are your own hardware, the rule of thumb is that you should not go over half of that tolerance and leave the other half to the other device. (if both ends are your own HW, then you can use whatever non-standard baud rate that works out well and it is a non-issue anyway). So when going above 2% of error, prepare for trouble.
AVR UART at 115200 baud from 16MHz osc has 3.5% error. That is way too bad. You may have better luck at U2X=1; then the baud rate is only 2.1% off, but the receiver has triple-sampling points set at more wide locations and the receiver is more tolerant to error (but the error is less). The TX is definitely better; RX may be better or worse. If this is a debug UART, tx-only, then this would be a solution.
A better approach would be either:
* Swap the crystal on the board to one that divides better to 115200*16. For example, 18.4320 or 14.7456MHz.
* Use better baud rate. If you do not have too much data and can take the reduction of transfer rate, go to a lower baud rate with less error (see table in DS). If not, go to 250 or 500kbaud, that divide evenly from 16MHz. You may have issues at those rates with some UARTs or USB-serial adapters, some do not go well above 230kbaud.
If the UART is hard-wired to a known chip, check the datasheet of that end as well!
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: UART 115200 on ATmega328p@16Mz
« Reply #6 on: January 06, 2015, 09:09:02 am »
What dgtl said.  115200bps on a 16MHz AVR is ... not good.

"But thousands of arduino users..." I hear you say.  And that works because the serial connection is to another 16MHz AVR, so it has the same error in the same direction;  not right, but "matched."  (And then it's USB, which doesn't care.)
 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6190
  • Country: us
Re: UART 115200 on ATmega328p@16Mz
« Reply #7 on: January 06, 2015, 01:29:32 pm »
What dgtl said.  115200bps on a 16MHz AVR is ... not good.

"But thousands of arduino users..." I hear you say.  And that works because the serial connection is to another 16MHz AVR, so it has the same error in the same direction;  not right, but "matched."  (And then it's USB, which doesn't care.)

USB does care about the rate when you use a FTDI serial to USB chip as commonly done with arduino pro mini (16mhz atmega328p).

It should work and I did it many times.

 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6190
  • Country: us
Re: UART 115200 on ATmega328p@16Mz
« Reply #8 on: January 06, 2015, 04:52:38 pm »
You can find here a serial i/o implementation that works on atmega328p 16Mhz at 115200 bps.

https://github.com/zapta/linbus/blob/master/injector/src_p891_memory/arduino/sio.h

https://github.com/zapta/linbus/blob/master/injector/src_p891_memory/arduino/sio.cpp

It doesn't uses interrupts so you need to call the loop() method from your main loop().

Search for 'sio' in this file to see the usage

https://github.com/zapta/linbus/blob/master/injector/src_p891_memory/arduino/arduino.ino

This should work on both Arduino and plain AVR.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: UART 115200 on ATmega328p@16Mz
« Reply #9 on: January 07, 2015, 06:43:47 am »
Quote
It should work and I did it many times.
Well, like dgtl said, there's 3.5% error.  If the thing you are connecting to is exactly correct, or slightly incorrect in the same direction, it will probably work.
You can get a -2.1% error by using the "double speed mode" (U2X0 in UART_SRA0)
Note that this error is in the opposite direction of the normal calculation; I think you can be almost certain of errors if you have one AVR set one way (-2.1%), and the other set the other way (+3.5%)  (5.6% total error.)
 

Offline dgtl

  • Regular Contributor
  • *
  • Posts: 183
  • Country: ee
Re: UART 115200 on ATmega328p@16Mz
« Reply #10 on: January 07, 2015, 11:42:44 pm »
FTDI states in appnote, that the baud rate error should be below 3%. So running AVR at 16MHz connected to FTDI is outside allowed tolerances on both sides (assuming FTDI is spot on; they have fractional baud rate divisor so the error should be much lower but I did not care to calculate the actual numbers). Even if it seems to work at first, you can not be sure that in the whole temperature range and by using randomly selected chips at production it would still work. Better select another baud rate; why not 500k?
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf