Really strange USART issue with xmega128a3u

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

Hi,

 

I just introduced a bootloader in the xmega128a3u. The bootloader works nicely in the xmega128a1u, and has been doing so for a couple of years. But when I used the same bootloader in the a3u, I lost communication as soon as I entered the bootloader itself. The application and the bootloader use USARTC1, but it just stopped transmitting.

 

I added some "blips" on an output port, so I could get a feeling of what was happening on an oscilloscope. Then I found out that the bootloader was running and receiving – and it also spent the exact amount of time transmitting, but still with no output. Then I noticed that the Tx port had become an input. Really strange, I thought, since I specifically set PORTC_DIR among other registers in the bootloader code.

 

I found a really easy work-around, but I have no idea why it works. Maybe someone else has a clue?

 

This is an assembly dump (exerpt) of what didn't work:

  SetupUsartPort();
   20124:	86 e8       	ldi	r24, 0x86	; 134
   20126:	80 93 44 06 	sts	0x0644, r24
   2012a:	10 92 84 06 	sts	0x0684, r1
   2012e:	8f eb       	ldi	r24, 0xBF	; 191
   20130:	80 93 40 06 	sts	0x0640, r24
   20134:	8f e3       	ldi	r24, 0x3F	; 63
   20136:	80 93 80 06 	sts	0x0680, r24
  USART_CTRLA=0; // Interrupts off
   2013a:	10 92 b3 08 	sts	0x08B3, r1
  USART_CTRLB=0; // USART off
   2013e:	10 92 b4 08 	sts	0x08B4, r1
  USART_BAUDCTRLA=(char)BSEL(F_CPU,115200);
   20142:	87 e1       	ldi	r24, 0x17	; 23
   20144:	80 93 b6 08 	sts	0x08B6, r24
  USART_BAUDCTRLB=(char)((BSEL(F_CPU,115200)>>8) & 0x0F)+(BSCALE(F_CPU,115200)<<4);
   20148:	84 ea       	ldi	r24, 0xA4	; 164
   2014a:	80 93 b7 08 	sts	0x08B7, r24
  USART_CTRLC=(USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc); // Asynch mode; No parity; 1 stop bit; 8-bit size
   2014e:	83 e0       	ldi	r24, 0x03	; 3
   20150:	80 93 b5 08 	sts	0x08B5, r24
  USART_CTRLB=(USART_RXEN_bm | USART_TXEN_bm);
   20154:	88 e1       	ldi	r24, 0x18	; 24
   20156:	80 93 b4 08 	sts	0x08B4, r24

The quick explanation: I first set up PORTC_OUT, PORTE_OUT, PORTC_DIR and PORTE_DIR. PORTC_DIR is set to 0xBF, meaning that pin PC7 should be an output.

Then, the USART is set up with baud rate and stuff. All addresses and values are correct. However, PC7 strangely works as an input, and nothing (obviously) comes out.

 

Now for the simple change that makes it all work:

  USART_CTRLA=0; // Interrupts off
   20124:	10 92 b3 08 	sts	0x08B3, r1
  USART_CTRLB=0; // USART off
   20128:	10 92 b4 08 	sts	0x08B4, r1
  USART_BAUDCTRLA=(char)BSEL(F_CPU,115200);
   2012c:	87 e1       	ldi	r24, 0x17	; 23
   2012e:	80 93 b6 08 	sts	0x08B6, r24
  USART_BAUDCTRLB=(char)((BSEL(F_CPU,115200)>>8) & 0x0F)+(BSCALE(F_CPU,115200)<<4);
   20132:	84 ea       	ldi	r24, 0xA4	; 164
   20134:	80 93 b7 08 	sts	0x08B7, r24
  USART_CTRLC=(USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc); // Asynch mode; No parity; 1 stop bit; 8-bit size
   20138:	83 e0       	ldi	r24, 0x03	; 3
   2013a:	80 93 b5 08 	sts	0x08B5, r24
  USART_CTRLB=(USART_RXEN_bm | USART_TXEN_bm);
   2013e:	88 e1       	ldi	r24, 0x18	; 24
   20140:	80 93 b4 08 	sts	0x08B4, r24
  SetupUsartPort();
   20144:	86 e8       	ldi	r24, 0x86	; 134
   20146:	80 93 44 06 	sts	0x0644, r24
   2014a:	10 92 84 06 	sts	0x0684, r1
   2014e:	8f eb       	ldi	r24, 0xBF	; 191
   20150:	80 93 40 06 	sts	0x0640, r24
   20154:	8f e3       	ldi	r24, 0x3F	; 63
   20156:	80 93 80 06 	sts	0x0680, r24

