possible compiler bug when setting UART baud rate

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

I've noticed that the first few bytes transmitted by my UART can sometimes get corrupted, then all later transmissions come through perfectly.  In an effort to solve this, I came up with a minimal example:

 

#define F_CPU	8000000

#include <avr/io.h>
#include <inttypes.h>
#include <stdio.h>

#define UART_DDR	DDRD
#define UART0_RXD	PD0
#define UART0_TXD	PD1

#define UART_BUFFER_SIZE	50
char uartBuffer[UART_BUFFER_SIZE];

void uart_setup(uint32_t);
void uart_transmit_polled(uint8_t);
void transmit_string(char *);

int main(void)
{
	uart_setup(9600);
	while (1);
}

void uart_setup(uint32_t baud)
{
	//baud = 9600;
	
	UART_DDR |= (1<<UART0_TXD);
	
	uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (baud*16)) - 1;
	UBRR0H = (uint8_t)(baudUBRRSetting >> 8);
	UBRR0L = (uint8_t)(baudUBRRSetting);
	
	UCSR0B = (1<<TXEN0);//just tx for this test
	UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
	
	snprintf(uartBuffer, UART_BUFFER_SIZE, "UBRR Value: %u\r\n", baudUBRRSetting);
	transmit_string(uartBuffer);
}

void uart_transmit_polled(uint8_t value)
{
	while (!(UCSR0A & (1 << UDRE0)));
	UDR0 = value;
}

void transmit_string(char *string)
{
	while (*string != '\0')
	{
		uart_transmit_polled(*string);
		string++;
	}
}

Compiling and running this AVR-GCC code on an atmega324pa board (with an 8 MHz crystal, +3.0V Vcc) using either Atmel Studio 7.0 or WinAVR 20100110 just gives me garbage when examining the level-converted UART output on a computer terminal program (9600 baud, 8 data bits, 1 stop bit, no parity, no flow control). 

 

However, if I un-comment the "baud = 9600;" line, it works flawlessly.  Perfect transmitted characters on each reset, no garbage/corruption at all.  Unless I overlooked something, it seems to be a compiler bug where using the function parameter directly in the equation causes some kind of baud rate problem or buffer corruption, but setting it to a constant before use works fine. 

 

I've output the UBRR value for each code test and get 51 either way, so it's not a calculation problem.  I see the same problem with both polled and interrupt-based/buffered UART transmission.  I tried using different TTL-RS232 converters and terminal programs, all with the same result. 

 

Could someone try running this and see if you get similar results?  The obvious workaround is to hard-code the baud rate, but I would like to find out the root problem.  My assembly knowledge isn't advanced enough to compare the low-level outputs, unfortunately.  Thanks for the help. 

 

- bobasaurus / Allen

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

Not a compiler bug here in 2015!

 

more likely

a) MCU power supply Vcc not stable yet. Add a delay.

b) something the connection to or the other end device. Rewire for a loop-back UART TX->RX and see what you read back.

c) Oscillator not really at declared value for F_CPU.

 

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

I just tried this code on a different board with an atmega324p (without the "a" suffix) running at 5V with a 16 MHz crystal.  And both versions work perfectly, no corruption at all.  So maybe this problem only applies to the atmega324pa.  Or it has a dependence on clock speed or speed grade. Or maybe my circuit has a problem. 

Last Edited: Fri. Dec 18, 2015 - 01:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stevech wrote:

Not a compiler bug here in 2015!

 

more likely

a) MCU power supply Vcc not stable yet. Add a delay.

b) something the connection to or the other end device. Rewire for a loop-back UART TX->RX and see what you read back.

c) Oscillator not really at declared value for F_CPU.

 

 

a) I've tried soft resetting without cycling power and get the same results. 

b) Loop-back is a good idea, I'll give it a try. 

c) I'm using an external 8 MHz crystal with appropriate caps, but maybe it's not rated as accurately as I'm hoping.  The proper fuse is set for it, plus the max settling delay. 

 

Thanks for the input. 

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

What does the RS232 signal look  like? Does it have the proper amplitude and baseline. If you are using one of those charge pump interface chips, you might have a wrong cap in one location or a bad solder connection to one of them.

 

Jim

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define F_CPU	8000000

8000000 don't fit an int! need to add UL

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

8000000 don't fit an int! need to add UL

 I'm pretty sure that a number that doesn't fit in an int becomes a long automatically.  EXPRESSIONS don't, so if you want "16*9600" in some bitrate calculations, at least one of them needs a suffix...

 

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

