Asking for the smart algorithm for selecting best UBRR value

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

Hi, guys,

I'm trying to write a function for automatic USART initialization with parameter input of desired baud rate, F_CPU and the following options:

#define USART_OPT_ONE_STOP_BIT       				0
#define USART_OPT_TWO_STOP_BITS      				0x0008
#define USART_OPT_NO_PARITY          				0
#define USART_OPT_EVEN_PARITY        				0x0020
#define USART_OPT_ODD_PARITY         				0x0030
#define USART_OPT_5_BITS_PER_CHAR    				0
#define USART_OPT_6_BITS_PER_CHAR    				0x0002
#define USART_OPT_7_BITS_PER_CHAR    				0x0004
#define USART_OPT_8_BITS_PER_CHAR    				0x0006
#define USART_OPT_9_BITS_PER_CHAR    				0x0406
#define USART_OPT_ASYNC       						0
#define USART_OPT_SYNC      						0x0040
#define USART_OPT_SLAVE       						0
#define USART_OPT_MASTER      						0x0100
#define USART_OPT_POLARITY_0       					0
#define USART_OPT_POLARITY_1     					0x0001

Coz what I'm not sure about is that U2X=0 has higher Maximum Receiver Baud Rate Error tolerance than U2X=1,
so if a baud_rate value result in the same error rate for both normal speed and double speed, then I should choose normal speed. But if such ideal situation is not the case, then I need two weighting factors.

Any one have ideas?

Cheng

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

My solution to the issue:

http://savannah.nongnu.org/file/...

This is basically just hard coding the only usable (less than 2% error) values out of the datasheet tables for all the popular crystal frequencies

Cliff

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

hehe, ok, that's a little bit big in size, thanks any way.

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

OK, I'll bite--what part of setting a single pre-processor constant is "a little too big in size"?

Lee

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.

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

what I mean is I want to build a function to calculate the most suitable UBRR value, not mapping the most common baud rate into the code.

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

Yeah but (as someone is famous for saying!), the fact is that any algorithmic routine can't really know which are the ones that fall inside or outside the 2% error window without resorting to floating point maths.

As long as I made no typos in copying the tables (and I'm certain I didn't) then the lookup table approach (which is what I do) is the simplest solution to this.

Cliff

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

This is the marco I previously used for this purpose:

/*******************************************************************************************************
 * SERIAL PORT (USART1) 
 */
// Macro for initialize
// the parameter baudrate should be unsigned long int and < F_CPU/8
// Note: when using double speed mode, the USART Rx need more precise timing
#define INIT_ASYNCH_U2X_USART1(baudrate, options) \
    do { \
		float UBRR_temp; \
		\
		\
		UBRR_temp = ((float)F_CPU / ((unsigned long int)baudrate << 3)) - 1; \
		if (UBRR_temp - (unsigned short int)UBRR_temp > 0.5) \
		{ \
			UBRR1H = ((unsigned short int)UBRR_temp + 1) >> 8; \
			UBRR1L = (unsigned short int)UBRR_temp + 1; \
		} \
		else \
		{ \
			UBRR1H = (unsigned short int)UBRR_temp >> 8; \
			UBRR1L = (unsigned short int)UBRR_temp; \
		} \
        UCSR1C = options; \
        if (options > 0xFF) /* if UART_OPT_9_BITS_PER_CHAR is selected */ \
		{ \
            UCSR1B |= _BV(UCSZ12); \
        } \
		else \
		{ \
            UCSR1B &= ~(_BV(UCSZ12)); \
        } \
        UCSR1A |= _BV(U2X1); \
    } while (0)

The PC does the float point and the result is just a UBRR value to be write into by Microprocessor.

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

I was under teh impression you wanted this evaluated run-time, and not in the compiler. In eather case floating point math is required... the only difference is which processor is performing the floating point calculation.

Using your latest post as a guide, in that you want to have the C pre-peocessor do the work. Then you simply need to calculate for 'baudrate' the UBRR values for both cases of U2X, as well as each of those values rounded up & down from the float to an integer. Then reverse the calculation using the 4 possible UBRR values, to find the actual baud rates you will be getting. pick whichever one is closest to your ideal UBRR. This won't necessarily get you into the +/- 2% figure, but it will give you the best possible value for 'baudrate'. You're half way there already with your code. You just need to repeat for the U2X=1 possibility now.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

I used the preprocessor/optimizer math approach successfully in several projects. One of the optimization levels -O1, -O2or -Os is required to avoid the floating point math in the generated code. Please find the source attached.

Best regards,
heinrichs.hj

Attachment(s): 

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

thanks a lot, guys

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