The only thing I've done: I've moved the port setup stuff down, so I set PORTC_OUT, PORTE_OUT, PORTC_DIR and PORTE_DIR after the USART has been set up and started. Then, PC7 is suddenly works as an output (like it should), and the bootloader works like a charm.

 

In the main program, the registers are set in the opposite order, but the USART works fine anyway. And the xmega128a1u doesn't care about the order in the application or the boot section.

 

Has anyone experienced something similar? And can anyone explain what is going one here?

 

Br, ErikT

You're absolutely right. This member is stupid. Please help.

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

Why is there no source shown for SetupUsartPort()? A quick grep of the iox*.h files reveals that 0x644 is PORTC_OUT, 0x684 is PORTE_OUT, 0x640 is PORTC_DIR and 0x680 is PORTE_DIR so I guess it is:

void SetupUsartPort(void) {
 PORTC_OUT = 0x86;
 PORTE_OUT = 0;
 PORTC_DIR = 0xBF;
 PORTE_DIR = 0x3F;
}

 

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

A quick grep of the iox*.h files reveals that 0x644 is PORTC_OUT, 0x684 is PORTE_OUT, 0x640 is PORTC_DIR and 0x680 is PORTE_DIR

Yeah, that's kinda what I tried to explain between the two code sections. But the point is not really what registers are accessed, but that somehow the order matters.

 

The content of SetupUsartPort is elsewhere, because we use the same bootloader code for several different products. Granted, many things could be made more beautiful.

 

You're absolutely right. This member is stupid. Please help.

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

Not sure if that helps: I am always initialising the port as output and the uart is initialised much later (256a3u, 128a4u).    Have you stepped through the code to confirm that the direction is set?    If it is an input later something must be writing it in between.   Set a data breakpoint if you can, otherwise insert breakpoints in between to home in on the problem.   Otherwise step through it until you see the direction changing.   Is something writing to DIRCLR or DIRTGL by mistake?

Last Edited: Sat. Apr 18, 2015 - 05:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am always initialising the port as output and the uart is initialised much later (256a3u, 128a4u).

Exactly. So do I. Normally, I do this:

  • Set initial output values (high/low)
  • Set port directions (in/out)
  • ...
  • Set USART registers

 

This works nicely in all my code. Except in the 128a3u bootloader area.

When I did this

  • Set initial output values (high/low)
  • Set port directions (in/out)
  • Set USART registers

the Tx port was somehow still an input port. But when I changed the order to this

  • Set USART registers
  • Set initial output values (high/low)
  • Set port directions (in/out)

everything worked as expected. Only the order was changed – nothing else. And there is no code in between.

 

No, DIRCLR, DIRSET and DIRTGL are not used at all. And DIR is only used in two places: In the beginning of the bootloader, and in the beginning of the main application. And to the exact same values, by the way.

 

I have no explanation for this at all. And I don't really expect to get one, although it would be nice if anyone could confirm that there is a problem. If I ever get the time, I will make a minimalistic piece of code in order to investigate this further.

You're absolutely right. This member is stupid. Please help.

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

ErikT wrote:
The content of SetupUsartPort is elsewhere

You said there is no other code in between but if it is elsewhere there must be at least a call or something.    Or is the assembler output above the true thing what the processor is doing?

 