this line need some UL :

uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (baud*16)) - 1;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

What does the RS232 signal look  like? Does it have the proper amplitude and baseline. If you are using one of those charge pump interface chips, you might have a wrong cap in one location or a bad solder connection to one of them.

 

Jim

 

I've tried several external (commercial) TTL-RS232 adapters with the same result.  I don't have an onboard converter.  The UART TTL signal is hard to capture, as I only get corruption on the first few bytes so the edge-triggered oscilloscope view isn't very handy (guess I could figure out how to acquire and hold the first few bytes, my scope skills aren't that advanced yet).  Viewing the looping output on the scope looks clean, stable 0-3V TTL with sharp transitions and about 100 uS per bit which matches 9600 baud. 

 

Edit: 104.0 uS per bit measured positive width.  Almost perfectly matches 1/9600*1000*1000 uS expected. 

Last Edited: Fri. Dec 18, 2015 - 08:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

this line need some UL :

uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (baud*16)) - 1;

 

F_CPU is replaced by 8000000 by the preprocessor.  This is an integer literal which I believe avr-gcc represents with a 32-bit integer.  So it is well within range of the integer math without worrying about overflow.  I've checked the resulting value of this calculation both ways and get the proper answer.  And still get corruption on the first few bytes of the atmega324pa UART transmission unless I assign baud to 9600 within the function.  And strangely this doesn't happen on the non-a-suffix version of this microcontroller I have on a different board. 

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

Why don't you simply study the LSS in the go/no go cases? 

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

If the baud rate is incorrectly calculated (e.g. not having UL), then why does the link start working well after the first few characters?

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

void uart_setup(uint32_t baud)
{
	//baud = 9600;
	
	UART_DDR |= (1<<UART0_TXD);
	
	uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (baud*16)) - 1;

Silly questions time....

 

You call USART_SETUP(9600) in main and in your function you then assign the value to 9600 anyway which would negate what value you are passing to the function no?

 

Also, do you really need a uint32_t integer where your formula calls for an uint16_t?

 

I do think Mr. Wagner, and Steve are right that there is something funny in the hardware.  My questions were purely from wonder.

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

jgmdesign wrote:


void uart_setup(uint32_t baud)
{
	//baud = 9600;

	UART_DDR |= (1<<UART0_TXD);

	uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (baud*16)) - 1;

Silly questions time....

 

You call USART_SETUP(9600) in main and in your function you then assign the value to 9600 anyway which would negate what value you are passing to the function no?

 

Also, do you really need a uint32_t integer where your formula calls for an uint16_t?

 

I do think Mr. Wagner, and Steve are right that there is something funny in the hardware.  My questions were purely from wonder.

 

The only reason I have that commented-out line is from experimenting with hard-coding the baud rate.  Directly assigning 51 to baudUBRRSetting fixes the corruption, as does making a temporary variable baudTemp = 9600 and using that in the calculations.  So I went further and discovered that even re-assigning the function parameter to 9600 fixes the problem, despite the fact that it should already be 9600 from the calling code. 

 

I could use a uint16_t for the function parameter type then cast it larger when calculating (baud*16), but the memory use seemed similar either way... I chose to keep it more readable without lots of intermittent casts mid-expression. 

 

 

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

clawson wrote:

Why don't you simply study the LSS in the go/no go cases? 

 

I've attached the "not working" and "working" LSS files (renamed to .txt for the attachments to pass) if anyone is interested.  I'll try to give them a read, but my AVR assembly is very rusty. 

Attachment(s): 

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

bobasaurus wrote:

The only reason I have that commented-out line is from experimenting with hard-coding the baud rate.  Directly assigning 51 to baudUBRRSetting fixes the corruption, as does making a temporary variable baudTemp = 9600 and using that in the calculations.  So I went further and discovered that even re-assigning the function parameter to 9600 fixes the problem, despite the fact that it should already be 9600 from the calling code. 

 

I could use a uint16_t for the function parameter type then cast it larger when calculating (baud*16), but the memory use seemed similar either way... I chose to keep it more readable without lots of intermittent casts mid-expression. 

 

So it seems like you're saying/showing it's not a compiler problem or hardware problem but a software problem.

Why not try adding the UL to your F_CPU and rewrite your baud calculations with less casting and see what happens.

 

 

 

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

