UART TX interrupt: should I put at least one byte in the TX FIFO?

cancel
Showing results for 
Search instead for 
Did you mean: 

UART TX interrupt: should I put at least one byte in the TX FIFO?

3,445 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Thu Feb 12 04:16:12 MST 2015
I'm trying to use the UART peripheral in LPC4350 MCU in interrupt mode, but I failed.

With other MCUs (AVR and SAMD20 from Atmel), I usually enable the TX register empty interrupt (IER->THREIE) as soon as I need to send some bytes, and before pushing any byte in the TX register/FIFO. The interrupt is immediately fired up, because the TX register/FIFO is really empty.

The behaviour of UART peripheral on LPC4350 MCU seems different. The THRE interrupt isn't immediately fired as soon as setting IER->THREIE bit, but I need to push at least one byte in TX FIFO to have the interrupt. It seems the THRE interrupt is "edge-triggered" (triggered when the TX FIFO switch from non empty to empty state) and not "level-triggered" (triggered when the TX FIFO is currently empty, indipendent from a previous state).
I'd like to push the first byte inside interrupt handler, so I need the interrupt even if the TX FIFO is empty.

The uart example in LPCopen really starts pushing bytes into TX FIFO, but if I change the code to avoid pushing the first byte, the interrupt isn't fired.

It seems an odd behaviour to me, so I'm thinking I made some mistake. Could someone confirm the odd behaviour is the real one, or did I make some mistake?
Labels (1)
0 Kudos
19 Replies

899 Views
sherry_zhang2
Contributor I

Hi,

So I also met the problem: with PL011 I have to write some bytes into the TX FIFO and then the TX interrupt can be triggered. Otherwise, the interrupt cannot be triggered. But on LPC55S69, the interrupt can be triggered directly.

What's the final explanation for this issue?

Thanks,

Sherry

0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Wed Feb 18 06:26:01 MST 2015
yes, bics = 'and not'

In the interrupt handler I only test bits in the line status. E.g. rxRdy and THRE.