Otherwise really use the debugger and single step through the assembler instructions monitoring the registers.   You then must notice either that the direction is not set or where it is reset afterwards.

 

When I see things like this I am always tempted to blaim Atmel for a hardware fault but in 99% of the cases it is my software.   Only once I ever discovered a hardware problem.

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

SetupUsartPort is acutally a macro. Yeah, I know, a lot of people want macros to be written in capitals, but I hate SHOUTING in my code.

 

No, there really isn't anything in between, which you can also see on the addresses. The code you see is cut from the .lss file, so I certainly expect it to be identical with what ends up inside the device.

 

Of course, in the vast majority of cases, any error is the developer's fault. But if you've worked with xmega128A1 (without the 'U'), you'll have noticed plenty of hardware errors. Most of these have work-arounds, but they are still errors. I've run into some of these myself, but (fortunately) Atmel had already discovered and documented them.

 

Now, if this really is a hardware error, I believe it is the first undocumented one for me. At least with Atmel devices. But I can't explain it in any other way. It is so incredibly simple to recreate the problem, and the particular piece of assembly code is really easy to read. As I've pointed out, the only change is the order in which the port and usart values are set. There are no interrupts (because I don't use interrupts in the bootloader), and besides, the main application interrupts don't mess with port direction bits.

 

Some day, when I have made a minimalistic piece of firmware which demonstrates the problem, I'll be back with the full code.

You're absolutely right. This member is stupid. Please help.

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

Nothing wrong with lower case macros.   If it is a c-function we have it lower case too.  sei() and cli() are also lower case.   The compiler doesn't care and you have to suit yourself.

 

We are using a good few 256a3u with port and then uart init and that works (also in boot loader memory area).   The 128a3u has only less memory but is identical otherwise, isn't it?

 

Have you single stepped through the assembler instructions?    That is normally an eye opener for me, or, very very rarely, the discovery of a hardware fault.   I cannot see that being a hardware problem since there is no dependency of port direction and uart setup any more as in the mega avrs (unless somebody left a line of HDL code in there by mistake smiley).
 

Last Edited: Mon. Apr 20, 2015 - 09:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nothing wrong with lower case macros.   If it is a c-function we have it lower case too.  sei() and cli() are also lower case.   The compiler doesn't care and you have to suit yourself.

Totally agree. But I've had my head ripped off about such things before. Some people seem to think that "If I cant' help the poor guy solve his problem, then at least I can criticize his syntax, and thus derail the discussion."

 

Yeah, pure reason suggests that 128a3 and 256a3 should be identical, except from the memory. But I've been surprised before.

Have you single stepped through the assembler instructions?

Nope. And I won't have time for that in the near future. Besides, I find glancing at assembly code more revealing than bouncing around in a debugger. I'm strange that way. Sorry.

You're absolutely right. This member is stupid. Please help.

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

But having macro names in capitals is an extremely well-established and widely-adopted practice.

 

Sure, the compiler doesn't care. And it makes no difference if you are the only one reading the code. But, if you expect other people to read your code, you must be prepared for them to be "surprised" when you break with such well-established and widely-adopted conventions!

 

Remember that the purpose of source code is as much (if not more) to convey your intentions to other human readers as it is to the compiler.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets not get distracted with unrelated discussions about source code style and lets focus on the problem.

ErikT wrote:
I find glancing at assembly code more revealing than bouncing around in a debugger. I'm strange that way. Sorry.

Fair enough.   But the assembler output does not show you what the registers are doing.   So PLEASE when you find time: set a breakpoint to the beginning of your initialisation, switch to assembler when breakpoint hit, single step the few instructions and report back.

 

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

Off topic:

awneil wrote:
But having macro names in capitals is an extremely well-established and widely-adopted practice.

Not when the macro represents a short function to be inlined.   See sei() and cli() for example.   

ThisIsAFunction() Microsoft style is also well established and widely adopted, I still use this_is_a_function() because I can read it.    If all the lemmings jump off the cliff I don't follow them.

Last Edited: Mon. Apr 20, 2015 - 10:53 AM