Trying to understand USART Baud

Go To Last Post
31 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I D/l an example simple c code which included this:

#define FOSC 8000000UL //system clock speed
#define	BAUD 9600UL
#define MYUBRR FOSC/16/(BAUD-1)

Ok, so my clock is 16M so I make that change. I look at the datasheet for my chip under the heading "Examples of UBRR Settings for Commonly Used Oscillator Frequencies" is a chart. I suppose lower error is better, right? So if the chart shows 0.0% error at 250k, 0.5M and 1M then my supposition is that I should change BAUD to one of those figures. Is that right?

But mostly I don't get the MYUBRR calculation. Doing the math (with 8M) results in 52 and change. The chart says it should be 51. Why not just look up what the chart says and write that in?

Why would I NOT want to be operating at as fast a speed as possible?

Sorry for dumb Qs as always :) thanks.

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Why would I NOT want to be operating at as fast a speed as possible?
I assume there will something connected to the other end of UART cable?
That something need to run at the same baudrate or transfer will not work.
9600baud is a common rate for devices to communicate at.
Especially when first connected.
You can choose any rate as long as it's agreed or the receiver have some clever software to auto detect baudrate.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I believe the equation would be ((FOSC/16)/ BAUD)-1).
Only a few crystal frequencies divide down to exact multiples of the standard baud rates. If you are not using one of these crystals, you want to select a baud that has 1% or less difference between the calculated baud and the desired baud.
For a 16MHz clock and 9600, ((FOSC/16)/ BAUD) gives 104.166. Multiply 104 by 9600 to get 998400. Divide 998400 by 1000000 (Fosc/16) to get 0.9984. The serial port works when the ratio between real and desired baud is 0.985 or better, about 1.5% error. For 9600 baud with a 16MHz clock, use 103 as the low baud register value and 0 as the high baud reg value. 103=((Fosc/16)/baud)-1.
Generally it's better to operate the serial port as fast as it can transfer data between two computers reliably. 300 baud was a good limit for printers in the 1950s and 1960s. For modems (in the pre-WiFI era of on-line access over telephones) 14000 baud was good. Note that with modems, baud rate is not the same as bit rate. Baud rate is the number of time that the signal is changing per second. By changing phase and volume, modems can consolidate four bits into one line state, so a 14000 baud rate gives a 56000 bits_per_second transmit rate. But generally, baud is the same as bits-second.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

s_mack wrote:
#define MYUBRR FOSC/16/(BAUD-1)
This should be:
#define MYUBRR (FOSC/(16*BAUD)-1)

Quote:
Why not just look up what the chart says and write that in?
What if you decide to change FOSC? What I often do is have some sort of compile time assert:
  compileTimeAssert(MYUBRR == 51);

That way, I can easily see what the value is, and that it matches the datasheet chart, and I get an error if I change FOSC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You ask: why not send as fast as possible?

One answer is error rate. Errors tend to go up at faster speeds. Many RS232 interface chips also have maximum baud rate limits that are not all that fast.

If your system does not NEED to be particularly fast, then it can do all sorts of other stuff while characters are being received or transmitted. This way, with almost no effort, it almost becomes "multi-tasking".

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, thanks. To clarify:

Both ends of this communication are AVRs running at 16Mhz. The code doesn't need to be flexible... both ends will always be only these two chips (it is a purpose-built application).

If error is 0.0% at 1M then wouldn't the faster error rate still be 0.0%?

The sample code I'm referring to is here: https://www.avrfreaks.net/index.p...

so the author has made the math error, not me. But thanks for that correction as that makes a lot more sense with -1 outside the brackets :)

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another typo?

Compiling, I get this warning:

Quote:
warning: 'USART_RXC_vect' appears to be a misspelled signal handler

Is the "C" accidental?

edit: you know what... ignore that Q. That's something I'm sure I can figure out on my own. Sorry.

edit2: in case anyone comes looking... USART0_RX_vect

Last Edited: Thu. Apr 26, 2012 - 08:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your 'sample code' is cr*p. There are several serious mistakes.

Use examples from respected sources. e.g. Atmel app notes, or avr-gcc examples, or anything.

Yes. If your AVRs are hard-wired together, you can run them both at 1M baud with no problems.

OTOH, your fingers may struggle with typing that fast.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

that "crap" code was the first example I could actually understand! lol :)

I've been struggling finding USART examples that really dumb it down to the basics so I can build from there.

Just about every example I could find was also related to AVR to PC communication or AVR to peripheral communication but that was one of the only ones that was AVR to AVR.

When learning (at least for me), its REALLY helpful to start with examples that are as close to my situation as possible rather than being as flexible/portable as possible.

Last Edited: Thu. Apr 26, 2012 - 08:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If error is 0.0% at 1M then wouldn't the faster error rate still be 0.0%?
The error described in datasheet is not rate of error (how often a byte will be missed at receiving end).
The error in datasheet describe how far from the desired frequency the actual frequency will be for different crystals (measured in %).
Like Jim says error rate tend to increase with higher data rates.
So if you don't specifically need to run as fast as possible to do what you want, don't.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks... my logic was as sho:

