Looking for ideas on circular buffers

Started by reotrb June 19, 2010
Hi folks,
I'm trying to find the best way of reading a stream of incoming characters on asynchronous USCIA0 port. The data rate is 9600BAUD, and the characters are NMEA strings from a GPS receiver. I've done some searching on Google and this message board, and it seems the best way of accomplishing this is with a circular buffer.

I've written some code that uses the DMA controller to read characters from the serial port receive register, and store them in a block of memory. When the block of memory is full, I check the DMA interrupt flag and begin parsing the NMEA string now stored in the memory block. The DMA controller is then reset to begin writing the next serial port byte to the same block of memory all over again.

My concern is this: how do I keep the DMA controller from over-writing the memory block before I've had a chance to begin reading it? It seems I would need the DMA to set the interrupt flag when half the memory block is full, so that the processor would have time to respond before the data was over-written.

Any ideas or suggestions on how to implement this would be greatly appreciated.

Regards,
Rob

Beginning Microcontrollers with the MSP430

On Sat, Jun 19, 2010 at 4:47 PM, reotrb wrote:

> Hi folks,
> I'm trying to find the best way of reading a stream of incoming characters
> on asynchronous USCIA0 port. The data rate is 9600BAUD, and the characters
> are NMEA strings from a GPS receiver. I've done some searching on Google and
> this message board, and it seems the best way of accomplishing this is with
> a circular buffer.
>
> I've written some code that uses the DMA controller to read characters from
> the serial port receive register, and store them in a block of memory. When
> the block of memory is full, I check the DMA interrupt flag and begin
> parsing the NMEA string now stored in the memory block. The DMA controller
> is then reset to begin writing the next serial port byte to the same block
> of memory all over again.
>
> My concern is this: how do I keep the DMA controller from over-writing the
> memory block before I've had a chance to begin reading it? It seems I would
> need the DMA to set the interrupt flag when half the memory block is full,
> so that the processor would have time to respond before the data was
> over-written.
>
> Any ideas or suggestions on how to implement this would be greatly
> appreciated.
>

IMO, forget about using the DMA, just use the RX data ISR to insert
bytes into the buffer.

Find out how long your NMEA parser takes, factor in any other
things that can happen simultaneously, add a safety factor, then
that will tell you how much space you must leave in the buffer
when calling the parser.

Your ISR will insert data at the tail of the queue, your parser will
remove it from the head, and you can call the parser either when you
hit the high water mark described above, or if you know your NMEA
strings are x bytes long, you can call the parser when there are x
bytes in the buffer.

--
Andy


Hi Andy,
Thank you for your response. Would you mind clarifying you're reasoning for not using the DMA controller? I figured that the DMA controller would make my program more efficient, since the processor could sleep or do other things during the time it takes to fill the memory buffer.

Your suggestion would force the processor to execute the ISR every time a byte is received. Whereas using the DMA controller the processor would execute a subroutine only once the memory block is filled.

I'm not suggesting you're idea is a bad one, but just trying to better understand it since I am a relative newbie at this.

--- In m..., Andy Warner wrote:
>
> On Sat, Jun 19, 2010 at 4:47 PM, reotrb wrote:
>
> >
> >
> > Hi folks,
> > I'm trying to find the best way of reading a stream of incoming characters
> > on asynchronous USCIA0 port. The data rate is 9600BAUD, and the characters
> > are NMEA strings from a GPS receiver. I've done some searching on Google and
> > this message board, and it seems the best way of accomplishing this is with
> > a circular buffer.
> >
> > I've written some code that uses the DMA controller to read characters from
> > the serial port receive register, and store them in a block of memory. When
> > the block of memory is full, I check the DMA interrupt flag and begin
> > parsing the NMEA string now stored in the memory block. The DMA controller
> > is then reset to begin writing the next serial port byte to the same block
> > of memory all over again.
> >
> > My concern is this: how do I keep the DMA controller from over-writing the
> > memory block before I've had a chance to begin reading it? It seems I would
> > need the DMA to set the interrupt flag when half the memory block is full,
> > so that the processor would have time to respond before the data was
> > over-written.
> >
> > Any ideas or suggestions on how to implement this would be greatly
> > appreciated.
> > IMO, forget about using the DMA, just use the RX data ISR to insert
> bytes into the buffer.
>
> Find out how long your NMEA parser takes, factor in any other
> things that can happen simultaneously, add a safety factor, then
> that will tell you how much space you must leave in the buffer
> when calling the parser.
>
> Your ISR will insert data at the tail of the queue, your parser will
> remove it from the head, and you can call the parser either when you
> hit the high water mark described above, or if you know your NMEA
> strings are x bytes long, you can call the parser when there are x
> bytes in the buffer.
>
> --
> Andy
>
>

On Sat, Jun 19, 2010 at 5:36 PM, reotrb wrote:

> Hi Andy,
> Thank you for your response. Would you mind clarifying you're reasoning for
> not using the DMA controller? I figured that the DMA controller would make
> my program more efficient, since the processor could sleep or do other
> things during the time it takes to fill the memory buffer.
>
> Your suggestion would force the processor to execute the ISR every time a
> byte is received. Whereas using the DMA controller the processor would
> execute a subroutine only once the memory block is filled.
>
> I'm not suggesting you're idea is a bad one, but just trying to better
> understand it since I am a relative newbie at this.
>