I guess I must be the only one with a pathological hatred of the use of floating point small 8 bit controllers then? I can't see any justification for blowing 2K of your code space for calculating a known at compile time fixed integer value. Heck I'd rather just read the table in the datasheet and code:

UBRR=51; //9600 on 8MHz

than resort to dragging in FP libraries. YMMV.

Cliff

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

I checked your code, the method I'm looking for is also been asked in your code

    if( error >= 0.02 && error_u2x >= 0.02 )  {
        /*
         * any idea how to create some kind
         * of compile time warning here is welcome
         */
    }

anyone got ideas?

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

No, Cliff, you didn't get it. What I want is using C compiler (PC) to do the float calculation and just use the result in final microprocessor code.

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

So you are suggesting using the preprocessor to do your FP maths?

BTW, did you see this dialog:

http://savannah.nongnu.org/patch...

and specifically the solution suggested by Carlos Lamas?

Cliff

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

ok, I just checked it. I'm wondering if function can do the same with #error indication.

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

Yes, just replace #warning with #error for the ""Baud rate achieved ..." messages.

Refering to your other post: The #if !(BAUD) just checks that BAUD is a legal constant.

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

darthvader wrote:
I checked your code, the method I'm looking for is also been asked in your code

    if( error >= 0.02 && error_u2x >= 0.02 )  {
        /*
         * any idea how to create some kind
         * of compile time warning here is welcome
         */
    }

anyone got ideas?

You can't do a compile time check based on a run time value.

Quote:
I can't see any justification for blowing 2K of your code space for calculating a known at compile time fixed integer value. Heck I'd rather just read the table in the datasheet and code:

I agree. I can't see the justification of spending hours trying to find the perfect macro to compute the UBRR in order to avoid a 10 second calculation (30 seconds if you want to see the exact amount of error).

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
I agree. I can't see the justification of spending hours trying to find the perfect macro to compute the UBRR in order to avoid a 10 second calculation (30 seconds if you want to see the exact amount of error).

I agree, but I decreased that 10 or 30 second calculation time by writing a small, open-source library that not only calculates divisors and error percentages for UARTs, but also timer prescalars, compare values, and error percentages for a given amount of time.

The program is available at http://files.b9.com/cluck and I wrote a recent blog entry about at http://b9.com

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

Why would anyone use a dumb algorithm?

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

I don't get it! If you just use the correct crystal frequency, the error is 0%, +/- the crystal tollerence.

I would think that using the internal oscilator and an OSCAL calibration function would be a better alternative to using "Off-Frequency" crystals for BAUD rate generation.

This all sounds as foolish as the "pathological hatred of the use of floating point small 8 bit controllers" statement.

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

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

microcarl wrote:
I don't get it! If you just use the correct crystal frequency, the error is 0%, +/- the crystal tollerence.

I would think that using the internal oscilator and an OSCAL calibration function would be a better alternative to using "Off-Frequency" crystals for BAUD rate generation.

This all sounds as foolish as the "pathological hatred of the use of floating point small 8 bit controllers" statement.

It is sometimes necessary to choose a crystal frequency based on timing considerations other than UART use. In these cases, where serial data transfer is required, it is necessary to know what baud rates are achievable within the acceptable tolerance.

Jim

Your message here - reasonable rates.

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

Quote:

In these cases, where serial data transfer is required, it is necessary to know what baud rates are achievable within the acceptable tolerance.

But can't that be done at design/desk time? Why would you do it at run time?

IF your crystal/AVR clock can change out from under you and IF your app then knows what that new clock rate is and IF the AVR needs to adapt to any arbitrary baud rate, THEN one might want a run-time routine.

Otherwise use a magic frequency & a look-up table with the common baud rates that your app will support encoded.

I still can't see why fp would be considered at run time. Or why even code it in that manner in the first place. Use the simple integer formula that people often use, the embodiment of the datasheet formula, to get a UBRR value. Then reverse the formula to be the ideal clock rate at that UBRR, and the error is easily calculated. If too far off, click the UBRR valeu the other way from the sign of the difference from ideal and actual clock frequencies, and re-calculate and see if that is better.

Lee

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.

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

Well,

I had thought about this issue in the past. The formula given by Atmel is fine when you use flaoting point math, otherwise you get a rounding error. It will be wrong about half the time.

So, I adjusted a little for the rounding error, and the technique checks out for me when I tested it in a spreadsheet. I am sure it is not perfect, but it seemed to work.

//the formula is:
baudrateDiv = (unsigned int) ((F_CPU + (unsigned long)(8L*baudrate))/(baudrate*16L)) - 1;

I'd love to hear about what might NOT work with this technique. I pieced this together from some code that I am currently using....

unsigned long baudrate = 19200;
unsigned int baudrateDiv;