Question:  Suppose that you send one or two characters and they are corrupted. What happens if you wait (some number of minutes) with the unit ON and try again? What happens if you wait after the unit is turned on (again, some number of minutes)?

 

Your analysis and debugging MIGHT be simplified if you set up your code so that it sends the message on a switch closure. Then, you could experiment as I suggested in the line above. My hunch is that if there is a substantial delay between unit turn on and message, that you might see different behavior. As a quick test, try putting a delay before the call to uart_setup().

 

Jim

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

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

ront1234 wrote:

bobasaurus wrote:

The only reason I have that commented-out line is from experimenting with hard-coding the baud rate.  Directly assigning 51 to baudUBRRSetting fixes the corruption, as does making a temporary variable baudTemp = 9600 and using that in the calculations.  So I went further and discovered that even re-assigning the function parameter to 9600 fixes the problem, despite the fact that it should already be 9600 from the calling code. 

 

I could use a uint16_t for the function parameter type then cast it larger when calculating (baud*16), but the memory use seemed similar either way... I chose to keep it more readable without lots of intermittent casts mid-expression. 

 

So it seems like you're saying/showing it's not a compiler problem or hardware problem but a software problem.

Why not try adding the UL to your F_CPU and rewrite your baud calculations with less casting and see what happens.

 

 

 

 

Sure seems like a compiler problem when assigning the same value that should already be present to an input parameter suddenly makes it work.  I've tried using NOP() statements and delays in place of the re-assignment, but they don't help so it's not an obvious timing problem. 

 

I tried adding the UL suffix to my F_CPU definition and see no difference... still get corruption unless I un-comment that line. 

 

I also tried changing the baud parameter to a 16-bit unsigned int, then casting it larger in the expression... no improvement. 

 

Here is the code with the "UL" suffix and casting changes (no improvement or change from the original code):

 

#define F_CPU	8000000UL

#include <avr/io.h>
#include <inttypes.h>
#include <stdio.h>

#define UART_DDR	DDRD
#define UART0_RXD	PD0
#define UART0_TXD	PD1

#define UART_BUFFER_SIZE	50
char uartBuffer[UART_BUFFER_SIZE];

void uart_setup(uint16_t);
void uart_transmit_polled(uint8_t);
void transmit_string(char *);

int main(void)
{
	uart_setup(9600);
	while (1);
}

void uart_setup(uint16_t baud)
{
	//baud = 9600;
	
	UART_DDR |= (1<<UART0_TXD);
	
	uint16_t baudUBRRSetting = (uint16_t) (F_CPU / (((uint32_t)baud)*16)) - 1;
	UBRR0H = (uint8_t)(baudUBRRSetting >> 8);
	UBRR0L = (uint8_t)(baudUBRRSetting);
	
	UCSR0B = (1<<TXEN0);//just tx for this test
	UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
	
	snprintf(uartBuffer, UART_BUFFER_SIZE, "UBRR Value: %u\r\n", baudUBRRSetting);
	transmit_string(uartBuffer);
}

void uart_transmit_polled(uint8_t value)
{
	while (!(UCSR0A & (1 << UDRE0)));
	UDR0 = value;
}

void transmit_string(char *string)
{
	while (*string != '\0')
	{
		uart_transmit_polled(*string);
		string++;
	}
}

 

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

What optimization setting are you using?

 

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

Size optimization (-Os) on both AS7 and WinAVR.  I just tried both -O0 and -O1 and it makes no difference... still corrupting unless I uncomment the line. 

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

I just tried running this code on a different manufacturer's board, which also has an atmega324pa but at 5V and 7.3728 MHz, and I get the same corruption problems.  So it doesn't seem to be dependent on my circuit, input voltage, or crystal speed.  I wonder if this is a quirk with the atmega324pa chips only... I'll try replacing one of my boards' microcontroller with a regular atmega324p and see what happens. 

Last Edited: Fri. Dec 18, 2015 - 10:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After replacing the atmega324pa on my board with the non-a model, I get the exact same problem.  So I guess it's not dependent on the "a" suffix. 

 

But, my other board running the atmega324p at 16 MHz shows no corruption, working perfectly.  I tried setting it to the internal 8 MHz RC oscillator, and the corruption is back (unless I uncomment the line).  So in summary:

 

