Reply by EventHelix.com April 11, 20072007-04-11
Steven Woody wrote:
> i am writting problem to receive data from a interrupt-driven serial > port, and i think my code will always have change of lossing data. > > in the receving ISR, i get a byte from a register and put it into a > buffer, > > static unsigned char buff[ BUFF_SIZE ]; > static buff_len = 0; > > __interrupt on_byte_in( void ) > { > if ( buffer_len < sizeof( buff ) ) > buffer[ buffer_len++ ] = the_byte_in_the_register; > } > > another function used to fetch the buffer data, > > flush_receiving_buff( void ) > { > for ( size_t i = 0; i < buff_len; ++i ) { > process the buff[ i ]; > buff_len = 0; ------------- (a) > } > > my question is that, during the execution of statement (a), one or > more new bytes might come in and code above will lost them. if you > folks see the problem, can you please suggest me a solution? i believe > the question is very basic and must have been well resolved. > > thanks in advance.
Consider adding a write index and a read index. The ISR increments the write index while the reading routine increments the read index. -- EventStudio 4.0 - http://www.EventHelix.com/EventStudio Model in Plain Text; Generate Sequence Diagrams in PDF/Word
Reply by rickman April 11, 20072007-04-11
On Apr 11, 2:05 pm, Michael Frederick <n...@email.address> wrote:
> On 4 Apr 2007 08:47:46 -0700, "Dave Pollum" <vze24...@verizon.net> > wrote: > > SNIP! > > > > >I've used double buffering with packet/protocol logic in the ISR, and > >that works well. However, if you don't want the packet logic in the > >ISR, you could still use double buffering. The ISR could mark a > >buffer as ready for the upper level code, and then store more > >characters in the _other_ buffer. Ready could be set if the buffer is > >full, or if a certain amount of time has passed and no new characters > >have been received. The ISR can save the numbers of bytes received in > >a variable associated with that buffer (use a struct - see below). > >After the upper level code has read all of the chars from the buffer, > >it as available. The ISR can then use that buffer again. Disabling > >and restoring interrupts aren't needed. This eliminates the need for > >both the ISR code and the upper level code to write to the count > >(buff_len) variable. > > > typedef struct { > > char nbr_bytes_read; /* 0..255 */ > > char OK_to_read_buffer; /* T/F */ > > char buffer[ BUFF_SZ ]; > > } buffer_stuff; > > buffer_stuff *buffer_1; > > buffer_stuff *buffer_2; > > >-Dave Pollum > > How do you handle swtiching between the two buffers cleanly and > quickly? I've written some code but it's a bit of a dogs dinner (too > many if statements)!
You use pointers to refer to the buffers. The code is the same regardless of which buffer the pointer points to.
> Also what do you do when both buffers are full and are marked as > OK_to_read_buffer? Just empty the data into a bin bucket until one > gets freed?
If you receive data faster than you can process it, you either have to have large enough buffers to hold all of the data or you have to drop messages. In general, double (or triple) buffers are used when you can process the data as fast as it arrives on the average and you won't need to continue to buffer it. Triple buffers are used if the data is coming in at a fixed rate and the data goes out at a fixed rate, but there is some latency inbetween. The first buffer is filled and processing starts, but the processing takes nearly as long as the message rate. So as the first buffer is emptied out the second becomes full and the processor has not yet released the first buffer. A third buffer makes it all work without a hitch. I expect this is needed more often when the whole thing is being done in hardware. If your processing in software is that close to not keeping up, it is likely to not work reliably.
Reply by Michael Frederick April 11, 20072007-04-11
On 4 Apr 2007 08:47:46 -0700, "Dave Pollum" <vze24h5m@verizon.net>
wrote:

SNIP!
> >I've used double buffering with packet/protocol logic in the ISR, and >that works well. However, if you don't want the packet logic in the >ISR, you could still use double buffering. The ISR could mark a >buffer as ready for the upper level code, and then store more >characters in the _other_ buffer. Ready could be set if the buffer is >full, or if a certain amount of time has passed and no new characters >have been received. The ISR can save the numbers of bytes received in >a variable associated with that buffer (use a struct - see below). >After the upper level code has read all of the chars from the buffer, >it as available. The ISR can then use that buffer again. Disabling >and restoring interrupts aren't needed. This eliminates the need for >both the ISR code and the upper level code to write to the count >(buff_len) variable. > > typedef struct { > char nbr_bytes_read; /* 0..255 */ > char OK_to_read_buffer; /* T/F */ > char buffer[ BUFF_SZ ]; > } buffer_stuff; > buffer_stuff *buffer_1; > buffer_stuff *buffer_2; > >-Dave Pollum >
How do you handle swtiching between the two buffers cleanly and quickly? I've written some code but it's a bit of a dogs dinner (too many if statements)! Also what do you do when both buffers are full and are marked as OK_to_read_buffer? Just empty the data into a bin bucket until one gets freed? Thanks.
Reply by werty April 9, 20072007-04-09
 If all code is structured , you have a

  2nd choice , to poll the serial port .

  If the kernel is structured , it can keep

 track of all the critical tasks , easily .

  If you ask the kernel to do an important

 task , is it important enuf to ignore

 the serial port ?

 Maybe , you are the programmer .

   In my machine , i am getting critical

  data from the old RS232 , i am

 using a modern mcu , with 16 char

 buffer .  This gives kernel enuf

 time to execute any task ( structure allows

 you to time it perfectly )

  and never overrun the ser' buffer .

   Its much faster to write a kernel from

 scatch , if you structure it with low level

 primatives , then mid-levels ( looks like

 a list of lists ) .

  C/C++ wont help you do this .



   My project is a ARM pocket PC .

  I will not use anyones code , i will not

 study any books , nor use the English

 language , nor ASCII text .

  It will be a tiny G.U.I. in 8 KB .

  Then expand it to 32Kb ....then

 to 1 megabyte ..




Reply by Thad Smith April 7, 20072007-04-07
Arlet wrote:
> On Apr 5, 4:45 am, Thad Smith <ThadSm...@acm.org> wrote: > >>Arlet wrote: >> >>>Alternatively, you could use something similar to this, and leave >>>interrupts enabled: >> >>>static unsigned char buff[ BUFF_SIZE ]; >>>static volatile unsigned char buff_head; >>>static unsigned char buff_tail; >> >>>__interrupt on_byte_in( void ) >>>{ >>> if ( (unsigned char) (buff_head - buff_tail) < BUFF_SIZE ) >>> buff[ buff_head++ % BUFF_SIZE ] = the_byte_in_the_register; >> >>>} >> >>>flush_receiving_buff( void ) >>>{ >>> while( buff_head != buff_tail ) >>> process the buff[ buff_tail++ % BUFF_SIZE ]; >>>} >> >>>This works only if BUFF_SIZE < 256. >> >>It only works if BUFF_SIZE divides evenly into UCHAR_MAX+1. In order to >>fix this, store the incremented indexes after mod reduction (which >>normally works faster by comparison, rather than mod operator). Also, >>you have BUF_SIZE+1 states to distinguish: 0 through BUF_SIZE stored >>characters. They can't be uniquely distinguished by the comparisons >>shown in the code. The standard approach is to either use a separate >>state variable or to eliminate the all full state by storing a max of >>BUF_SIZE-1 characters. > > > Actually, it works as long as the number of possible states can be > represented by an unsigned char, or (BUFF_SIZE+1) <= (UCHAR_MAX+1). In > theory, BUFF_SIZE could be as high as 255. The fact that the > comparison is done _before_ the mod reduction makes sure that we still > have the complete state information available.
Let's assume BUFF_SIZE = UCHAR_MAX = 255. At initialization buff_head and buff_tail = 0. We get 255 insertions into buff[0] to buff[254] without removals, leaving buff_head = 255. We know that another insertion will be refused if there is no removal. Let's take out one byte, leaving buff_tail = 1. Now we get another insertion, which is allowed since (255-1) < 255, and put it in buff[255%255] = buff[0]. OK so far. buff_head is incremented to 0. Remove the second byte from buff[1], leaving buff_tail = 2. Do another insertion, which is allowed since (unsigned char)(0-2) < 255. We place the character into buff[0%255] = buff[0], which overwrites the previously stored (and unfetched) character. It's broken. -- Thad
Reply by Grant Edwards April 7, 20072007-04-07
On 2007-04-07, CBFalconer <cbfalconer@yahoo.com> wrote:
> Grant Edwards wrote: >> On 2007-04-05, Ulf Samuelsson <ulf@a-t-m-e-l.com> wrote: >> > ... snip ... >> >>> I.E Receive interrupt subroutine writes data to the Software >>> FIFO and updates its write pointer. This is an atomic >>> operation because the main loop will not interrupt the >>> interrupt. >> >> But reading the pointer is not an atomic operation. It doesn't >> matter if only one of the read/write operations is non-atomic. >> The algorithm only works without disabling interrupts if _both_ >> reading and writing the pointer is atomic. > > So don't use pointers, use indices, which can be single bytes, and > reading or writing must be atomic.
Yes, I know. I already said that. Then ULF said that it didn't matter that pointer accesses weren't atomic on the AVR because you could use a "producer consumer pattern". -- Grant Edwards grante Yow! ANN JILLIAN'S HAIR at makes LONI ANDERSON'S visi.com HAIR look like RICARDO MONTALBAN'S HAIR!
Reply by CBFalconer April 6, 20072007-04-06
Grant Edwards wrote:
> On 2007-04-05, Ulf Samuelsson <ulf@a-t-m-e-l.com> wrote: >
... snip ...
> >> I.E Receive interrupt subroutine writes data to the Software >> FIFO and updates its write pointer. This is an atomic >> operation because the main loop will not interrupt the >> interrupt. > > But reading the pointer is not an atomic operation. It doesn't > matter if only one of the read/write operations is non-atomic. > The algorithm only works without disabling interrupts if _both_ > reading and writing the pointer is atomic.
So don't use pointers, use indices, which can be single bytes, and reading or writing must be atomic. -- <http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt> <http://www.securityfocus.com/columnists/423> <http://www.aaxnet.com/editor/edit043.html> "A man who is right every time is not likely to do very much." -- Francis Crick, co-discover of DNA "There is nothing more amazing than stupidity in action." -- Thomas Matthews -- Posted via a free Usenet account from http://www.teranews.com
Reply by CBFalconer April 6, 20072007-04-06
Arlet wrote:
>
... snip ...
> > Of course, the mod may be unacceptably slow, so in that case > BUFF_SIZE needs to be restricted to a constant power of two (with > 128 being the highest number that meets the requirements), so it > can be implemented by a bitwise 'and'. Any decent compiler will do > the optimization automatically. Using the mod is still possible if > it meets the timing requirements, and if there's a need for > special buffer sizes.
After proper initialization you don't need a mod operation. The code can take the faster form: if (BUFF_SIZE == ++index) index = 0; eliminating the need for a power of two. -- <http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt> <http://www.securityfocus.com/columnists/423> <http://www.aaxnet.com/editor/edit043.html> "A man who is right every time is not likely to do very much." -- Francis Crick, co-discover of DNA "There is nothing more amazing than stupidity in action." -- Thomas Matthews -- Posted via a free Usenet account from http://www.teranews.com
Reply by CBFalconer April 6, 20072007-04-06
Simon Clubley wrote:
> "Ulf Samuelsson" <ulf@a-t-m-e-l.com> writes: >> >> Hope everyone is aware that the RTS/CTS is to be considered >> a recommendation to the other end to try to stop its babbling. >> >> You must expect to get AT LEAST 16 more incoming characters >> if you want to be PC compliant. > > Interesting. I must admit that I was looking at it from the > viewpoint of microcontroller/unbuffered device to microcontroller > communication. > > In your example therefore, if the OP's talking to a device using > buffered UART controllers, then he simply can't have interrupts > off for an extended period, unless his UART also does buffering.
What you do know is that sender buffered data will be sent at full speed. Therefore you only need to inhibit the sending with RTS and wait for a limited period, say enough to transmit 16 chars. Now you don't have to inhibit that interrupt (although you may do so). Your receiving UART may be able to hardware buffer that many characters before interrupting anyhow, mitigating the problem of enforcing a delay. -- <http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt> <http://www.securityfocus.com/columnists/423> <http://www.aaxnet.com/editor/edit043.html> "A man who is right every time is not likely to do very much." -- Francis Crick, co-discover of DNA "There is nothing more amazing than stupidity in action." -- Thomas Matthews -- Posted via a free Usenet account from http://www.teranews.com
Reply by Grant Edwards April 5, 20072007-04-05
On 2007-04-05, Ulf Samuelsson <ulf@a-t-m-e-l.com> wrote:

>>> First set up your buffer, setup 2 pointers to it, the serial >>> interrupt uses one of them to write each byte, incrementing >>> each time (ie a circular pointer). Ditto for your reading >>> routine (use the other pointer). To flush the buffer set both >>> pointers to the same number. You will not need to disable the >>> serial interrupt. >> >> That's true as long as you know that reading/writing a pointer >> is an atomic operation. That's not true for all >> microprocessors. For example, on the AVR, reading/writing a >> pointer isn't atomic -- a MAJOR flaw in any processor that's >> intended for use with C -- one of the reasons I dislice the >> AVR ISA. > > Use a Producer/Consumer with separate pointers.
Yes, that's what we were describing. It doesn't work on an AVR because pointer read/write operations are not atomic.
> I.E Receive interrupt subroutine writes data to the Software > FIFO and updates its write pointer. This is an atomic > operation because the main loop will not interrupt the > interrupt.
But reading the pointer is not an atomic operation. It doesn't matter if only one of the read/write operations is non-atomic. The algorithm only works without disabling interrupts if _both_ reading and writing the pointer is atomic.
> The main loop can disable interrupts while accessing the pointer.
No it can't. We were discussing methods where there was no disabling of interrupts. That only works if reading and writing the buffer positions are both atomic operations. If you use pointers on an AVR, reading/writing them is not an atomic operation and therefore can not be done without disabling interrupts.
> In Embedded C++, you can probably define a "++" operator > which operates on circular buffers which handles everything for you.
In other words, "You can use the 'producer consumer' model without disabling interrupts. All you have to do is disable interrupts?" -- Grant Edwards grante Yow! I'm losing my at hair...did it go to visi.com ATLANTIC CITY??