//simple bounds check:
if (baudrateDiv >4095) baudrateDiv = 4095;		//max is 4095 on the M128

UBRRH = (unsigned char) (baudrateDiv>>8);
UBRRL = (unsigned char) (baudrateDiv & 0x00FF);

What this does not do is to check if the baud rate is so high that you should use the higher speed U2X setting. I think the logic for this would be relatively simple....

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

guys, I'm still new to microprocessor coding, but shouldn't you guys who did this for long time have the general driver code for the peripherals in AVR? I mean a general library should save you time, right?

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

Spamiam wrote:
(...)

//the formula is:
baudrateDiv = (unsigned int) ((F_CPU + (unsigned long)(8L*baudrate))/(baudrate*16L)) - 1;


This is the "most correct" way of doing positive integer division. When you want to do R = A / B, you should always add B/2 to A before the division.
Why? Check the following cases:

21 / 11.0 ~ 1.909 (float division)
21 / 11 = 1 (integer division)

The integer division is called "division by defect" (I'm not sure if this is the correct name in english), which means that you will loose ALL of the fractional part of the result. When the fractional part is bigger than 0.5, the most approximate value is the "next integer", giving the smallest error on the result.
In the integer division of the example above, the most "correct" (smaller error) result should be 2 and not 1, because 1.909 is much closer to 2 than to 1.
Adding B/2 before dividing by B will force the integer result into the smallest error. In the example, it would be

(21 + 11/2) / 11 = 26 / 11 = 2

There are some "edge" cases but I think they are not relevant, since it's just a matter of considering a glass "half full" or "half empty".

Embedded Dreams
One day, knowledge will replace money.

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

darthvader wrote:
guys, I got it:)

http://www.jaggersoft.com/pubs/C...


Asserts generate code. I don't know what they do in GCC or other compilers for microcontrollers. In a PC, an assert can display a pretty error window, but in a microcontroller... I don't think you'll want an assert in your code, at least in production. In debug... well, the code generated by the assert may be too big for you....

Embedded Dreams
One day, knowledge will replace money.

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

but the optimizer can get the constant calculation done automatically, right?

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

microcarl wrote:
This all sounds as foolish as the "pathological hatred of the use of floating point small 8 bit controllers" statement.

Foolish in what sense exactly?

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

no, now I stuck again.
there is still problem even now the static assert is used.

How to solve this problem:

#define COMPILE_TIME_ASSERT(expression) switch(0) { case 0 : case (expression) : ; }