7.3728 MHz xtal:  corruption (on a different manufacturer's board, atmega324pa @ 5V)

8 MHz xtal: corruption (on my board, both atmega324p and atmega324pa @ 3V)

8 MHz RC: corruption (on my other board, atmega324p @ 5V)

16 MHz xtal: no problems, works perfectly (on my other board, atmega324p @ 5V)

 

So the problem is minimized or eliminated when running at 16 MHz instead of 8 or 7.3728.  Quite strange. 

Last Edited: Fri. Dec 18, 2015 - 11:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting...

 

When using the crystal what fuse settings?

Try full swing with slow power and longest delayed clock.

 

What happens when you put a small delay (maybe 20ms) before uart_setup(9600) in main?

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

After reading the datasheet I see you are using pretty much the example init routines the datasheet uses which is fine.

 

One thing you do not have that the datasheet does have is this:

/* Wait for empty transmit buffer */
while ( !( UCSRnA & (1<<UDREn))) )

Maybe you could put this between your init and your transmit routine and see if that clears things up?  Possible that the USART is not completely "ready to go" at the slower clock speeds when you are loading the transmit register?

 

Could not hurt to try.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

I've attached the "not working" and "working" LSS files

The "working" code ends up not doing any math, and just copies some constants into the BRR.

The non-working code looks fine as well - does 32bit math where it ought to, and has appropriate function calls...  I don't see why it shouldn't work.

 

000000e0 <uart_setup>:
  e0:	51 9a       	sbi	0x0a, 1	; 10     ;; UDR
  e2:	9b 01       	movw	r18, r22     ;; copy baud
  e4:	ac 01       	movw	r20, r24
  e6:	84 e0       	ldi	r24, 0x04	;; shift left 4 bits (*16)
  e8:	22 0f       	add	r18, r18
  ea:	33 1f       	adc	r19, r19
  ec:	44 1f       	adc	r20, r20
  ee:	55 1f       	adc	r21, r21
  f0:	8a 95       	dec	r24
  f2:	d1 f7       	brne	.-12     	; 0xe8 <uart_setup+0x8>
  f4:	60 e0       	ldi	r22, 0x00	; 0   ;; 8000000
  f6:	72 e1       	ldi	r23, 0x12	; 18
  f8:	8a e7       	ldi	r24, 0x7A	; 122
  fa:	90 e0       	ldi	r25, 0x00	; 0
  fc:	0e 94 ae 00 	call	0x15c	; 0x15c <__udivmodsi4>  ;; divide
 100:	21 50       	subi	r18, 0x01	;; cast to 16bits, subtract 1
 102:	31 09       	sbc	r19, r1
 104:	30 93 c5 00 	sts	0x00C5, r19   ;; store in UBRR.
 108:	20 93 c4 00 	sts	0x00C4, r18

 

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

For the non-working one, look at the LSS file for writes to UBRR. I'll bet there is only ONE. If there is only one, it cannot be a "compiler bug" because the UBRR is not being changed! 

 

Is the number of data bits ever changed, or the parity or stop bits or anything else that would change the validity of the character other than baud rate?

 

Another possibility, though improbable, is buffer over-run. Like something clobbering the first few characters of uartbuffer.

 

Do you have a debugger? You could put a break where the a character is loaded into UDR0 and observe what that character actually is.

 

I have little doubt that what you report is actually happening. However, it is simply not caused by a "compiler bug". And,. thinking "compiler bug" will divert your thinking and analysis process, causing you to overlook the real cause. So, step back, and ask yourself how you can determine what is really put into the UDR0. That will provide your answer, though possibly indirectly.

 

Jim

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

Last Edited: Sat. Dec 19, 2015 - 07:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use your sound card as an oscilloscope and capture exactly what gets sent. Or a $10 logic analyser.

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

There are no flaws in the generated code.  Period.

 

I would guess that your terminal program is getting confused by a false start bit.

 

You make the TXD pin an output with:

	UART_DDR |= (1<<UART0_TXD);

Since the default state of the PORTn bits is 0, this will drive TXD low, which can be seen by the PC's USART as a start bit.

 

Enabling the TX (with TXEN) will drive TXD high to the idle state.

 

In the hard-coded 'working' version, there are only a few cycles (actually, 8 cycles, so one microsecond) between those two events.

 