I read and ignore the IID register incase there is a break condition etc. (that I don't care about).
This resets the interrupt assertion (sometimes can't hurt).
I never take any actions based on the IID, only the line status (uStatus).

After the forced trigger, THRE in line status is set. But not apparently in the IID.

EDIT: If I disable the THREIE in IER (when queue is empty), the test of line status will still show THRE
so the mask (r12) is set to the THRE bit. Then any tests on the LSR will not find THRE (it gets masked -- while
in the same invocation).
Mike
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Wed Feb 18 01:04:09 MST 2015
See Post 9, 2nd paragraph.

I'm sorry Mike, but I can't understand assembler instructions and the comments aren't clear to me.

movsr12, 0; initially, turn off the THRE mask

;---------------------------------------;
0:ldrr0, [r3, uStatus]; check status flags
bicsr0, r12; but mask unwanted bits

If I understood correctly, r3 is loaded with the base address of USART registers, r12 is loaded with zero and r0 is loaded with LSR value (register r3+uStatus).
What is the reason of bics instruction? I think it is
r0=r0 AND NOT(r12)=r0 AND NOT(0)=r0 AND 0xffffffff fffffff=r0

I don't understand what is the goal of this instruction.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Tue Feb 17 08:37:43 MST 2015
See Post 9, 2nd paragraph.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Tue Feb 17 06:51:20 MST 2015
Hello Mike,

I'm trying to implement your technique, to force the first interrupt through STIR register. You say:

Quote:
This generates an NVIC interrupt (for the UARTx) regardless. You still need to check the peripheral interrupt status as usual. It could well be that none of the sub-causes are indicated; then your interrupt handler would return having done nothing.

In this case, the THRE bit has been set, but NXP decided not to generate the peripheral interrupt immediately
as described in my previous post.


What do you mean with peripheral interrupt status? I think IIR register.
In the ISR I check IIR register for interrupt requests pending, but in the first interrupt (forced with STIR register), the IIR shows again no pending interrupt (INTSTATUS = 1)!

What do you check in your ISR? You wrote:
if (THRE bit)

but which THRE bit? I thought the INTD bits in IIR == THRE (0x1)... but this doesn't work. At the moment of the first "software-generated" interrupt, IIR doesn't show any pending interrupt request.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Mon Feb 16 02:23:00 MST 2015
OK, I get you concern now.

Unfortunately there simply is not a normal interrupt for tx complete.  :((
Only polling.

I don't have any suggestions.

Mike
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Mon Feb 16 02:08:56 MST 2015

Quote:
I don't really undstand what you are saying.


I'm sorry for my poor English, I'll try to explain with an example.

Node A: MCU without automatic direction control.
Node B: LPC with automatic direction control.

Node A sends a packet/request to Node B and switch to receiver mode by software, maybe 50us later than the last stop bit of the last byte of request.
Node B receives the full packet and decides to answer, but it is very fast and put the first start bit of the first byte of the answer after 40us the last byte received. As you can understand, the answer will be corrupted. The solution is to introduce a short delay on Node B before answering.


Quote:
Still you say that you want a delay. Strictly speaking, the delay belongs after the last character received and
not before the first response character is transmitted; however it is probably more convenient if invoked before
sending a block of characters.


Indeed I usually add the delay in the transmissione mechanism.



Quote:
[1] A simple spin loop -- cheap, uses no resources.


I don't like to block the entire application for 100us... only for this stupid thing.


Quote:
[2] Send one (or more) dummy characters (at the same baud rate) on another UART that is not being used.
You don't even have to assign any pins to it. And poll on line status TXEMPTY.


Two UARTs to use just one? Too bad.


Quote:
There is even a UART register to set an additonal delay (in bit times) if the other end is slow with it's reception.


Sincerely I don't understand what is the meaning of this register (I think you are talking about RS485DLY). This adds simply a delay from the stop bit to the automatic direction change (from tx to rx). When this additional delay could be useful?
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sun Feb 15 04:47:03 MST 2015
I don't really undstand what you are saying.

One of my UART channels is multi-drop full duplex (4 wire) RS485.
However, the same 'only one transmitter' still applies.

When the LPC code receives a protocol packet (with the unit's address) it does whatever is indicated and sends
a reply.
[Note: when I receive the last character of a packet, it must have been completely transmitted by the sender]

I use the auto direction control feature to switch the 485 transceiver (for the transmit pair, but it is the same for
the half-duplex case). This automatically enables the transmitter on the first character and disables it after
complete transmission of the last character. There is even a UART register to set an additonal delay
(in bit times) if the other end is slow with it's reception.

So (for LPC's at least) I don't see what the problem is. Any issue is at the other end.

There is one occasion where I do whant to know when the latest response has been completely sent
(as opposed to knowing that it is 'in flight' and the hardware will disable the transmitter appropriately); that is
when I get a 'reset' command. In that case, I simply call a blocking function that polls on the TXEMPTY bit
in the line status register.

Still you say that you want a delay. Strictly speaking, the delay belongs after the last character received and
not before the first response character is transmitted; however it is probably more convenient if invoked before
sending a block of characters.

[1] A simple spin loop -- cheap, uses no resources.
[2] Send one (or more) dummy characters (at the same baud rate) on another UART that is not being used.
     You don't even have to assign any pins to it. And poll on line status TXEMPTY.
[3] Ask somebody else :p

Mike
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Sun Feb 15 03:45:29 MST 2015
Mike, maybe you can help me with another issue, similar to this.

The UART peripheral of LPC little monsters MCUs doesn't provide a "transmit complete" interrupt signal. As you know, that kind of signal is very important in half-duplex serial communication where you need to activate/deactivate an external transmitter. I know the UART (at least the UART in LPC43xx) is able to drive automatically a direction pin, so you can answer that the "transmit complete" interrupt isn't needed. I don't agree. Let's explain me.

In a half-duplex RS485 bus, only one node is the transmitter. When the transmitter node ends sending data on the bus, it changes its direction to receiver. If the transmitter MCU is able to drive automatically a direction pin, the swap from transmitter to receiver mode is immediate. In my situations, I have on the bus some nodes that change the direction through "transmit complete" interrupt... so by software. It could be a short delay between the end of the stop bit of the last byte and the direction inversion. During this period, the transmitter stays in transmitting mode, so all the other nodes can't initiate a transmission (otherwise a collision will occur). They must implement a delay between the reception of a datagram and the start of the answer (or another transmission).

Usually this delay is very small (100us is generally sufficient) and is comparable with a small number of "character time" send on the bus (100us corresponds to about 1 character time at 115200bps). The delay can be implemented with a hardware or software timer. But there is a more efficient way... use the same UART peripheral to implement the delay. It is sufficient to add a "dummy preamble" to the answer, for example 1 or 2 "dummy bytes". In order to avoid that receivers on the bus can receive "dummy bytes", I usually enable the transmitter of the external transceiver only after the last dummy byte is really shifted out, just before the first useful data byte.

I implemented this technique on other MCUs with success, so I'd like to make the same with LPC MCUs, but I have some difficulties.

If I use the automatic direction control of the external transceiver, how can I avoid the real transmission of dummy bytes? One possibility is to disconnect the UART TX signal from the real pin through pin multiplexing feature. But in this case, I need the "transmit complete" interrupt (because I have to reassign the pin to UART TX as soon as the last "dummy byte" is shifted out from TSR register), that LPC UART doesn't provide.

Ok, I could avoid to use automatic direction control, but even in this case I need "transmit complete" interrupt. Indeed, I need to change direction exactly at the same moment before, when the last "dummy byte" is really shifted out.

So the question is: how to have a "transmit complete" interrupt? I think the normal THRE interrupt is a "transmit complete" interrupt if I push in the TX hw FIFO one byte at a time (as I will do). See this page.

What do you think? Better alternatives?

I know there is a better alternative: it is sufficient to send some 0xFF bytes as preamble. Also the protocol should be designed to have packets with 0xFF bytes as a preamble without useful bytes.
The entire frame of 0xFF byte appears as a single negative pulse.
If the previous transmitter is yet in transmitter mode, 0xFF will not be received, so no problem.
If it is already in receiver mode, it will receives 0xFF so it uses it as preamble.

Unfortunately I don't have a protocol with 0xFF preamble...
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Sun Feb 15 03:01:57 MST 2015

Quote:
This technique is called Bouncing THRE.
The UARTn THRE interrupt (UnIIR[3:1] = 001) is a third level interrupt and is activated when the UARTn THR FIFO is empty provided certain initialization conditions have been met. These initialization conditions are intended to give the UARTn THR FIFO a chance to fill up with data to eliminate many THRE interrupts from occurring at system start-up. The initialization conditions implement a one character delay minus the stop bit whenever THRE = 1 and there have not been at least two characters in the UnTHR at one time since the last THRE = 1 event. This delay is provided to give the CPU time to write data to UnTHR without a THRE interrupt to decode and service. A THRE interrupt is set immediately if the UARTn THR FIFO has held two or more characters at one time and currently, the UnTHR is empty. The THRE interrupt is reset when a UnTHR write occurs or a read of the UnIIR occurs and the THRE is the highest interrupt (UnIIR[3:1] = 001).



Sincerely, I have read that paragraph in the past, but I couldn't understand what it means, where it could be useful and what exactly happens.

I understand that these "initialization conditions" introduce only a delay (that I'll try to use to have a transmit complete interrupt, see my next post). So again I don't understand why the first interrupt is never triggered, even if these "initialization conditions" aren't met. I expected the first interrupt was simply delayed.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Feb 14 17:13:00 MST 2015
I take it that you realise that UART interrupts do not neccessarily correspond one-to-one with UART events.
In the handler you have to keep checking until all events of interrest have been handled.

In the following (just for laughs -- and vanity) I poll on the line status and only read the interrupt ID to release the
interrupt status.

There is a minor complication if the Tx queue is empty, I turn off the THREIE, but (obviously) the line status still
shows THRE. So I use a mask. Hopefully, the comments will explain all my secrets.

;-----------------------------------------------------------------------!
; Sio IrqHandler!
;-----------------------------------------------------------------------!

fn UioIrq0

movsr0, 0; channel 0
b.nUioIrqHandler;

fe UioIrq0

etc.

;-----------------------------------------------------------------------!
; Sio IrqHandler!
;-----------------------------------------------------------------------!

fn UioIrqHandler

adrr3, sioTbl; get channel specific details
addsr3, r3, r0, lsl 5; tbl+channel*32
ldrr2, [r3, 28]; get ucb
ldrr3, [r3,  0]; get peripheral base addr
movsr12, 0; initially, turn off the THRE mask

;---------------------------------------;
0:ldrr0, [r3, uStatus]; check status flags
bicsr0, r12; but mask unwanted bits
ldrr1, [r3, uIntID]; read interrupt status register
tstr0, (1<<0); rxRdy
bne20f; yup
tstr0, (1<<5); txRdy
beq9f; nope -- ignore line status

;---------------------------------------;
; transmit next character;
;---------------------------------------;
10:ldrr1, [r2, uTxHead]; head offset
ldrr0, [r2, uTxTail]; tail offset
cmpr0, r1; any more to xmit?
bne12f; yup

;---------------------------------------;
11:ldrr0, [r3, uIrqEnable]; no more chars
bicsr0, (1<<1); disable TxRdy
strr0, [r3, uIrqEnable];
movsr12, (1<<5); and set mask also
b0b; see if more to do

;---------------------------------------;
12:ldrr0, [r2, uTxBase]; buffer base addr
ldrbr0, [r0, r1]; get next char
strr0, [r3, uTxChar]; xmit
addsr1, 1; bump
movwr0, txBfSz-1; with wrap
andsr1, r0;
strr1, [r2, uTxHead]; update head
b0b; see if more to do

;---------------------------------------;
; character received;
;---------------------------------------;
20:ldrr1, [r2, uRxBase]; buffer base addr
ldrr0, [r2, uRxTail]; tail offset
addsr1, r0; now a pointer
ldrr0, [r3, uRxChar]; get the character
strbr0, [r1]; stash
ldrr0, [r2, uRxTail]; tail offset
addsr0, 1; bump
movwr1, rxBfSz-1; wrap  factor
andsr0, r1; with wrap
strr0, [r2, uRxTail]; update tail
b0b; see if more to do

;---------------------------------------;
9:bxlr; return

fe UioIrqHandler



Cheers, Mike
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Feb 14 16:04:55 MST 2015

Quote:
And another question. STIR register wants the interrupt ID of the peripheral, but one peripheral could have many associated interrupts. For example, UART peripheral has at least transmitter and receiver interrupts. What is the exact interrupt that is fired up when I write to STIR register?



This generates an NVIC interrupt (for the UARTx) regardless. You still need to check the peripheral interrupt status
as usual. It could well be that none of the sub-causes are indicated; then your interrupt handler would return having
done nothing.

In this case, the THRE bit has been set, but NXP decided not to generate the peripheral interrupt immediately
as described in my previous post.

Sometimes I think the programming is only a series of work arounds for other peoples laziness, incompetance,
indifference, ...
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Feb 14 15:56:26 MST 2015
Actually, re the initial non-trigger of the THRE interrupt (which, like you and undoubtedly many others,
I relied upon on with other Atmel, Intel, ... processors) in the UM for the 1778 at least this is claimed
to be a beneficial added feature! Go figure! Not!

This technique is called Bouncing THRE.


Quote:
The UARTn THRE interrupt (UnIIR[3:1] = 001) is a third level interrupt and is activated
when the UARTn THR FIFO is empty provided certain initialization conditions have been
met. These initialization conditions are intended to give the UARTn THR FIFO a chance to
fill up with data to eliminate many THRE interrupts from occurring at system start-up. The
initialization conditions implement a one character delay minus the stop bit whenever
THRE = 1 and there have not been at least two characters in the UnTHR at one time
since the last THRE = 1 event. This delay is provided to give the CPU time to write data to
UnTHR without a THRE interrupt to decode and service. A THRE interrupt is set
immediately if the UARTn THR FIFO has held two or more characters at one time and
currently, the UnTHR is empty. The THRE interrupt is reset when a UnTHR write occurs or
a read of the UnIIR occurs and the THRE is the highest interrupt (UnIIR[3:1] = 001).



That explains why they did it, but is really not a good excuse.

So that is why I force an interrupt via the NVIC to avoid this helpful feature.

Regards, Mike


0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Sat Feb 14 02:32:09 MST 2015
Thank you Mike for your answer. Fortunately the community is more active than Atmel guys :-)


Quote:
I write in assembler, below is some pseudo-code.


Really? You write in assembler those little monsters of MCUs?

The code you shared is exactly the same I wanted to use. There's just one difference: you enable THRE interrupt (set THREIE bit in IER register) and trigger "manually" (by software) the first interrupt through STIR register of NVIC.

Anyway I don't understand why we need to force the first interrupt by software. Initially the FIFO is empty, so the THRE bit in LSR register is set. I think this is the bit that fires the interrupt, as soon as it is enabled. But, actually, it isn't.

The only explanations could be in "pulse-sensitive" versus "level-sensitive", but I couldn't well understand the difference.

I use Cortex-M0+ device from Atmel (SAMD20) too. For this MCU I don't need to force the first interrupt, I only need to enable the "transmitter empty" interrupt and the first interrupt is automatically fired (of course, I enabled the peripheral interrupts in NVIC during UART initialization).

And another question. STIR register wants the interrupt ID of the peripheral, but one peripheral could have many associated interrupts. For example, UART peripheral has at least transmitter and receiver interrupts. What is the exact interrupt that is fired up when I write to STIR register?
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Fri Feb 13 03:45:58 MST 2015
I write in assembler, below is some pseudo-code.
Forum may be removing the angle bracket -> in pointers?

WHOOPS! I use the 1778 -- your UM chapters/paragraphs will be different. Sorry.

UxPutBlock(uchar *p, ulong n)
{
  for (; n--; )
  {
    *UxTxBuf++ = *p++; //NB: handle ring buffer wrap
    bufTail++;  // With wrap
  }

  UARTn->IER |= 0x02; // I.e THRE interrupt enable
  NVIC->STIR = ??;  // UARTn periheral ID (5/6/7/8/35 for UARTS 0/1/2/3/4; see UM)
  // This is key!
}

UxIRQ
{
  ...
  if (THRE bit)
  {
    if (head = tail) // all sent?
      UARTn->IER &= ~THREIE;
    else
    {
      ch = *buff+head;
      head ++;
      UARTn->THR = ch;
    }
  }
...
}


I don't use LPCOpen so work out the register block names and members for your self.

PS: For the NVIC interrupt trigger register and non-priveleged code, see UM 5.5.22

Mike
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Thu Feb 12 15:13:53 MST 2015

Quote:
My UART routines do not enable the TX interrupt at init time.

To send a character or block I add character(s) to a UART specific ring buffer. [Software]



This is exactly what I do with other MCU.


Quote:
Then I enable the TX interrupt and use the NVIC interrupt software trigger mechanism to
get the first TX interrupt


Why this doesn't work on my side? I noticed the first interrupt is triggered only after pushing the first byte in THR. Could you write the code you use, please?
I enable UART interrupt in NVIC (NVIC_Enable) during init only one time. Successively I enable only THRE interrupt, but the interrupt isn'T triggered.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Thu Feb 12 12:33:36 MST 2015
My UART routines do not enable the TX interrupt at init time.

To send a character or block I add character(s) to a UART specific ring buffer. [Software]

Then I enable the TX interrupt and use the NVIC interrupt software trigger mechanism to
get the first TX interrupt.

This first (and the subsequent) interrupts write the next character from the (private) ring buffer
to the UART TX register.

The TX interrupt after the last (currently) character notes that the (software) queue is empty and
turns off the TX interrupt enable.

Regards, MIke.

0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Thu Feb 12 09:15:11 MST 2015
An another comment... the design of the UART peripheral in LPC MCUs are very bad!

How to use UART in interrupt mode without disabling interrupt (that is always a poor practice)? I think it's impossible.

THRE  =11100111111111111
THREIE=00011111111110000
TXD   =11111SBBBBBBP1111
          ^ ^      ^
          a b      c


At the beginning, THRE is 1 (FIFO is completely empty), THREIE=0 (interrupt disabled) and TXD=1 (signal is high).
At time a, uart_sendchar() is called. It puts the byte in FIFO (THR=c) and enable interrupt (IER->THEREIE=1). Immediately THRE is 0 (FIFO is not empty).
After some small amount of time (time b), THRE is set again because the data byte is passed to the shift register TSR, so the FIFO is empty again. Please note that the THRE ISR isn't triggered for the "initial conditions" explained in the datasheet. About at the same time, data byte (start bit) appears on TXD signal (S=Start bit).
At time c, when the stop bit appears on TXD line (P=stoP bit), interrupt is fired and the ISR function disable interrupt (IER->THREIE=0), because we assume there aren't other bytes to send.

This works if only one byte is send. If uart_sendchar() is called a second time, what should I do? It depends on the exact moment when it is called. I write down the following code:
void 
uart_putchar(UART *uart, unsigned char c)
{
uint32_t ier = uart->hw->IER;
if (ier & UART_IER_THREINT) {  /* Interrupt enabled */
FIFO_Push(c);
uart->hw->IER |= UART_IER_THREINT;  /* Enable interrupt */
} else {
uart->hw->THR = c;
uart->hw->IER |= UART_IER_THREINT;  /* Enable interrupt */
}
}


As you can understand, that code doesn't work. If uart_sendchar() is called immediately before the ISR of the last byte, the CPU enters in the first if branch. Now the data byte is pushed in a "software" FIFO (popped in ISR)... but if ISR is triggered just before FIFO_Push(c), the ISR disables interrupt (no more data to send, "software FIFO" is empty).
Ok, I can re-enable the interrupt again after FIFO_Push(), but the ISR isn't triggered anymore. I need to push a new byte in THR.

Consider that FIFO_Push() is atomic, so I don't need to disable interrupts in uart_sendchar() only to safe the "software" buffer (on the contrary of ringbuffer in uart example of LPCOpen).

I think it's impossible to write a uart driver in interrupt mode, without disabling temporarly tx interrupt.
0 Kudos

899 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by giusloq on Thu Feb 12 07:29:27 MST 2015
And there is another thing I couldn't understand. UART has a 16-bytes TX FIFO, but how can I use it?

I expect to write something similar to:
if (!UART_TxFifo_Full(pUart)) pUart->THR = c;


But how to write the UART_TxFifo_Full() function? In the uart.c example provided with LPCOpen I see:

/* Fill FIFO until full or until TX ring buffer is empty */
while ((Chip_UART_ReadLineStatus(pUART) & UART_LSR_THRE) != 0 &&
   RingBuffer_Pop(pRB, &ch)) {
Chip_UART_SendByte(pUART, ch);
}


I think the comment is wrong. After writing the first byte to THR, flag UART_LSR_THRE is immediately reset (Tx FIFO isn't empty, but it isn't full too) and the execution exits from the while above after the first loop. The code above works, but it doesn't use the complete TX FIFO, but only one position, because it checks for TX FIFO empty and not full condition.

I think we need to check the Tx FIFO full flag, but it seems it lacks.
0 Kudos