[TUT][SOFT] Interrupt Driven UART Receive Ring (FIFO) Buffer

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

Hi,

Introduction:

This is a very simple tutorial on creating a ring buffer for bytes received over UART. The files supplied in the zip archive are:

  • firmware/main.c
  • firmware/uart.h
  • firmware/uart.c
  • serial.xcodeproj

The firmware has been written for an ATmega8, flashed with a tuxgraphics STK500v2 (SMD) programmer in Mac OS X 10.6. If you have access to XCode (it's on your OS X DVD, only if you use Mac that is) you can open the .xcodeproj file. You can otherwise edit the files as you wish with your text editor/IDE of choice.

How it works...

This is the Interrupt Service Routine (ISR), and is only serviced whenever the USART hardware module receives a byte of data. The interrupt 'flag', i.e. the fact that the interrupt has taken place, is only reset once the byte held in the register 'UDR' is read.

ISR(USART_RXC_vect)
{
	while ( !(UCSRA & (1<<RXC)) );
	if (rxn==BUFFER_SIZE) // if BUFFER_SIZE is reached, reset to start of buffer.
		rxn=0;
	rx[rxn++] = UDR; // increment rxn and return new value.   
	rxFlag=1; // notify main of receipt of data.
	
	#ifdef DEBUG
		printf(...);
	#endif
}

'rx' is a char array of size 'BUFFER_SIZE'; this can be tweaked by definitions at the start of main.c to your suiting. Ultimately the size of the FIFO buffer is determined by the frequency at which bytes are to be most likely received over UART.

The '#ifdef DEBUG' line above is simply a pre-processor directive at compile time. This tells the C compiler to only include the lines of code between that and its ending '#endif' line if 'DEBUG' has been defined before. Due to this reason, you will find an entry at the very start of the main.c as

//#define DEBUG /* uncomment to enable debug information via UART */

This is a handy way for turning various levels of debugging code on and off, as required.

The UART Receive Complete Interrupt is enabled within ioinit() as follows,

	UCSRB |= (1<<RXCIE); // Enable the USART Recieve Complete interrupt (USART_RXC)	
	sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed 

sei() acts like a 'master' switch. As the name suggests, the Global Interrupt Enable needs to be on for any enabled interrupts to actually be enabled.

In the provided sample code, the while() loop within main.c simply prints out the entire buffer each time the RCX Interrupt is serviced.

	while(1) // Loop forever
	{
		if (rxFlag == 1) // process received data in buffer.
		{
			/*
			 Ideally, once data is read from the ring buffer,
			 the buffer should be updated to reflect that it
			 has been taken off it.  This is to ensure that no
			 data would be lost when the buffer counter resets 
			 on 'roll over'.
			 
			 Such functionality is pending implementation.  The
			 following loop simply returns the current contents
			 of the entire buffer, each time the rxFlag goes high.
			 */
			for (i=0;i

uart.c and uart.h

The code has been modularised for clarity and these routines can be used/altered as needed. It should be noted that printf() ultimately provides the same functionality as uart_putstring().

uart_putc() is an extra definition simply for use by printf(). Pick the method you prefer to save on ROM space.

Future Work

Of course, there is much room for improvement. For one, an entire byte has been used when a single bit would have sufficed. The routine within the while() loop needs to also remove any bytes read from the buffer, freeing up space; if not, when the buffer's array counter loops around, data within the buffer will be overwritten and lost.

The attachment has modularized the basic uart routines via uart.c and uart.h; I have for the moment gone with a temporary 'uart_putc' simply for use by printf(). The Makefile has been tweaked to perform a 'clean' first for 'make all'.

Thanks: Much thanks to Johan and Dean, and this great community. I've only just started working with AVR and hope I could be of some help (even in a limited capacity).

edit: An alternate method would be to ditch the byte 'RxFlag' and instead, simply monitor the 'size' of the receive buffer within main(). Once a sufficient amount of bytes have been captured, say 50 bytes for GPS NMEA string parsing, a copy of the buffer could be operated on for parsing the NMEA data.

The FIFO buffer can be neglected for the duration or the global interrupt may be disabled during this period. This is just one example for the use of a FIFO buffer, and is the raison d'etre for the development of this code sample.

WinAVR Users

You will need to use your own Makefile/IDE (what have you) to cross-compile the code. The supplied Makefile will work fine for Linux users as long as they update the port '-P' parameter and programmer options in it for flashing with avr-dude.

Attachment(s): 

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

Hello
I study your code.

But when i'm lauching the same on my atmega8, there's nothing until i send a char: after his rebooting continuously?
I don't understand this interrupt problem....

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

Quote:

there's nothing until i send a char: after his rebooting continuously?

That suggests the ISR() is not capturing the vector. When you built the code did you get a warning about the vector name possibly being mis-spelt?

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

Yes man...
But i was thinking it was the good name : i look in the avr-libc online manual....

If you know how i can avoid this, thanks :)

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

DO smobody know the good name for reception complete interruption??

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

Quote:

DO smobody know the good name for reception complete interruption

Aubrey?

(well OK, it's a nice name but the one given in the user manual usually works better)

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

Thanks clawson
But in the avrlibc it's

 USART_RXC_vect

Where can i found another onr??
I will see in my .../avr/lib headers...

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

Hello
In

 http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

I find the old name of this interrupt...

My programm seem to work with it, no more resets....
Why old name is ok.
Maybe i've got a old lib???

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

Oh Bsodmike
Nice to make a tuto, but with error it become an excellent tuto :
There is a problem in the incrementation of rxn, so your code doesn't wit for x char (x=buffer size).
So, it's work like this :



ISR(SIG_UART_RECV ) //USART_RXC_vect)
{
   while ( !(UCSRA & (1<<RXC)) );
	{
  	 if (rxn==BUFFER_SIZE) // if BUFFER_SIZE is reached, reset to start of buffer.
		{      
			rxn=0;
			rxFlag=1; // notify main of receipt of data.
		}
   	rx[rxn++] = UDR; // increment rxn and return new value.   
	}
}

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

My version of avr-libc, for the problem of old name :

 apt-cache policy avr-libc
avr-libc:
  Installé : 1:1.6.2.cvs20080610-2

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1

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

Quote:
so your code doesn't wait for x char (x=buffer size).

It is not supposed to wait for BUFFER_SIZE. The program should be notified immediately whenever any character is received.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

Maybe i've got a old lib???

WinAVR currently contains AVR-LibC 1.6.7

The March 09 version of Bingo's package on my website (.deb for Linux) contains 1.6.8: http://www.wrightflyer.co.uk/avr...

Which Linux distro are you using? Is it a derivative of Debian (such as Ubuntu or any of the other *untu's). Do you use .deb's or .rpms for repository packages. If you can use .deb's I'd HIGHLY recommend you pick up that .deb from my web site.

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

I'm using debian 5, 64 bits, and i add my libs with debs.
So i think i'm running 1.6.2.
I will look if i can upgrade...

Sorry for the ring. I didn't understand use of this example : i'm workin on receiving string on my avr ( order from the pc...)

Thanks everybody

computer : GNU Linux / e6750
ship : Attiny 2313 / ATmega8
compiler : avr-gcc 1:4.3.2-1