Reading a multi-char string in from USART using interrupts

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

Does anyone have a suggestion on how to read in a string using the USART and interrupts? I am able to get the RX USART to work with the RX interrupt and I can also read in ONE character at a time. I want to be able to read in a string (multiple characters, i.e. char array) using interrupts. I am not proficient at C and I am a little unclear as to how to do this.

Since I am expecting multiple characters from the USART, must I disable the RXCIEn when the interrupt is first detected and enable after all characters are read?

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

No, you get an interrupt when each character arrives. Store them into a buffer or something.

- Jani

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

Your interrupt should just read a single character from the UART and put it into a global character array (and likely set some kind of flag to be used in the main loop). Don't worry about disabling interrupts as when you hit the interrupt it will disable them all, let you service the single character and when you exit re-enable them.

If you only want to receive strings at a time, you'll have to designate an end-of-string character. The newline character is a commonly used one. Then in your interrupt routine all you do is receive the character, add it to the end of the global array and check if it's a newline character. If it is, set some global boolean to say there's a complete string ready for parsing. If it's not, then just exit and wait for the next interrupt.

You might want to add two arrays so that the interrupt routine can continue filling one with the next string while you wait for the main program to process the first string. Should only be required if your program takes long enough to process the string that there might be more characters coming in the UART before it's done.

Clancy _________________ Step 1: RTFM Step 2: RTFF (Forums) Step 3: RTFG (Google) Step 4: Post

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

If the strings are modem responses then the chances are they end '\r' or '\n' so also have the buffering stuff look out for those characters going by and when seen set a flag that the main() code can use to say "a complete 'sentence' has just arrived"

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

You might want to consider using the interrupt simply to copy *every* received character into a small buffer and updating a 'next write' pointer after each character. When you get to the end of the buffer you just start again at the beginning.

Your main program polls the buffer for new characters by comparing the 'next write' pointer with a 'next read' pointer that it updates after every read it makes. If the two pointers are the same there are no new characters to process.

Provided your main loop can remove the characters from the buffer faster than you can receive them, it won't stomp all over the previously received but unread data.

Your main program can extract the incoming data one character at a time and place it in a secondary buffer until it receives and end-of-line character - 0x0d or 0x0a.

This way the interrupt routine is kept very small and doesn't need to know anything about the state of the main routine; the main routine can poll the buffer for characters whenever it is expecting them.

Neil

Neil

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

I will be reading in groups of characters. There will not necessarily be a terminating character. I interrupt and grab the data as a 'chunk'. I can echo the chunk back out the USART TX.

I'm not sure I am doing things right. How is

char* inMsg;

different from

char inMsg[];

It looks like the [] allocates data space whereas * does not. It looks like when I use the [] and a pointer to increment, I can read in the chunk as mentioned. If I touch the char array (i.e. echo it out, perform strncmp, strcpy) it looks like it gets corrupted.

I also tried implementing the Example from:

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

but couldn't get it to work AT ALL. I did add in the appropriate port numbers (I am using an ATmega2560), include statements and implemented a init_uart function making sure to set up UBRR0H and UBRR0L from my other Frankenstein.

Can anyone offer info on handling char array strings or the proper way to do USART file I/O using a method similar to the above?

-Thanks-

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

Quote:
There will not necessarily be a terminating character
So how will your code know when to stop? Any string can only be utilised if it has a terminating character like a carriage return or what ever you decide. The other way is to have a fixed length string. There may be another way which is what telephones used to do and wait for a longish pause (a gap) between pulses which would indicate the end of the string.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

There is an excellent example of Serial Interrupt handler
contained in the ICCAVR C compiler example code. It is located in the examples.avr \M32 folder. The example was supplied by author: John Baraclough Date:2005-10-06

I don't know if you use ICCAVR or not. It can be download as a demo , there is a link at avrfreaks.net.

I don't know if it is a copyright issue or not. If you can't get it , perhaps someone can comment on the intellectual property rights for the ICCCAVR supplied files.

I will email ICC and see.

I'll believe corporations
are people when Texas executes one.

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

What do you mean with "string"? Usually it means a group of characters that are delimited in soma way. Do you mean stream of characters - continuous flow?

Typical USARTs can buffer upto about 64 characters, but most often the USARTs embedded within microcontrollers much less. ATmega169 can butter one finished and one unfinished character (that can't be read), so the characters must be read one by one. When ever a new character becomes ready the old one is lost.

If you want to use bigger buffer than your device has, you need to implement the buffering in SW - by using ring buffer (the working solution for streams).

Debugging is for sissies and delivery for surgeons. Real men do demonstration.

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

https://www.avrfreaks.net/index.p...

look up this thread, the queue.h and queue.c files shown there are a good way of handling your UART I/O.
you were right above, you need to use buffer[] to store your incoming and outgoing characters. chose something depending on the amount of ram you have available and the amount of data that's coming in and the frequency of your buffer processing routine. chose powers of two for sizes, 64,128 or 256 with the queue code, it works best.
as john pointed out, you need to know the size of your incoming string, or, you need to have some sort of character or character sequence that tells you that a new string is coming.
or be old school and use a break character :-D

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

attigeek wrote:
I'm not sure I am doing things right. How is

   char* inMsg;

different from

   char inMsg[];

It looks like the [] allocates data space whereas * does not. It looks like when I use the [] and a pointer to increment, I can read in the chunk as mentioned. If I touch the char array (i.e. echo it out, perform strncmp, strcpy) it looks like it gets corrupted.


Those two lines are identical, they both just define inMsg as a pointer to bytes of type char. Apart from the 16bits set aside to hold the pointer itself neither will allocate any storage for the received characters. If that's what you intended either put a number in the square brackets to say how many bytes you want to reserve or (a little more complicated and generally pointless on a micro such as an AVR) use:

   char* inMsg;
   inMsg = malloc(10);

to reserve 10 bytes of storage (just as an example) and have the inMsg pointer initialised to point at them.

Cliff

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

Don't know if you use ICCAVR the C compiler.The attached code demonstrates a way to receive/transmit serial data.

Attachment(s): 

I'll believe corporations
are people when Texas executes one.

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

Well, a break character (framing error) is not always easy to produce from the keyboard. Not all terminals support it any more.

If the buffer is filled in interrupt and read elsewhere, I'd stick to statically allocated memory - like:
#define IN_BUFFER_SIZE 64
volatile char in_buffer[IN_BUFFER_SIZE];

(the "volatile" so that it can be shared between the applivation and interrupt)

Debugging is for sissies and delivery for surgeons. Real men do demonstration.