[TUT] [SOFT] Using the USART - Interrupt driven serial comms

Last post
324 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For an updated version of this tutorial in PDF format, please see this page of my website.

Interrupt driven USARTs

The following is a short extension of my previous tutorial, Using the USART - Serial communications. This tutorial will teach the basics for creating interrupt-driven USART communications. It assumes that the reader has both read and fully understood my previous tutorial on basic serial communications.

Interrupts?

AVRs - and almost all microcontrollers - contain a feature known as interrupts. Interrupts, as their name implies, allows for external events (such as inputs from the user or AVR peripheral) to momentarily pause the main microcontroller program and execute an "Interrupt Service Routine" (shorthand "ISR") before resuming the main program where it left off. Interrupts are extremely useful for dealing with irregular input (such as pin changes or the arrival of a serial byte), as well as for processing "background tasks" like togling a LED each time a timer overflows.

In this tutorial, we are going to make use of the AVR's USART peripheral interrupts.

A recap, our echo program

From the last serial tutorial, we have created a simple program from scratch which will echo bytes received on the AVR's USART interface. The full program listing is as follows:

#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main (void)
{
   char ReceivedByte;

   UCSRB = (1 << RXEN) | (1 << TXEN);   // Turn on the transmission and reception circuitry
   UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes

   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

   for (;;) // Loop forever
   {
      while ((UCSRA & (1 << RXC)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
      ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"

      while ((UCSRA & (1 << UDRE)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
      UDR = ReceivedByte; // Echo back the received byte back to the computer
   }   
}

Readers should be able to fully understand this code - if you cannot please re-read the previous tutorial on basic serial communication.

Now, we want to extend this code so that the serial data is echoed back when received in an interrupt, rather than our main program loop. To do this, first we need to include the AVRLIBC standard library header, "avr/interrupt.h". This file contains library functions and macros which relate to the interrupt functionality of the AVR. We'll add this to the top of our code, below the "avr/io.h" header include:

#include 
#include 

Once included, we now have a way to make our ISR to deal with the serial reception. To make an ISR, we use the syntax:

ISR({Vector Name})
{
	// Code to be executed when ISR fires
}

And place it in our program as if it was a normal function. To add one to deal with the reception of a byte via the USART, we need to look for the appropriate name in our AVR's datasheet. In the datasheet for our example AVR, the MEGA16, we see that the name of the interrupt for when a byte is received is "USART_RXC". The standard AVRLIBC library file "avr/io.h" - included in our program as well as any other AVR-GCC program involving the AVR's IO functionality - defines the vector names for us.

AVRLIBC's symbolic names for each of the interrupt vectors is identical to the datasheet, with the addition of a "_vect" suffix to denote that it is a vector name. So, as our datasheet listed "USART_RXC" as the vector name, the syntax for our program is:

ISR(USART_RXC_vect)
{
	// Code to be executed when the USART receives a byte here
}

Which we'll place at the end of our program, after our main function. The new program looks like this:

#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main (void)
{
   char ReceivedByte;

   UCSRB = (1 << RXEN) | (1 << TXEN);   // Turn on the transmission and reception circuitry
   UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes

   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

   for (;;) // Loop forever
   {
      while ((UCSRA & (1 << RXC)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
      ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"

      while ((UCSRA & (1 << UDRE)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
      UDR = ReceivedByte; // Echo back the received byte back to the computer
   }   
}

ISR(USART_RXC_vect)
{
	// Code to be executed when the USART receives a byte here
}

Populating the ISR

At the moment our new USART reception ISR doesn't actually do anything - we've just defined it. We want it to echo back the byte that is sent, so we'll move our main loop code:

while ((UCSRA & (1 << RXC)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"

while ((UCSRA & (1 << UDRE)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
UDR = ReceivedByte; // Echo back the received byte back to the computer

Over to it. However, we can now remove the two while loops - since the ISR only fires when a byte is received, and only one byte is sent after each reception we can guarantee that both checks are now redundant. When the ISR fires we know that there is both a byte received in the USART input buffer, as well as nothing in the output buffer. Using this knowledge, we can simplify our ISR code to the following:

ISR(USART_RXC_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
   UDR = ReceivedByte; // Echo back the received byte back to the computer
}

Note that I've also moved the variable declaration of "ReceivedByte" over to the ISR, as that is now where it is actually used.

It's worth mentioning at this point a small section on the datasheet about the RXC interrupt:

Quote:
When interrupt-driven data reception is used, the receive complete routine must read the received data from UDR in order to clear the RXC Flag, otherwise a new interrupt will occur once the interrupt routine terminates.

That's important to remember - if you are using the RXC interrupt, you must read a byte from the UDR register to clear the interrupt flag. We do that in our above code, but keep it in mind for your future projects!

Enabling the USART receive interrupt

Let's take a look at the latest incarnation of our test code:

#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main (void)
{
   UCSRB = (1 << RXEN) | (1 << TXEN);   // Turn on the transmission and reception circuitry
   UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes

   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

   for (;;) // Loop forever
   {
         // Do nothing - echoing is handled by the ISR instead of in the main loop
   }   
}

ISR(USART_RXC_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
   UDR = ReceivedByte; // Echo back the received byte back to the computer
}

If you compile and run this, you'll notice that nothing happens - no characters are echoed back to the PC. This is because although we've defined and populated the ISR, we haven't enabled it. To do so, we need to do two things:

    1) Turn on global interrupts 2) Enable the USART Byte Received interrupt

Item one is simple, so we'll do that first. The AVR microcontrollers contain a global flag which can be set or cleared to enable or disable the handling of interrupts. Note that setting this flag doesn't enable all interrupts, it only allows for the possibility of running them. If the Global Interrupt Enable flag is disabled, all interrupts will be ignored, even if they are enabled (more on that later).

To turn on the Global Interrupt Enable flag, we can use the macro "sei()" which the "avr/interrupt.h" library helpfully defines for us. This is so named as it generates a "SEI" assembly instruction in the final code listing, which the AVR interprets as an order to set the Global Interrupt Enable flag. The compliment of "sei()" is "cli()" (to turn off the handling of interrupts) however we will not be using that macro in this tutorial.

We'll add our "sei();" instruction to our main routine, after configuring the USART registers:

   // ...
   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

   sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed

   for (;;) // Loop forever
   // ...

Now for item 2 on our list, which needs to be performed before the interrupt will be enabled. We need to specifically enable the USART Receive Complete interrupt, which we can do by setting the appropriate flag in the USART control register.

In the MEGA16, this bit is called RXCIE (Recieve Complete Interrupt Enable) and is part of UCSRB. Setting this bit enables the handling of the USART_RXC event vector:

UCSRB |= (1 << RXCIE);

We'll add this to our main routine, before our new "sei();" command.

Putting it all together

Now we have a working interrupt driven serial example:

#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main (void)
{
   UCSRB = (1 << RXEN) | (1 << TXEN);   // Turn on the transmission and reception circuitry
   UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes

   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

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

   for (;;) // Loop forever
   {
         // Do nothing - echoing is handled by the ISR instead of in the main loop
   }   
}

ISR(USART_RXC_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
   UDR = ReceivedByte; // Echo back the received byte back to the computer
}

Which, like out original program, will echo characters received via the USART. However, because our program is now interrupt driven, we can add in code into the main loop which will be executed when data is not received - such as flashing a LED.

Interrupts allow for infrequent "background" tasks to be executed when they occur, without posing a run-time penalty of having to poll the hardware until the even occurs. This frees up our main loop to take care of the critical code, with the interrupt code pausing the main code to execute when the event of interest ocurs.

Interrupts should be made to be as short as possible in execution time. This is because while one ISR is executing, others are blocked and thus if another ISR condition occurs while one ISR is executing, that ISR event will be missed or delayed.

Because of this, communication ISRs are generally made short by receiving in characters via an ISR, and placing them into a buffer which the main code may read at its leisure. This ensures that received data will not be missed, while giving the main program time to complete what it is currently doing before having to process the input. Similarly, long transmissions may be made by placing the data to be sent into a buffer, and have an interrupt send the data as the hardware is ready while the main program performs other tasks.

Please note that the names of the registers and bits for the UART(s) in the AVR you are using may be different from those shown above. In place of UDR, UBRRH, UCSRA etc. you may find they are called UDR0 (or UDR1), UBRR0H, UCSR0A and so on.

I hope this tutorial is informative - if not please post your suggestions! I'm open to feedback, and please feel free to post your own examples, code, etc. below.

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Hi Dean, thanks for the great tutorial. One thing - I think the line:

   UCSRB |= (1 << RCXIE); // Enable the USART Recieve Complete interrupt (USART_RXC) 

actually needs to use RXCIE rather than RCXIE. I have this running and it's working great in hyperterminal.

A quick question: Do you know a way to do a 'full speed' loopback test in windows? Typing isn't really stressing it at all. I'm using Artur Lipowski's USART library in a motor controller project and I'm losing the occasional character (with a 0% error clock frequency). I'd like to blast a large text file through the loopback, capture it when it gets back to the pc and then do a file compare to test for differences. ie if the avr is doing nothing but passing the character back and there are still errors I can narrow it down to some problems on my board.

Thanks again for the tutorial, it's very timely. I've been fighting with the usart for a couple of days now. much appreciated.

Update: I went away and checked and hyperterminal has a "paste to host" option that allows you to paste in a large block of text. I turned off local echo and posted in a large source file. Although this method doesn't allow an actual file compare, inspection showed that the AVR returned the text file completely intact. I guess this means there is some problem with my motor control code fighting with the code to handle the USART, although I'm not sure how.

Anyway, thanks once again for your great tutorial. I'll look at implementing a simple circular buffer based off your code and swapping my existing handler out.

Cheers
Andrew

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

Connect your TxD pin to your RxD pin.

Smiley

FREE TUTORIAL: 'Quick Start Guide for Using the WinAVR C Compiler with ATMEL's AVR Butterfly' AVAILABLE AT: http://www.smileymicros.com

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

Thanks for the thought, Smiley. But I don't think that's what I need - I was sorta using the avr for the loopback, just sending stuff to the chip and trying to measure if I got exactly the same stuff back.

Actually I think I understand what's happening now.

The UART code works fine in a project that does nothing but send/recieve characters, but if I include it in the motor controller (with 5 servo motors) it drops characters.

BUT (and here's the n00b thing to watch) if the motors are disconnected there's no problem and no dropped characters. So it appears to be the motors drawing current that are causing the problems. (Typical - I'm a software guy so I think in terms of software problems. I'll just go and beat myself with a stick now. :-/ )

I've seen many schematics that put a capacitor between Supply and ground probably for this very reason. Please pardon my ignorance. Perhaps if someone could confirm I'm guessing right, and verify what to do about it it'd be great, and thanks again.

Andrew

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

"reverse" diodes on the motor lines (4 in total per motor), a 0.1uF ceramic cap as close as possible to the AVR power pins. this should minimize the motors effect on the circuit. power isolation would be the best (with optical isolation on the control signal) but i doubt you need that much of "protection".
You could also add a capacitor triangle on the motor leads (if using DC motor): cap between leads, lead1 to case, lead2 to case. also ceramic.

very OT although ;) maybe could be moved to another thread in OT or something...

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

Thank you very much for your help (although I profess to not completely understanding what you said i can research it and work it out now you have given me a direction.) :)

Apologies if this is off topic. I considered before posting here, but thought - if the beginner is trying to use the USART and it's dropping characters then it'd be worth highlighting this as possible cause to consider. Once again, apologies and let me know if I should delete my posts here, and thanks again.

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

Quote:
actually needs to use RXCIE rather than RCXIE. I have this running and it's working great in hyperterminal.

Whoops! I wrote the above in response to your post, since you were having trouble (and I thought I could help others at the same time). Should have checked the datasheet more thoroughly. I'll fix that ASAP.

Sorry about the lack of polish on this one - I only had an hour in which to write it. So long as I get my main point across I suppose it's a passable tutorial ;).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Great tutorial. It will definitely come in handy. Thanks! I have a question though I am not entirely sure I should post it here, but I'll do so anyway. (If you want me to delete it and repost somewhere else, just ask!)

Your code works wonders if I only recieve one byte (one character) of data. But what happens if I want to recieve, let's say, 5 consecutive bytes? This probably just requires some smart C-code, but it I believe it is somewhat connected to how the AVR works too. Remember that I am a novice, but I still hope my question makes sense. Let me explain.

Let's say I want to send 5 bytes of data. Now as I see it, if I have multiple interrupts enabled in my program, they will stack them and do them one after another in the order they happened? Is that correct? So let's say that I send a byte but I am currently in another interrupt, that is when a character is sent I will not directly go to ISR(USART_RXC_vect) since another interrupt is currently beeing processed. Let's say this interrupt also takes some time. What will happen then? Will the sender just write 5 characters really fast and thus not let the AVR get a chance to read the bytes one by one. ie will only the last byte sent be read when the AVR finally goes into the ISR(USART_RXC_vect) interrupt function? Will only the last byte be in the UDR?

What I am basically asking is will the AVR somehow recieve bytes into a certain buffer and not send them out to the UDR register until it has been read when receiving data? Or do I have somehow have to use the RTS/CTS flags in the RS232 protocol? (even though I have no clue how that would work anyway with this particual problem)

Thanks for any answers I might get.

Edit:
I also found a very nonsignificant error heh.

char ReceivedByte; 
   ReceivedByte = UDR; // Fetch the recieved byte value into the variable "ByteReceived" 

should probably be:

char ReceivedByte; 
   ReceivedByte = UDR; // Fetch the recieved byte value into the variable "ReceivedByte" 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No they will not be stacked like you said. when you are in a interrupt routine all other interrupts are disabled. so you should get out if it as fast as possible.

I'll see if I can find my code on receiving more than one byte...

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

The UDR register is double buffered, I think, so you have have two pending characters in it at a time (reading one then frees up that place for the next character).

If one interrupt is executing, other interrupts that are pending will have have to wait until the current interrupt is complete. It's possible to make interrupts that are themselves interruptible, but that's an advanced topic and certain to cause problems if you don't know what you are doing.

To receive blocks of characters, you need to stuff them into a buffer inside the interrupt (to keep it short), then read those characters out later in your main routine to process them. This is commonly done in the form of a "Ring Buffer", a special type of circular buffer. You might want to Google that to find some example code (or I could PM some to you).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Thanks for the informative answer Dean. I just have one followupquestion. bloody-orc said all other interrupts are disabled. I read that as they won't happen at all if I'm currently in another interrupt. When I read your post I read it as they will indeed stack and happen one after another, in the order they happened? Which one of thoose two statements are correct?

Also, I'd love some example code! Thanks in advance.

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

The interrupts semi-stack. What happens when an interrupt is entered, is that the global interrupt flag (allowing for the processing of pending interrupts) is disabled. Interrupt conditions can still cause other interrupt's flags to be set, however the ISRs won't fire until the currently executing ISR ends (and re-enables the global interrupt enable flag). The order of which the pending interrupts are processed once the ISR exits is dependent on the Interrupt's priority - its order in the vector table.

Each interrupt can only have its flag set once, so if two Pin Change interrupts occur (for example) while you are processing the LCD interrupt, the Pin Change ISR will only fire once when the LCD ISR completes.

Make sense?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Hi together
I have a similar problem but in the opposite way.
I have to dend data with a baudrate of 38400 over the rs232 to an other software.
I tried to make an interrupt on the "USART_TXC_vect" that looks like this.

(main)
sei();
UCSRB |= (1 << RXEN) | (1 << TXEN) ; // Turn on the transmission and reception circuitry


(Timerroutine)
ISR(SIG_OVERFLOW1) 
{
...
program
...
after initialisation the software
UCSRB |= (1 << TXCIE); // Enable the USART Transmit Complete interrupt (USART_TXC)
...
(end timerroutine)

//Interruptroutine
ISR(USART_TXC_vect) 
{ 
     sensor= ((ADCH << 8 ) | ADCL);//read value out of ADC
     UDR = ((sensor<< 6)>>4)| 0x01; //send value to software
}

I tried this but it doesn't work.
Is this generally posible like this?
(I have to send with this baudrate, but parallel to sending data I've to controll the display and keys of my device(this works) in my program.)
Thanks for some help.
Stefan
(I've an atmega32)

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

Please use the forum [code ] [ /code] tags (without the spaces) when posting code - it keeps the formatting and makes it easier to read. I've fixed your above post, but the indentation was eaten by the board.

You don't seem to have set up the ADC in your code. You need to do that before the ADCH/ADCL register values become valid.

Also, "sensor" isn't defined in your code. Where is it defined and what is it defined as?

The shifting and ORing of the two ADC registers creates a 16-bit value (of which only 10 bits are used), which is placed in your "Sensor" variable. However, the UDR register is only 8-bit, so you'll be loosing bits when you send them. You seem to have some weird shifting going on:

UDR = ((sensor<< 6)>>4)| 0x01; //send value to software

Equates to:

UDR = ((sensor << 2)| 0x01); //send value to software

Which means you're shifting the 10 bits to the left twice, then ORing with 0x01. That will cause bit 0 to always be one, and bit 1 to always be read as 0. Is this what you want? Also, since UDR is only 8 bit as mentioned, that value will be cast down and the upper 8 bits thrown away. So what you're actually sending is the lower 6 bits of the ADC register pair shifted left twice, and the LSB set.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

One other thing to keep in mind is that there are actually two different interrupt vectors associated with the USART transmitter:
1) A "UART Data Register Empty" (UDRE) interrupt will fire continually as long as there's space in the double-buffered UDR for additional characters to be added to the hardware queue.

2) A "UART Transmit Complete" (TXC) interrupt will fire once following the completion of the final queued character.

If you design an application based solely on the TXC interrupt -- as you've done above -- you must "prime the pump" by sending the first character of a multi-byte transaction manually, and then the TXC interrupt will eventually fire when that byte is totally finished and the UART is sitting idle waiting for some more data.

I think it's easier to write software based on the UDRE interrupt instead. All you need to do in that case is populate a software buffer, then enable the UDRE interrupt source -- the hardware will take care of the rest.

In some circumstances it is appropriate to use both forms of transmitter interrupts in tandem -- for example when you don't want to have to bother with "priming the pump" so the UDRE interrupt is more convenient, but you also need to take a special action (such as switching the data direction of an RS485 transceiver) after you're certain that the final byte of a transaction has cleared the UART.

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

Sorry, perhaps i was a bit imprecise.
It works with the ADC, i didn't wrote de code above to save time and don't confuse you ;-)...
I "only" have problems to get the baudrate, so that every circle has a new "Sensor"-value.
So I tried to make an interrupt with the TXCIE-Bit to know when it's ok to fill in the data to UDR (when the UDR is empty).
Or to say it in another way: when TXE-Interrupt comes, i use to write the sensor-data to UDR.
I hope you got what i mean. it's a little bit complicated to explain.

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

Thanks Dean. It makes perfect sense. It stacks the interrupts by priority. So if a priority interrupt flag of priority 1 is set, it goes is put at the top of the stack and fires as soon as the current ISR is finished.

Also, I am fairly certain I could create the "ring buffer" code myself, but I'd be more than happy if you could provide me with an example through a PM. It'd save me time. Thanks again for the wonderful and detailed explanations.

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

hey this code not working when i tried to complie this code i got an warning that
../SMS_Controller.c:135: warning: 'USART_RXC_vect' appears to be a misspelled signal handler

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

You may be using an old version of WinAVR (actually, an old version of avrlib-c which is included in the package). The "*_vect" names were added in a recent release and the old "SIG_*" names deprecated - an up-to-date version is in the current WinAVR 20070122.

If you don't want to upgrade, try the old name "SIG_USART_RECV" (or "SIG_UART_RECV" depending on your chosen AVR model).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I am using the latest verion of WinAVR 20070122 and my avr is atmega8535 still i m getting this warining what could be reason behind this??

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

For the MEGA8515, the datasheet says that the vector is named "USART_RX_vect". "SIG_UART_RECV" should have also worked (despite being deprecated) - what error did you get when you used that?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I'm also having the same "warning: 'USART_RXC_vect' appears to be a misspelled signal handler" messages, however after I replace it to SIG_USART_RECV the warning message disappear, 0 warning message :D

but nothing is echoed back, nothing is shown in my hyperterminal windows, I used the same circuitry from 1st usart tutorial.
while the result from 1st tutorial are somewhat ok, this times it's far from ok :( :cry:

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

coolleo,

Sounds like your avr-gcc and avr-libc is out of date then. As you are using Windows I guess you are also using WinAVR - the latest is WinAVR20070525

 

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

Hi. I tried using the code posted in the tutorial. However, the microcontroller seems to be hanging once the interrupt occurs. I dont get back the sent data. Also, the background programme(a blinking LED) also stops the moment I transmit a byte from the computer.

I am using atmega16 with 1.8432 Mhz crystal. Normal usart works fine.Could you tell me what was going wrong?

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

Hey Dean,
Any plans to expand this to include interrupt driven transmition?
After reading lfmorisson's post in this thread I was thinking that the best way to do buffered transmition would be to have a function to queue data like

if (buffer empty interrupt off)
{
  enable buffer empty interrupt
  place first byte in buffer to send
  place the rest in the queue
}
else
{
  place everything in the queue
}

and then have the following in my buffer empty interrupt...

if (more stuff queued)
{
  place next byte in buffer
}
else
{
  disable buffer empty interrupt
}

This would allow my main code to actually go to sleep for periods at a time instead of constantly being awake if I just enabled the buffer empty interrupt and let it go all the time. Sound good?
I'm just worried that an interrupt may fire after doing the check in the queuing function, and turns off the interrupt if the queue is empty. Should I do a cli() just before the check and sei() after the end of the else statement?

Edward

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

I have the same query as above, by Edward (futrtrubl). The USARTs on my mega164p have a similar issue while receiving constant data at 4800 baud. I have ended up polling for flags ("loop_until_bit _is_set") on both Tx and Rx ends and miss a fair bit of characters (I still do that on the transmission end as you can see in the code). For starters, I'd atleast like an interrupt driven procedure for the incoming data on the reception end; but so far the service routine seems to miss out when it comes to execution.


static int uart_putchar( unsigned char cg , FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

static int uart_putchar(unsigned char cg, FILE *stream)
{
   if (cg == '\n')
        uart_putchar('\r', stream);
      loop_until_bit_is_set(UCSR0A, UDRE0);
      UDR0 = cg; // to terminal window at 1200baud.
      return 0;
}
void main(void)	
{

	init_devices(); // call to various initializing function. 
	PORTA = 0x01;
	
	for(;;)
	{
		if (newintFlag)
		{
			stdout = &mystdout;
			printf("%c\n",uart_rxData);
                        // printf("HelloWorld\n");
		}
	}
}

ISR(USART1_RX_vect)
{	
  uart_rxData=UDR1; // this variable is a volatile unsigned char 
  newintFlag = 1; // volatile static int

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

sorry , I missed a line while cleaning up the code.
I set the volatile variable newintFlag =0; in the if statement of my main().

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

I could expand it to include buffered transmission - it's very simple. The algorithm would be:

FUNCTION Main
  Enable TX Interrupt
  Enable Global Interrupts

  LOOP FOREVER
    {Do Stuff}
    Transmit_Data(Data)
    Sleep
  END LOOP
END FUNCTION

FUNCTION Transmit_Data(Data)
  IF (UDR NOT FULL)
    Store Data into RingBuffer
  ELSE
    UDR = Data
  END IF
END FUNCTION

ISR TX_ISR
  IF RingBuffer NOT EMPTY
    UDR = RingBuffer.Next
  END IF
END ISR

Fairly simple - you just need a buffer to hold the data before transmission. I've written just such a library - check out the "Advanced Configurable RingBuffer Library" entry on my Website here.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

The AVR actually has two different transmission-related interrupt mechanisms. The TX Complete inteerrupt fires after the last bit data has been clocked out of the UART and it is now sitting idle.

Alternatively, the TX Buffer Empty interrupt fires after the byte currently sitting in UDR as been moved into the shift register and it's safe to write a new byte to UDR for future transmission. This mechanism is possible because the UART't transmission system is actually buffered -- there are physically two separate registers, one of them is the shift register which holds the byte currently being clocked out, and the other is a holding register which can be filled ahead of time to ensure that there's always a byte queued up ready to automatically start transmission as soon as the transmitter is ready for it. This eliminates the idle period between the end of one byte and the start of the next.

Using the TX Buffer Empty interrupt can shave a few microseconds of idle time off of the transaction, potentially speeding up large bulk transmissions.

If Dean's algorithm doesn't meet your fancy, an alternative implementation would be:

FUNCTION Main 
  Enable Global Interrupts 

  LOOP FOREVER 
    {Do Stuff} 
    Transmit_Data(Data) 
    Sleep 
  END LOOP 
END FUNCTION 

FUNCTION Transmit_Data(Data) 
  Store Data into RingBuffer 
  Enable TX Buffer Empty Interrupt (instead of Dean's TX Complete Interrupt)
END FUNCTION 

ISR TX_BUFFER_EMPTY_ISR 
  IF RingBuffer NOT EMPTY 
    UDR = RingBuffer.Next 
  ELSE
    Disable TX Buffer Empty Interrupt
  END IF
END ISR 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Dean and lfmorrison. I think I will stick with the buffer empty interrupt route, as it simplifies my send function if I send more than one byte at a time, and shaving of idle time is always nice.

Edward

PS Dean, I think there could be a problem if the interrupt fires after you check that UDR is empty in Transmit_data() and there is no more data in the ring buffer. Data will be stuffed into the ring buffer but the tx interrupt will not fire again until another call to Transmit_data, at which point the data will be sent in the wrong order.

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

Dean:
I get the following error when attempting to implement the Ring Buffer Library:
../RingBuff.c:12: error: 'for' loop initial declaration used outside C99 mode

would you know what could cause that. I am actually trying to loop my transmitted data from one UART to the other via the ring buffer. However, I run into another problem when I initialize with both the RXC (rx_complete interrupt) and UDRE ( tx_buffer_empty interrupt) I fail to read anything ( on the output terminal). here is what I am trying to do:


RingBuff_t* DataBuff;
void main(void)	
{
	init_devices(); // Buffer_Initialize(DataBuff);

	for(;;)
	{
	}

}



//========= Rx Receive Flag Service Routine==================
ISR(_VECTOR(28)) //USART1_RX_vect
{
  Buffer_StoreElement(DataBuff, UDR1);
  UCSR0B |= (1 << UDRIE0) ; // enable output buff empty interrupt
 // uart_rxData = UDR1; 
 // newintFlag = 1;

}



ISR(_VECTOR(21)) //USART0_UDRE_vect
{
	if (!(Buffer_GetElement(DataBuff)))
	{
		// If Buffer Empty
		UCSR0B |= (0 << UDRIE0) ; // disable output buff empty interrupt	
	}
	else
	{
		UDR0 = 	Buffer_GetElement(DataBuff);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to configure your GCC installation to use the C99 standard. If you're using a makefile, change your -std line to:

CSTANDARD = -std=gnu99

If using AVRStudio as your frontend, change the standard via the project options screen.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

thanks mate. got it working.

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

Dean:
actually I have been unable to store the "unsigned char" datatype in the ring buffer. i set the BUFF_DATATYPE configuration variable (default: uint8_t) to unsigned char with no luck. could you point me in the right direction here.

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

Hi santafree,

Sorry! I just went over my code and it took me quite a long time to realize I made a silly boo-boo. Your buffers should not be pointers to a RingBuff_t and should be passed by address. I've updated the zip on my site - download that and check out the corrected example (also made a few minor code changes).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
You need to configure your GCC installation to use the C99 standard. If you're using a makefile, change your -std line to:

CSTANDARD = -std=gnu99

If using AVRStudio as your frontend, change the standard via the project options screen.

- Dean :twisted:

I have the same problem with the GCC config, I'm usign AVRStudio and I cannot see how to change the standard in the project options screen.. would it be beacuse of my version? I'm using ver 4.13.524... or how can I change to the C99 standard??

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

Project-configuration options-custom options

If a -std=???? already exists then select it and use [edit], pressing [add] when finished. Otherwise just type "-std=gnu99" into the box to the left of [add] and then press that same add button.

 

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

In AVRStudio:

Project Menu -> Configuration Options. Select the "Custom Options" tab, in the text box type "-std=gnu99" and press the "Add" button. Click "Ok" to close the window and it should work fine.

Also check out my other tutorial on configuring AVRStudio for optimal GCC projects, as there's a few other pointers in there.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

My program is not working, Help me please!!!

#include 
#include 
#include 
#include 
#include 

//Variables
#define Frec_Osc_Cristal 16934400  
#define Baud_Rate 250	
#define _U2X 1   //1 ó 0

//Definicion del UBRR para el Baud rate
#define _UBRRL ((Frec_Osc_Cristal/(Baud_Rate*(8*(2-_U2X))))-1)
int n;


void inicio(void)
{
	//Definicion del baud rate
	UBRRH = (_UBRRL>>8);
	UBRRL = _UBRRL;
	
	//8 bits|8 bits|2 stop bits|lecturaUCSRC pag 167
	UCSRC =(1 << UCSZ0 | 1 << UCSZ1 | 1<<USBS | 1<<URSEL);
	
	//habilit de interrup de Tx completada | habilit de interrup reg vacio|habilit tx
	UCSRB =(1 << TXCIE | 1 << UDRIE | 1<<TXEN);
	
	//si es uno doble el baud rate
	if (_U2X) UCSRA |= (1 << U2X);
	
	sei(); //Habilito las interrup globales

}


SIGNAL (SIG_UART_DATA) 
{ 
	n=0;
	while(n<=20)
	{
		n++;
		UDR = 0x01;
		PORTC =0xFF;
	}
}


SIGNAL (SIG_UART_TRANS) 
{ 
	PORTC =0x02;
}



void USART_Transmit( unsigned char data )
{
  while ( !( UCSRA & (1<<UDRE)) )
  UDR = data;
}



int main(void)
{
	inicio();
	//pin 0 y 1 de portc como salida
	DDRC=0xFF; 
 	
	
	while(1)
	{
		while(!(UCSRA & (1 << UDRE)))
		USART_Transmit(43);
		//PORTC =0x02;
		USART_Transmit(0xAA);
		PORTC =~ PORTC;
		n=0;
		
		while(n <= 0xFFF)
			n++;
	}
}

I don´t know that is my error

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

"not working" ???

(BTW SIGNAL() is deprecated - use ISR() these days)

 

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

clawson wrote:
"not working" ???

(BTW SIGNAL() is deprecated - use ISR() these days)

The led in portc no turn on, because USART_TXC_vect interrupt no working

#include 
#include 
#include 
#include 
#include 

//Variables
#define Frec_Osc_Cristal 16934400  
#define Baud_Rate 250	
#define _U2X 1   //1 ó 0

//Definicion del UBRR para el Baud rate
#define _UBRRL ((Frec_Osc_Cristal/(Baud_Rate*(8*(2-_U2X))))-1)
volatile int m=0;
int n;

void inicio(void)
{
	cli();
	
	//pin 0 y 1 de portc como salida
	DDRC=0xFF; 
	
	//Definicion del baud rate
	UBRRH = (_UBRRL>>8);
	UBRRL = _UBRRL;
	
	//8 bits|8 bits|2 stop bits|lecturaUCSRC pag 167
	UCSRC =(1 << UCSZ0 | 1 << UCSZ1 | 1<<USBS | 1<<URSEL);
	
	//habilit de interrup de Tx completada | habilit de interrup reg vacio|habilit tx
	UCSRB =(1 << TXCIE | 1 << UDRIE | 1<<TXEN);
	
	//si es uno doble el baud rate
	if (_U2X) UCSRA |= (1 << U2X);
	
	sei(); //Habilito las interrup globales

}

ISR(USART_UDRE_vect)  

{ 
	m++;
	if (m < 20)
	{
		UDR = 0x01;
		
	}
	else
	{
		m=0;
		PORTC=0x00;
	}
}


ISR(USART_TXC_vect) 
{ 
	PORTC =0xFF;
}



void USART_Transmit( unsigned char data )
{
  while ( !( UCSRA & (1<<UDRE)))
  UDR = data;
}

int main(void)
{
	inicio();
	while(!(UCSRA & (1 << UDRE)));
	USART_Transmit(43);
	
	while(1)
	{
	}
}

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

The TXC interrupt vector will only fire once after the final bit of a frame has been clocked out and there's nothing else buffered in UDR waiting for transmission.

Because of the way that your UDRE interrupt service routine is structured, there will *always* be something waiting in the UDR buffer. (Remember, the UDRE interrupt fires continually as long as there is space left in the UDR buffer.) Therefore, the TXC interrupt will *never* have a chance to fire.

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

With respect, as you would modify the program so that each 20 data (UDRE interrupt´s) the interruption TXC fire?

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

Well, that sort of depends, because I'm not convinced of exactly what you actually want that software to do.

You've got a call to UART_Transmit() in the mainline which will be competing with the constantly-firing UDRE interrupts for writing to UDR.

It's impossible for me to judge exactly what you actually intended to accomplish with the mainline's UART_Transmit() call, because the UDRE interrupts will already have started start firing, stuffing 0x01's through the UART, as soon as the last line of code in inicio() finishes executing.

I'd prefer to see a more complete process specification before I commented on how to structre any changes.

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

Just wondering if I can expect to be able to reliably test a Max232 chip on a breadboard? I have a VB6 program that is polling an AVR regularly and displaying the results on screen. Through the spare RS232 on the STK500 it works very well. But when I try to use a max232 chip I have set up on a breadboard using the circuit here the communications is not so good. It keeps losing connection as I am not getting a return from every request for data. I just want to make sure my circuit is OK before I make a pcb.

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

Taipan,

But you don't show the clock source to the AVR on your own circuit. Reliable comms generally means reliable clock source (and vice versa). Also do you have a scope and can look at the TX/RX signals for timing and any noise etc.

 

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

clawson wrote:
Taipan,

But you don't show the clock source to the AVR on your own circuit. Reliable comms generally means reliable clock source (and vice versa). Also do you have a scope and can look at the TX/RX signals for timing and any noise etc.


I have tried it 2 different ways, firstly with an Atmega16 along with a 7.3728 crystal and the Max232 on the breadboard, and secondly using the AVR on the STK500 and just routing the TXD/RXD to the Max232 on the breadboard. Both ways give about the same result.

Using Bray's Terminal I just have to send a single ascii character to get a return from the AVR. If I use the Spare RS232 on the STK500 I can hold down the key sending a continual stream of characters and get 100% return from the AVR. When I use the Max232 on the breadboard I am getting about an 80% return.

I do have a scope, but I don't know a lot about using it. I'll get it out tomorrow and give it a try. Thanks for the help.

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

I found my circuit worked OK at 9600 baud but I was initially trying to use 115200. At that speed some of the sent characters were arriving corrupted.

I forgot to say thanks for the great tutorial. It certainly made things easy for a beginner like myself.

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

By looking at iom8535.h, the recognized vector is USART_RX_vect. This is inconsistent with the datasheet, but USART_RX_vect (without the 'C') is copied from AVR Studio's partdescriptionfiles. I ran into the same problem with atmega8515, where the vectors are defined to be UART instead of USART. We maybe in a situation where GCC would say it is a problem originated from Atmel, and Atmel would say the partdescriptionfiles are only meant for AVR studio.

#define USART_RX_vect _VECTOR(11)

anilsoni85 wrote:
I am using the latest verion of WinAVR 20070122 and my avr is atmega8535 still i m getting this warining what could be reason behind this??

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

Dean,
Thanks for posting such a clear tutorial! It is immensely useful in getting started with the USART.
One suggestion to clarify the ISR use: Please include a line mentioning how the Recieve Complete Interrupt flag clears (from the ATmega16 datasheet):

Quote:
When interrupt-driven data reception is used, the receive complete routine must read the received data from UDR in order to clear the RXC Flag, otherwise a new interrupt will occur once the interrupt routine terminates.

This is similar to the explanation you gave in your timers tutorial for the OCF1A flag:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106
In fact, had I not read that very useful tutorial, I wouldn't have known where to look in the datasheet to understand how the interrupt flag clears.
Again, thanks for adding such useful content to the forum. Keep 'em coming!!

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

Hi reg2117,

Thanks for the praise and comment. That's a good idea - I've added the datasheet quote to the tutorial. Thanks for the feedback!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

Pages