Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

Re: [lpc2000] UART TX FIFO and INTs problem

2004-02-18 by Robert Adsett

A couple of questions and observations.  Maybe they'll spark something.
At 06:49 PM 2/17/04 +0000, you wrote:
>I'm using Two 256 byte circular buffers for RX and TX Interrupts on
>UART0.

Not really related to your actual problem but since you are using printf 
why bother with a transmit interrupt?  Unless you are planning on 
multithreading?  I've always found that all a serial transmit interrupt on 
single threaded apps does is introduce needless complexity (read bugs).

>
>Putchar() writes a char to the TX buffer's current head pointer and
>post increments it (auto wrap on index byte). It then increments the
># of TX chars in the buffer (tx_chars) and enables the THRE Interrupt.
>
>The THRE handler part takes a character from the tail pointer, and
>post increments. It then decrements tx_chars.
>If TX buffer tail equals TX buffer Head, the THRE Interrupt is
>disabled.
>
>Of course in case I write too fast to the TX buffer and it overflows
>(to simplify for the time being) at the end of Putchar() I check if
>tx_chars is larger than say, 200 Chars.
>If it is, I wait in a while loop for tx_chars to drop below a
>threshold, say 32.

That raises a bunch of design issues to mind.  Why such a large gap?  Why 
not add to buffer as soon as any room is available?  And finally why two 
different variables to keep track of the room in the ring buffer?

>
>I've used this principle on many MCUs, and never had a problem.
>
>The problem is that if I write a _constant_ flow of characters much
>faster than the Baudrate after quite some time the transmission INTs
>stop. [ NO other interrupts are running during this test ]
>To test it, I set a pin HIGH when Putchar() is waiting for the TX
>buffer to empty a bit, and clear it when buffer filling resumes - a
>bit like this :
>
>volatile unsigned char tx_chars;
>
>.....                            /* write char to buffer */
>++tx_chars ;                     /* another char in TX buffer */
>if ( tx_chars > 200 )
>     {
>     /* set test pin HIGH */
>     while ( tx_chars > 32 ) ;    /* let TX buffer flush out a bit */
>     }
>/* set test pin LOW */
>}
>
>I can see all is fine, the TEST pin spends time HIGH while the buffer
>is being flushed, and is then LOW for a few mS (printf() is re-
>filling up the buffer from 32 or less to > 200 chars)
>
>When the transmit out of UART0 TX stops, tx_chars is set to one more
>than the threshold (for example - here 33 ), and Tail and Head
>Pointer of TX buffer are equal, hence the THRE interrupt is disabled.
>The code of course is stuck in the while ( tx_chars > 32 ) loop ......
>
>A plausible explanation would be that at times, the TX FIFO holds
>more than one byte, and when the THRE interrupt occurs, more than one
>char should be subtracted from tx_chars.

I don't see how that could happen given your outline.  The pointer is 
updated exactly once, as is the count.  One possibility is that there is an 
access control problem.

Are your pointers and counters declared as volatile?
Is access to both protected with interrupt disabling and re-enabling sequences?

One thing that can happen on the ARM that won't on micros with small 
register sets is that the pointer and counters can be held in the register 
sets and won't get spilled out to memory unless they are declared as 
volatile.  I'd expect the issue to be a little more dramatic in that case 
but...

This does 'feel' more like an access control race where the interrupt 
decrements the counter and returns to normal execution which immediately 
overwrites it with an old updated value.  Something like:

r1 = tx_chars
                         - jump to transmit interrupt
                         - Save appropriate registers
                         - Perform transmit
                         - r1 = tx_chars
                         - r1 -= 1
                         - tx_chars = r1
                         - restore saved registers
                         - return from interrupt
r1 += 1
tx_chars = r1

And now tx_chars is one higher than it should be.  As bugs go these ones 
tend to be very timing sensitive.

Ring any bells?

Robert

" 'Freedom' has no meaning of itself.  There are always restrictions,
be they legal, genetic, or physical.  If you don't believe me, try to
chew a radio signal. "

                         Kelvin Throop, III

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.