Total error = periodic error x period x rate.

So 9600 error = 0.2% x period x slow rate = small error
38.4k error = 0.2% x period x faster rate = larger error
230.4k error = 8.5% x period x even faster rate = huge error... but,
1M error = 0.0% x period x stupd fast rate = NO ERROR

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. This code should work fine. The "usart.c" file is fine. I have removed all the rubbish involving interrupts.

#include 
#include 
#include "lcd.h"

#define FOSC 8000000UL //system clock speed
#define	BAUD 9600UL
#define MYUBRR (FOSC/16/BAUD - 1)

void USART_init(unsigned int ubrr);
void USART_Transmit(unsigned char data);
unsigned char USART_Receive(void);

unsigned char data;

int main(void)
{
	LCDinit();
	USART_init(MYUBRR);

	LCDclr();

	while(1)
	{
		data = USART_Receive();
		LCDsendChar(data);
		_delay_ms(10);
	}
}

I hope that this looks simple enough for you.

Regarding baud rates. All that matters is that both sides agree. Normally this means 9600, 19200, 38400, 57600 115200, ... baud.
If you want to use 123456 baud, this will be fine if the other side uses 123456 baud too.

In fact the error % shown in the data sheet only applies to those specific baud rates. If both sides are +7.5% of the correct 115200 baud, they have 0% error.

With short hard wired connections, a high baud rate will work fine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most of the apps I've built run at 9600.
I.e. many cellphones communicate at that rate as default.
This does not mean that 0.2% of the data will be lost.
You can safely get away with an error of 1%, still no data lost.
The receiving end sample data several times per bit to decide if the bit is a "0" or a "1".
At higher rates the risk of sampling noise, either as reflection in wires or external noise increase. The length and quality of cabling become important.
That is the noise spike is long enough to make the receiver mistake a "0" for a "1"

If the error in baudrate is close to accepted margin and you send large packets of data the receiver eventually get out of phase with the transmitter (Frame error) and you start getting gibberish bytes.
If you have both a high baudrate error and a fast speed your chances of communication errors increase.

If as you say both sides will be AVR's with same crystal then you can pick any non-standard baudrate like 1.02M.
Often UART communication occur i.e. btw an AVR and a PC or a phone or any other device. This is when the low error in baudrate is important because industry have decided to use standard baud rates such as 9600, 19200 etc. to allow for devices to communicate without a dedicated clock line.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks David!

As for "wire length"... it couldn't get any shorter honestly. Less than 2cm of trace.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A compelling reason to use a 'known' baud rate is debugging. Being able to 'sniff' comms with your PC can be very helpful.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to note that if you do run the link at 1MHz then even with a 16MHz CPU unless you think you think you can keep stuffing a byte into UDR every 16 cycles you are going to have long periods of inactivity on the wires so don't think you can realistically operate at 1MHz anyway. I guess at 256K you have 64 cycles per byte but to be honest most programmers (given a choice) only ever use two baud rates. 9600 when they want to talk to a "slow" device or 115200 when the data transfer needs to be fast. Part of this stems from the fact that even if the link is between two micros, when you are developing the code the temptation will be to use a PC (and your eyes/brain) as one side of the link and PCs can easily do 9600 or 115200 but you may have more issues trying to find a comm program that can do 250K/500K/1M.

EDIT: oops - my last point just duplicates what Kartman said.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys.

I really don't care about the transfer speed except that I think I do want it to be significantly above 9600 for previously-unmentioned reasons*. Again, I was mostly focusing on the datasheet's 0.0% error at the higher speeds but see now that isn't so important.

* the reason I need > 9600 comes down to the reason I'm building this board. One of the two uC's is an existing device that has no available i/o but has a user base with an expanding need for more functionality. My new board will bolt on directly to the existing one with direct connection to the usart pins. The new uC will offer more gpio and acts as a bridge to the existing uC. It will also offload some of the number crunching and it will be accepting UART communication from a sensor at 9600 baud and will be passing that on to the existing uC. So it seems to me that it would need significant more total speed available if its going to be passing 9600 baud data along with other data as well.

Do I need 1M? No, of course not. But if available and stable, then why not? But I think with the advice given above, I'll target 115200 as that should be more than sufficient.

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I seem to remember that you have F_CPU = 8MHz.
Note that 115200 baud is not achievable.

If you can guarantee that every board combination is going to be identical, you can both run at 125000 baud. (e.g. the nearest to 115200)

If you ever connect to a foreign PC or MCU, your comms will fail.

I would just stick to what is 'normal' and achievable.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

16MHz...but I'll figure it out. thx

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your two AVRs are only inches away, then you could put data on an output port pin on the transmitter and read it on an input pin on the other. Then use another set of Input/output pins to signal that a bit is available.
Or use an entire port to send eight bits at a time.

Tx_AVR:
Puts eight bits on port B.
Set port A.0 high.
Read port A.1 over until it goes high.
Clear port A.0.
Put next eight bits on port B.
repeat.