void Init_USART0(double baud_rate, WORD format)
if (!(format & USART_OPT_SYNC)) // if it's asynchronous
{
    COMPILE_TIME_ASSERT(baudrate <= USART_U2X_MAX_BAUD_RATE); // error, if exceeding maximum baud rate
}
else
{
    COMPILE_TIME_ASSERT(baudrate <= USART_SYNC_MASTER_MAX_BAUD_RATE); // error, if exceeding maximum baud rate
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
My solution to the issue:

http://savannah.nongnu.org/file/...

This is basically just hard coding the only usable (less than 2% error) values out of the datasheet tables for all the popular crystal frequencies

Cliff

Hey I really like that header file cliff! Do you mind if I modify it a little bit? I will be sure to leave you as the author, with just a little 'Slightly modified by Brent Reamer' note!

I know it says

Quote:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: ...
but I like to ask anyways.

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

Perhaps this site would help you more:
http://www.ioccc.org/

Regards,
Steve A.

The Board helps those that help themselves.

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

I think I was wrong, this static assert can't check the parameter value inside the function.

Help please!

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

The following is how I implimented BAUD rate selection in a recent project.

Controller = Tiny2313
Clock frequency = 14.7456Mhz
Compiler = ImageCraft ICCAVR v7.xx

// PORT input bits used for the external BAUD rate settings
#define J_1 0
#define J_2 1 

#define NULL 0x00;

// Initialization of I/O port
// Set J_2:J_1 PULL-UPS active
PORTD = (1<<J_2) | (1<<J_1);
// Set LCD_control J_2:J_1 to inputs	
DDRD &= ~((1<<J_2) | (1<<J_1));

// Initialize UART0 
void USART_Init(void) {
 UCSRB = NULL; // Disable while setting baud rate
 UCSRA = NULL;
 UCSRC = (1<<UCSZ1) | (1<<UCSZ0); // 8 bit data
 UBRR = USART_SetBAUD (); // Set baud rate
 UCSRB = (1<<RXEN); // RXEN = Enable
}

// Actual baud rate = 9600 BAUD, (0.0% error) @ 14.7456MHz
// Actual baud rate = 19.2K BAUD, (0.0% error) @ 14.7456MHz
// Actual baud rate = 38.4K BAUD, (0.0% error) @ 14.7456MHz
// Actual baud rate = 115.2K BAUD, (0.0% error) @ 14.7456MHz
#define BAUD_9600 95
#define BAUD_19200 47
#define BAUD_38400 23
#define BAUD_115200 7 

char USART_SetBAUD (void) {
	char BaudSelectJumpersValue;
	static char BaudLookupTable[] = {BAUD_115200, BAUD_38400, BAUD_19200, BAUD_9600};

	// Get BAUD rate jumper settings	   
	BaudSelectJumpersValue  = PIND & ((1<<J_2) | (1<<J_1));

	return BaudLookupTable[BaudSelectJumpersValue];
}

Why would you ever use floating point with BAUD rate setting. Before I did that, I'd used initialize the USART channel (host & target systems) to say, 9600 BAUD and use the host system to automatically select the appropriate BAUD rate at COMMs connection time.

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

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

darthvader wrote:
no, now I stuck again.
there is still problem even now the static assert is used.

How to solve this problem:

#define COMPILE_TIME_ASSERT(expression) switch(0) { case 0 : case (expression) : ; }
(...)


A 'case' in a switch statment doesn't support non-constant and non-integer expressions.

Embedded Dreams
One day, knowledge will replace money.

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

yes, I think I have to write the function to parameterized macro then.

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

Wouldn't the five days this subject has been discussed have been better spent by, on day one, spending 10 minutes looking at the "examples of baud rate setting" tables in the AVR datasheet and leaving the other 4.95 days for doing something useful with the AVR? Just an idea. ;)

Cliff

(although I submitted that setbaud.h I only did it in response to the frequent requests on here for solving baud rate setting inaccuracies - personally I always just check the table in the datasheet and hard code the 51 or whatever!)

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

Quote:

personally I always just check the table in the datasheet and hard code the 51 or whatever!

As do I. I haven't run across a project yet that required other than a "standard" baud rate, so a "magic frequency" crystal takes care of the situation.

There might be a case if an arbitrary baud rate needs to be matched. But I can see no need for float--the "% error" that is normally calculated doesn't really have any maning except to humans; the raw difference in "ticks" or "counts" or whatever could just as easily be used--the object is to minimize that.

I've even got internal oscillator projects where I tune then to as close to 3.6864MHz instead of 4MHz--it just seemed more forgiving during initial testing.

If I had a MIDI project (with the odd baud rate), or using AT90USB (internal PLL for the USB means an 8MHz or 12MHz should be used) then maybe some calcs for the "best" UBRR/U2X are needed. But then it would still be fixed and could be done at design time. Existing AVRCalc can answer any % error questions.

Lee

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.

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

clawson wrote:
Wouldn't the five days this subject has been discussed have been better spent by, on day one, spending 10 minutes looking at the "examples of baud rate setting" tables in the AVR datasheet and leaving the other 4.95 days for doing something useful with the AVR? Just an idea. ;)

Cliff

Well, it would have been. Just pull out a calculator and do the math. Check it for the amount of error, and move on.

But it would be nice to have a general solution for ANY cpu speed, with intelligent setting of the U2X and a warning of a problem if more than a 2% error is present. Oh, and all of the work done by the compiler, so that it simply results in a constant in the code.

In my case, I have a set of 3 jumpers that are used to select one of 7 baud rates. So, I have the compiler do the math for 7 different constants. It works well, but I did not worry about the U2X because I was running at a 16MHz on the M88, and I was not going to super high baud rates.

-Tony

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

Quote:

Oh, and all of the work done by the compiler, so that it simply results in a constant in the code.

Oh, and adapt to all settings of CLKPR automatically at run time, but have only a compile-time constant.
Oh, and also take into account CKDIV8.
Oh, yeah, have it adapt also when OSCCAL is changed.

These ain't your Father's AT90Ss. The implication of a single "compile-time constant" means a constant AVR clock rate and desired baud rate, right? I think existing tools can handle that fairly well, whether it is the datasheet tables or AVRCalc or the macro presented earlier.

Having the compiler compute it for you? Use CodeVision, use the Wizard, specify baud rate and AVR clock speed, and Ouila! error percentage and U(S)ART register setting code is right there.

Lee

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.

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

Quote:

Well, it would have been. Just pull out a calculator and do the math. Check it for the amount of error, and move on.

But it would be nice to have a general solution for ANY cpu speed, with intelligent setting of the U2X and a warning of a problem if more than a 2% error is present. Oh, and all of the work done by the compiler, so that it simply results in a constant in the code.

Maybe just grab a copy of AvrCalc here: http://www.esnips.com/web/AtmelAVR

and be done with it :)

Mike H.