In the passed-argument 'non-working' version, the call to __udivmodsi4 (which performs the 32-bit divide) takes about 600 cycles, or about 75 us.  At 9600 baud, one bit time is 104 us, so 75 us is very nearly one bit time.  The UART on the PC side will see this as a start bit and will therefore be thrown out of sync by one bit on every received byte thereafter, until the next time the line goes to the idle state for long enough to resynchronise the TX on the AVR with the RX on the PC.  Until that happens, the actual start-bit sent by the AVR will be interpreted as bit 0, bit 1 will be seen as bit 2, etc., and the stop bit will be seen as bit 7, which will always be set.  So every received character until re-sync will be 0x80 or above.  Most PC terminal emulators display that as a special character of some kind, either 'extended' ASCII, or as block graphics.

 

In the case of the 'working' version, the very short time which TXD is driven low (1 us) will get filtered out by your PC UART's false start-bit detection, and the two UARTs will never be out of sync to begin with.

 

Since TXD automatically becomes an output when TXEN is set, there is no need to twiddle DDRD at all.  Delete that line of code and it should work either way.

"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]

 

Last Edited: Sun. Dec 20, 2015 - 07:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

+1

 

Really nice bit of detective work there Joey! yes

Letting the smoke out since 1978

 

 

 

 

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

TXD automatically becomes an output when TXEN is set, there is no need to twiddle DDRD at all.

 I was wondering about that.   Some of the newer microcontrollers (SAMD?) are pretty insistent about configuring the pins for output in the GPIO controller in order for them to work as outputs from other peripheral.  (presumably this means that the multiplexing happens "further back" from the pin.)

 

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

I have no experience with SAMD, but AFAIK, AVR8 are pretty consistent:

 

 

 

 

 

"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]

 

Last Edited: Mon. Dec 21, 2015 - 04:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

digitalDan wrote:
Really nice bit of detective work there Joey! yes
If I'm right ;-)

 

However, I'm fairly convinced:

bobasaurus wrote:
But, my other board running the atmega324p at 16 MHz shows no corruption, working perfectly.  I tried setting it to the internal 8 MHz RC oscillator, and the corruption is back (unless I uncomment the line).

At 16 MHz, the ~600 cycles taken by the 32-bit division will be about 37 us, so about a third of a bit time.  The PC USART false-start-bit-detection would reject it, so that explains that.

"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]

 

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

joeymorin wrote:

There are no flaws in the generated code.  Period.

 

I would guess that your terminal program is getting confused by a false start bit.

 

You make the TXD pin an output with:

	UART_DDR |= (1<<UART0_TXD);

Since the default state of the PORTn bits is 0, this will drive TXD low, which can be seen by the PC's USART as a start bit.

 

Enabling the TX (with TXEN) will drive TXD high to the idle state.

 

In the hard-coded 'working' version, there are only a few cycles (actually, 8 cycles, so one microsecond) between those two events.

 

In the passed-argument 'non-working' version, the call to __udivmodsi4 (which performs the 32-bit divide) takes about 600 cycles, or about 75 us.  At 9600 baud, one bit time is 104 us, so 75 us is very nearly one bit time.  The UART on the PC side will see this as a start bit and will therefore be thrown out of sync by one bit on every received byte thereafter, until the next time the line goes to the idle state for long enough to resynchronise the TX on the AVR with the RX on the PC.  Until that happens, the actual start-bit sent by the AVR will be interpreted as bit 0, bit 1 will be seen as bit 2, etc., and the stop bit will be seen as bit 7, which will always be set.  So every received character until re-sync will be 0x80 or above.  Most PC terminal emulators display that as a special character of some kind, either 'extended' ASCII, or as block graphics.

 

In the case of the 'working' version, the very short time which TXD is driven low (1 us) will get filtered out by your PC UART's false start-bit detection, and the two UARTs will never be out of sync to begin with.

 

Since TXD automatically becomes an output when TXEN is set, there is no need to twiddle DDRD at all.  Delete that line of code and it should work either way.

 

Wow Joey, you completely solved it.  If I move the DDR initialization to directly before or after the UCSR0B/C initialization, the problem is completely fixed.  Even commenting-out the DDR initialization fixes the issue, as you mentioned (which explains why the datasheet's uart setup code does not touch the DDR).  It must have been the false start bit and accidental bit timing due to the compiled assembly changes.  I was seeing unicode representations of non-printable or non-ascii characters until things synched up again.  So much for it being a compiler bug... it was just timing after all.  Damn subtle, great detective work. 

 

Thanks a lot for all the help from people in this thread.  I try to avoid posting unless I find an unusual case like this where only true experts could puzzle it out.  Sorry for my delayed response, I had a few other tasks at work to handle first.  I'll remember the DDR trick and fix some of my existing firmware.