Problem Switching from Polled to Interrupt Driven UARTs

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

In my bootloader I poll status bits to transmit data on UART1. When I am done in the bootloader, I jump to 0x0000 and begin my main application. Almost immediately I set up my transmit routines (transmit complete, NOT udr empty) and enable interrupts. Imeediately I get massive amounts of garbage.

I think what happens is that when I am in my bootloader the TXC1 interrupt gets set and is never cleared since interrupts are not enabled. When I jump to the main app, the interrupt flag is still set and as soon as I use sei() the interrupt is fired off and massive data is transmitted.

My old transmit code is basically as follows:

if (--uart1_counter)
		UDR1 = *uart1_data_ptr;

Someone pointed out to me that I could change that to

if (uart1_counter)
{
     uart1_counter--; 
     UDR1 = *uart1_data_ptr;
}

And this dose keep the garbage from coming. However, its a workaround as the interrupt is still triggered as soon as I call sei();

I've changed my init routine to the following:

void init_uart1	( void )
{
	//turn on TX, RX, and interrupts for UART1
   	UCSR1B = (1<<TXEN1) | (1<<TXCIE1) | (1<<RXEN1) | (1<<RXCIE1);

   	// uart1_prescale is at most 12 bits.  Load the upper nibble
   	// to the lower half of UBRR1H and the lower byte to
   	// UBRR1L.  This sets the baud rate chosen by the #define 
   	// statements in uart.h.  The upper nibble of UBRR0H must
   	// be 0000
   	UBRR1H = (((uart1_prescale)>>8) & 0x0F);
  	UBRR1L = uart1_prescale;
  	
 	while((UCSR1A & (1<<UDRE1)) == 0); // Wait for it to be empty then clear interrupt
 	UCSR1A |= (1<<TXC1); // Clear pending transmit interrupt
}

The declaration and handling of UART0 is basically the same as UART1, but since I am running Tera Term Pro on uart1 I see the junk there. If UART0 has the same problem, then I'll mirror the fix there (although it does not appear that I've having the problem there....)

So how do I clear the interrupt? I would have thought that the last line of code above would do it, and I added the line above it when just the last line alone wasn't fixing it.

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

Oops, wrong forum... meant this for AVR GCC, not AVR :( Mods, please move

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

Your change to test the uart1_counter (without the autodecrement) isn't
a workaround, it's the correct way to do it. Getting a TXC interrupt at a
moment (which would include startup) where you have nothing to send
is, one might expect, a routine occurrence. As such, your ISR doesn't
really have to do anything special to handle the particular case of startup.

I see you clearing TXC1 during initialization, which should work, but only
if it's set already. Since one can expect that TXC will be set a full
character time later than UDRE, it seems to me quite possible that
TXC hasn't happened yet by the time you (try to) clear it.

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

I havn't done a terrible amount of programming, but the way I see it, getting a TXC when you don't want is a problem. I suppose there's nothing I can do about EMI and such, but while its sitting on my desk in a controlled environment this shouldn't happen. Maybe I'm just being pompous though...

By the way, if I disable my bootloader reset vector fuse I don't get garbage from the UART.

I am willing to accept that the way I currently have it is the proper way, but I really do not want to accept that I have to live with the fact that my interrupt routine fires when I enter my application. If I keep doing this, I'll just allow more and more bugs to enter the code.

When I get to work tomorrow I'll add in a for loop at the end of my uart1 transmit routine.

Maybe I'll also make a new project and manually send data, then turn on interrupts and see what happens. Might be easier to control and play with that way. Maybe I'll also find out that I've got more bugs than my pompous self cares to admit :)

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

If this first-time (one-time) TXC interrupt is really a concern, the fix would
be to change your bootloader to explicitly spin-wait for the final TXC
condition, then explicitly clear it Before calling into the application. That
way, it is true, the application would see the same behavior with or
without the boot loader.

My suggestion (though I don't know the details of your application) is that
getting a TXC interrupt when your output buffer is empty is the same
whether it's because (a) the buffer is initially empty (b) you just sent
the last byte in the buffer or (c) a cosmic ray caused the TXC indicator
to hiccup -- in all cases the ISR takes the same action (actually INaction).
The "robustness argument" says: if the anomaly doesn't make a
difference in the program's action, why worry about it?

Just out of curiosity, why are you using TXC rather than UDRE? Both
work, of course, but unless you Really need to know that the Tx line is
free, using UDRE gets you that bit of extra buffering for the same amount
of work.

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

I'm using TXC instead of UDRE simply because when I did my senior design project about a year or so ago, thats the interrupt I used and I pretty much just recycled the code. That and I don't like the idea of turning off hardware to get it to shut up :) If I were minizing power consumption though I might do it that way

Quote:
explicitly spin-wait for the final TXC condition, then explicitly clear it Before calling into the application

I did have an extremly excessive for i = 0 to 60000 loop just wasting time, but I did not combine that with clearing the interrupt. Like I said, I havn't done a terrible amount of programming, and I'm still leanring. Which is one of the reasons I want this to be as perfect as possible. Learn now and get it right, and not learn now and patch it later :)