Rx_AVR:
read port A.0 until it goes high. Clear port A.1
read eight bits on port B.
set port A.1 high.

This is parallel port interfacing with handshaking. When all the details are worked out, the data can be transferred between two AVRs at millions of bytes per second. Way faster than any serial port.
The serial port using RS232 is designed to send data reliably between for very different computer systems located as much as a 100 feet apart. It works for two AVRs inches apart, but it's not optimal. It is easy to understand and reliable. The SPI interface that is built into most AVRs would work fast and reliable also. So would the TwoWire Interface (or I2C as more commonly known) that is found in most mega AVRs.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

s_mack wrote:
One of the two uC's is an existing device that has no available i/o

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
I have removed all the rubbish involving interrupts.

Revisiting this... why were interrupts rubbish? As it stands, I just have a WDT triggering as the loop waits for USART data. There's not always going to be data, so isn't an interrupt a good way to do it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are learning about microcontrollers.

It is easier to start with straightforward wide-awake polling. I would get experience with this first.

After all, you did say that most examples were too complex.

When you are happy with getchar(), putchar(), printf(), ... and your polled USART, you can progress to USART interrupts. The important lesson is that your high level application code is completely unchanged. e.g. you call printf() or gets() in the same way.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Back in the last Millenium where a big mainframe computer could almost execute a million instructions in a second, the programmers had to be clever to get that big ol slow booger to stop what he was doing and echo back the character you just typed without too much waiting around while handling dozens of students editing on modems at the same time. An interrupt can hit right in between any two assembler instructions. If you are unlucky, it will hit right when you get the first byte of a two byte register load done. That's why programming with interrupts has a few more lines in the check list of stuff to watch out for. A microcontroller program that reads a serial port and does something can poll the serial port looking for characters coming in at 9600 bits per second (about a ms per char) many times in a millisecond. Probably dont need interrupts yet. But if the baud rate is 115200, the chars arrive every 83 usec, so a receive interrupt works great... interrupt handler grabs the char, stores it in the receive array, and returns to what it was doing. Thus my informal rule for using interrupts: Use an interrupt if the input arrives faster than the program is looping and checking for input.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I must be missing something with the polling. Because when I use the code posted earlier, I just get WDT resets happening if there's no communication happening at the moment. That was throwing me off.

In the usart.c file is this code:

unsigned char USART_Receive(void)
{
	//wait for data to be received
	while(!(UCSRA & (1<<RXC)));
	
	//get and return received data from buffer
	return UDR;
}

Doesn't that while loop just hold up EVERYTHING on the micro if there's no data coming in? That's my issue.

But I get what you're saying. One step at a time. :)

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Gary Killdall that wrote CPM to look like DEC RSTS invented the kbhit() function that returns a boolean relecting the state of the RXC bit. Just dont call getchar() unless kbhit() returns a 1 and it wont hang up waiting. Its the first line of the getchar function basically.

Imagecraft compiler user

Last Edited: Sun. Apr 29, 2012 - 08:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That while() loop is exactly the latch-up that a Watchdog is designed to catch.

Forget about dogs and interrupts until you can getchar() and putchar() with confidence.

Yes. You have to kick the dog inside every while() that can potentially hang.
There are other techniques to save you. e.g. timeouts.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   while(!(UCSRA & (1<<RXC))) {
     wdt_reset();
   }

Would prevent the dog biting but it kind of defeats the purpose of using the dog in the first place. The idea is that it stops things that seem to be "taking a long time". If you actually plan to do something that IS going to take a long time then either kick the dog or kill it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lol... that's no good :) That's ignoring the problem, not solving it!

I think I wasn't clear. There must be some mechanism for not being stuck inside a while loop if there's no UART data to be had. I'm thinking interrupts is the key, but you guys don't seem to like them. But then how? What if the transmitter isn't sending anything at the moment? The point of "polling" is to periodically check if something's happening.

Sam needs to call the office, so sam phones. No answer. Sam hangs up and gets some work done. A few minutes later, Sam tries again (repeat).

That's a lot better (for Sam and her boss) than:

Sam needs to call the office, so sam phones. No answer. Sam waits there looking like an idiot with a receiver up to her ear for an hour and a half getting no work done today. Sucks for Sam... she got fired.

Or bit by a watch dog. Either way, not so great.

- Steven

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I think I wasn't clear. There must be some mechanism for not being stuck inside a while loop if there's no UART data to be had

A timeout would be trivial:

uint8_t timeout = 1000;

...

   while(!(UCSRA & (1<<RXC)) && timeout) {
     _delay_ms(1);
     timeout--;
   }

Either use a timer or a software delay loop as above - do not try to use the watchdog for this!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You dudes dont like subtle hints. Here is how its been done since the 80s.

unsigned char kbhit(void){
//return nonzero if char waiting  polled version
unsigned char b;

  b=0;
  if(UCSR0A & (1<<RXC0)) b=1;
  return b;
}
.
.
.
//Usage:
  if(kbhit()){
    c=getchar();
    //do something
  }
  //else get on with the other schtuff

Imagecraft compiler user