You can still sleep between interrupts, I'm not convinced using the DMA
controller will save you much power - but I'll admit that I didn't analyse
it.

If you really want to use the DMA, then take the concept I described and do
it with DMA instead. Each time you program the DMA, figure out how many
bytes you can read before the buffer gets too full, then reprogram the DMA
and kick off the parser from the DMA ISR instead of the USART Rx ISR.

--
Andy


On most MSPs with DMA there are several DMA channels available, and some
allow more than one source/dest buffers. In the latter case alternate
between buffers, in the former case swap DMA channels and buffers on
entry to the DMA interrupt, so once one buffer is full your DMA
interrupt switches use to a second channel, which is configured with its
own buffer.

DMA is marginally faster than not, in most cases, as each transfer only
takes 2 clock cycles, but if you have written your code compactly using
a lot of low cycle count register operations you won't save much

Al

reotrb wrote:
> Hi Andy,
> Thank you for your response. Would you mind clarifying you're reasoning for not using the DMA controller? I figured that the DMA controller would make my program more efficient, since the processor could sleep or do other things during the time it takes to fill the memory buffer.
>
> Your suggestion would force the processor to execute the ISR every time a byte is received. Whereas using the DMA controller the processor would execute a subroutine only once the memory block is filled.
>
> I'm not suggesting you're idea is a bad one, but just trying to better understand it since I am a relative newbie at this.
>
> --- In m..., Andy Warner wrote:
>> On Sat, Jun 19, 2010 at 4:47 PM, reotrb wrote:
>>
>>>
>>> Hi folks,
>>> I'm trying to find the best way of reading a stream of incoming characters
>>> on asynchronous USCIA0 port. The data rate is 9600BAUD, and the characters
>>> are NMEA strings from a GPS receiver. I've done some searching on Google and
>>> this message board, and it seems the best way of accomplishing this is with
>>> a circular buffer.
>>>
>>> I've written some code that uses the DMA controller to read characters from
>>> the serial port receive register, and store them in a block of memory. When
>>> the block of memory is full, I check the DMA interrupt flag and begin
>>> parsing the NMEA string now stored in the memory block. The DMA controller
>>> is then reset to begin writing the next serial port byte to the same block
>>> of memory all over again.
>>>
>>> My concern is this: how do I keep the DMA controller from over-writing the
>>> memory block before I've had a chance to begin reading it? It seems I would
>>> need the DMA to set the interrupt flag when half the memory block is full,
>>> so that the processor would have time to respond before the data was
>>> over-written.
>>>
>>> Any ideas or suggestions on how to implement this would be greatly
>>> appreciated.
>>>
>> IMO, forget about using the DMA, just use the RX data ISR to insert
>> bytes into the buffer.
>>
>> Find out how long your NMEA parser takes, factor in any other
>> things that can happen simultaneously, add a safety factor, then
>> that will tell you how much space you must leave in the buffer
>> when calling the parser.
>>
>> Your ISR will insert data at the tail of the queue, your parser will
>> remove it from the head, and you can call the parser either when you
>> hit the high water mark described above, or if you know your NMEA
>> strings are x bytes long, you can call the parser when there are x
>> bytes in the buffer.
>>
>> --
>> Andy
>>
>>
>
On 19/06/2010 22:47, reotrb wrote:
> Hi folks,
> I'm trying to find the best way of reading a stream of incoming characters on asynchronous USCIA0 port. The data rate is 9600BAUD, and the characters are NMEA strings from a GPS receiver. I've done some searching on Google and this message board, and it seems the best way of accomplishing this is with a circular buffer.
>
> I've written some code that uses the DMA controller to read characters from the serial port receive register, and store them in a block of memory. When the block of memory is full, I check the DMA interrupt flag and begin parsing the NMEA string now stored in the memory block. The DMA controller is then reset to begin writing the next serial port byte to the same block of memory all over again.
>
> My concern is this: how do I keep the DMA controller from over-writing the memory block before I've had a chance to begin reading it? It seems I would need the DMA to set the interrupt flag when half the memory block is full, so that the processor would have time to respond before the data was over-written.
>
> Any ideas or suggestions on how to implement this would be greatly appreciated.

If you just want to get, say, a position, you don't need a circular
buffer. Just read the data until you get, say $GPMRC, and put the next
70 bytes into a buffer. Then extract the data you need.

Leon
--
Leon Heller
G1HSM
Leon Heller :

> If you just want to get, say, a position, you don't need a circular
> buffer. Just read the data until you get, say $GPMRC, and put the next
> 70 bytes into a buffer. Then extract the data you need.

However the length may vary. DMA is only useful with fixed length packets.
What he need is to read in the data line by line (separated by linefeed or
use $ as separator). Once the separator has been received the receiving has
to be stopped and/or switched to a second buffer. After that he can interpret
the NMEA string in the buffer (in the main loop) and enable the receiving
once the decoding has been finished. The DMA itself can not stop at a certain
character - this behaviour is possible only with a short interrupt routine.

M.