## n = (Fosc / (16 * BAUD)) - 1: May Not Always Be Appropriate...

9 posts / 0 new
Author
Message

While working through my Arduino "Brain-Fart" dilemma:

https://www.avrfreaks.net/forum/most-epic-brain-fart-2014

I came to a conclusion...

I have never been a proponent of the n = (Fosc / (16 * BAUD)) - 1 equation.  I've always gone to the data sheet and looked up the appropriate value needed to derive the desired BAUD rate for a given crystal frequency and defined what the BAUD rate is with the following:

// USART0 = 115.2K BAUD @ 18.432MHz
#define BAUD_115_2K 9

I did, at one time, use the n = (Fosc / (16 * BAUD)) - 1 equation to prove to myself that this equation worked.

Well, while in the fog of my "Brain-Fart", I decided to see if I could employ the use the BAUD rate equation to verify what the BAUD should be and, here is what I discovered.

For any of the "Magic" frequencies: 14.7456MHz, 18.432MHz, etc., the BAUD rate calculation resolves to numbers that are exact whole numbers - meaning, there is no fractional part.

But... for crystal frequencies that don't resolve to whole number results, such as 1.000MHz, 4.000MHz, 8.000MHz, 12.000MHz, 16.000MHz, 20.000MHz, the formula doesn't seem to be the most appropriate approach and, here is why:

For a, say, 16.000MHz crystal, the resulting BAUD constant when calculating for a BAUD rate of 115.2K BAUD works out to:

n = (Fosc / (16 * BAUD)) - 1 = n = (16.000MHz / (16 * 115200 BAUD)) - 1 = 8.680555... - 1 = 7.680555...

I don't know how it is for GCCAVR, WinAVR or some of the other AVR C compilers but, rather than rounding 7.680555... up to 8 when moving from a float to UNSIGNED INT, ImageCraft's ICCAVR C compiler truncates the result of the equation to the value of 7.  The issue is that, according to the data sheet BAUD charts, deriving a BAUD rate of 115.2K BAUD when using a crystal frequency of 16.000MHz and a BAUD constant of 8 inherently produces an error of -3.5%.

For a BAUD constant of 7 the BAUD rate is:

BAUD = Fosc / (16 * (UBRR + 1)) = 16000000 / (16 * (7 + 1)) =  125000 BAUD >>> from the datasheet <<<

% error = 100 - ((115200 / 125000) * 100) = 100 - (0.92 * 100) = 100 - 92 = +8% error.

While I have only performed the error resulting due to fractional truncation for one BAUD rate, I suspect the error will be proportionally as bad, across all of the BAUD settings, using the much touted n = (Fosc / (16 * BAUD)) - 1 to derive BAUD rates with non BAUD friendly crystal frequencies.

I think I'll keep using hand fed BAUD constants presented in the data sheet BAUD charts.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Carl--Much ado about nothing.  Yes, in the case of your truncation example, rounding up would be a bit better.

However, both resulting numbers are "bad" and outside the band of reliable serial comms.

Like driving off the shoulder 3.5 meters on the right and 8.5 meters on the left.  Neither is good.

-- Many of us use "Wizards" that help with this type of thing when building an app.

-- Many of use use avrcalc and/or kavrcalc to check settings.

-- Many of us >>only<< use magic frequency crystals in our USART AVR8 apps.

-- When the above "only" is violated, then indeed the datasheet chart and/or tools mentioned above are consulted.

(Somewhere in that equation there had better be a cast to UL ...)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Add rounding (the (8*BAUD) being 1/2 of the divisor):

n = ((Fosc + (8 * BAUD)) / (16 * BAUD)) - 1

microcarl wrote:

I think I'll keep using hand fed BAUD constants presented in the data sheet BAUD charts.

I agree, and I usually document the actual Baud, and in many cases check that against the USB-UART device baud choices too.

This pastes into CCalc

// FOsc=16M; Baud=115200; BaudDiv = round(FOsc/(16*Baud))   BaudDiv = 9
// TrueBaud = FOsc/(16*BaudDiv)      TrueBaud = 111111.1111
// BaudErr = 1-Baud/TrueBaud         BaudErr = -0.0368
// and this for USB Baud
// USB_Baud = 12M/(round(12M/Baud))  USB_Baud = 115384.6154
// or
// USB_Baud = 12M/(round(12M/TrueBaud))  USB_Baud = 111111.1111

ie a design can  set 111.111k Baud @ uC, and set that on most USB-Bridge parts, for lowest errors.

We have seen people spend weeks trying to write the "best" set of defines to calculate the proper UBRR value taking into account rounding, deciding when to use U2X, calculating the error, producing warnings when no appropriate value is found, etc., all to avoid the 10 seconds it takes to calculate the value by hand. I never saw the point. In the time it took to create the defines you could have done the hand calc a thousand time.

Regards,
Steve A.

The Board helps those that help themselves.

Why write macros for anything, then?

The point of any macros is that once written, you can deploy them in (ideally) any circumstances, regardless of considerations like F_CPU.

That's what the macros in <util/setbaud.h> do, which btw handle rounding neatly.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

http://www.avrcalc.com/

or one of its cousins.

Last Edited: Wed. Dec 24, 2014 - 06:21 AM

Why write macros for anything, then?

Generally I don't, especially for something as trivial as calculating UBRR.

Why? It is two divides to get the value, one more divide if you want the amount of error.

Regards,
Steve A.

The Board helps those that help themselves.

Koshchi wrote:

Why write macros for anything, then?

Generally I don't, especially for something as trivial as calculating UBRR.

Why? It is two divides to get the value, one more divide if you want the amount of error.

Obviously, for me, working two divide operations isn't a problem.

But even with that, it's just so much easier to look at the BAUD tables in the data sheet and get the BAUD constant and the percent error, as well.

To me, the n = ((Fosc / 16) / BAUD) - 1 is better used as a tool, to discover if some other frequency other than what is listed in the BAUD charts will have little enough error to work with.  For instance, will Fosc = 4.096MHz work for a given BAUD and, what error will you be working with?

Other than that, it's the BAUD charts...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Wed. Dec 24, 2014 - 09:06 PM