EmbeddedRelated.com
Forums

UART THRE interrupt not working

Started by Tom July 11, 2007
I've been stuck on this for a few days now. I was using the UART tx
in a polled fashion, but it was wasting too much processing time. So
I'm trying to set up an interrupt for txing. Rx works fine for the
record.
Basically what I have is whenever I want to print something using
printf, I use putchar to put the data into a buffer. The THRE
interrupt is supposed to fire to pull data out of the buffer and
into the FIFO. But the THRE interrupt never fires. I've
tried "priming the pump" and everything else I've seen on here. I've
also checked to make sure that UART0_IER is being set when DLAB is
0.
Heres some pertinent snippets of my code:

int putchar (char c)
{
thcnt++;
if (tx_restart == 1) {
tx_restart = 0;
UART0_THR = c;
}
else {
tbuffer[tin] = c;
tin++;
if (tin >%5) {
tin = 0;
}
}
return c;
}

void com_initialize (void)
{
volatile char dummy;
__DISABLE_INTERRUPT();
SCB_PLLCFG = 0x03; // sets M to be 4 and P to be 1, sets
CCLK to be about 59 MHz
SCB_PLLCON = 0x03; // enables PLL and sets PLL to be
clock for microcontroller
SCB_PLLFEED = 0xAA; //
SCB_PLLFEED = 0x55;
GPIO_IOSET = 0x00FF0000; /* clear
LEDs */
/*------------
Setup serial port registers.
------------*/
PCB_PINSEL0 = 0x55400005; /* Enable RxD0 and TxD0 and JTAG*/

VICVectAddr0 = (unsigned)sio_irq;
VICVectCntl0 = 0x20 | 6; /* UART0 Interrupt */
VICIntEnable |= 0x00000040; /* Enable UART0 Interrupt */
UART0_LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */
UART0_IER = 0x00; /* Disable UART0 Interrupts */
UART0_FCR = 0x01; // enable fifos

UART0_DLL = 0x00000008; // &0x00ff; // sets UART
UART0_DLM = 0x00000008>>8; // ????
UART0_LCR = 0x03; // sets DLAB to 0
UART0_IER = 0x03; /* Enable UART1 RX and THRE Interrupts */
__ENABLE_INTERRUPT();
}

void sio_irq (void)
id = UART0_IIR;
VICVectAddr = 0;
if (id && 0x02)
{
if (tin != tout) {
UART0_THR = tbuffer[tout];
tout++;
tx_restart = 1;
if (tout >= 255) {
out = 0;
}
}
else {
tx_restart = 0;
}
The first bit is my putchar routine, the second is where I
initialize the UART, and the third is part of my irq.

Any ideas?

An Engineer's Guide to the LPC2100 Series

Tom, I just had this problem last week my LPC2103 board.
The THRE transmit interrupt works fine on my LPC2148 PCB, but not
with the LPC2103 it seems. Not sure why either, so I just stuck
my UART0 xmit code into my 200uSec timer interrupt instead for now.

I'll look at your code too, Tom, and if I see something, I'll
certainly let you know, but right now, I'm kinda stumped too.

boB

--- In l..., "Tom" wrote:
>
> I've been stuck on this for a few days now. I was using the UART tx
> in a polled fashion, but it was wasting too much processing time. So
> I'm trying to set up an interrupt for txing. Rx works fine for the
> record.
> Basically what I have is whenever I want to print something using
> printf, I use putchar to put the data into a buffer. The THRE
> interrupt is supposed to fire to pull data out of the buffer and
> into the FIFO. But the THRE interrupt never fires. I've
> tried "priming the pump" and everything else I've seen on here. I've
> also checked to make sure that UART0_IER is being set when DLAB is
> 0.
> Heres some pertinent snippets of my code:
>
> int putchar (char c)
> {
> thcnt++;
> if (tx_restart == 1) {
> tx_restart = 0;
> UART0_THR = c;
> }
> else {
> tbuffer[tin] = c;
> tin++;
> if (tin >%5) {
> tin = 0;
> }
> }
> return c;
> }
>
> void com_initialize (void)
> {
> volatile char dummy;
> __DISABLE_INTERRUPT();
> SCB_PLLCFG = 0x03; // sets M to be 4 and P to be 1, sets
> CCLK to be about 59 MHz
> SCB_PLLCON = 0x03; // enables PLL and sets PLL to be
> clock for microcontroller
> SCB_PLLFEED = 0xAA; //
> SCB_PLLFEED = 0x55;
> GPIO_IOSET = 0x00FF0000; /* clear
> LEDs */
> /*------------
> Setup serial port registers.
> ------------*/
> PCB_PINSEL0 = 0x55400005; /* Enable RxD0 and TxD0 and JTAG*/
>
> VICVectAddr0 = (unsigned)sio_irq;
> VICVectCntl0 = 0x20 | 6; /* UART0 Interrupt */
> VICIntEnable |= 0x00000040; /* Enable UART0 Interrupt */
> UART0_LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */
> UART0_IER = 0x00; /* Disable UART0 Interrupts */
> UART0_FCR = 0x01; // enable fifos
>
> UART0_DLL = 0x00000008; // &0x00ff; // sets UART
> UART0_DLM = 0x00000008>>8; // ????
> UART0_LCR = 0x03; // sets DLAB to 0
> UART0_IER = 0x03; /* Enable UART1 RX and THRE Interrupts */
> __ENABLE_INTERRUPT();
> }
>
> void sio_irq (void)
> id = UART0_IIR;
> VICVectAddr = 0;
> if (id && 0x02)
> {
> if (tin != tout) {
> UART0_THR = tbuffer[tout];
> tout++;
> tx_restart = 1;
> if (tout >= 255) {
> out = 0;
> }
> }
> else {
> tx_restart = 0;
> }
> The first bit is my putchar routine, the second is where I
> initialize the UART, and the third is part of my irq.
>
> Any ideas?
>
Not looked at the rest, but you have an extra & here...

>> if (id && 0x02)
I noticed the extra & in the if statement and removed it. I started getting something like I was expecting after that, but it still doesn't work quite like it should. But I think this was something to do with the buffer.
I had a test program that contained only the UART interrupt so I could get it working. I took the pertinent code from the test program into the main program for which this interrupt is needed and I got absolutely no transmissions from the UART. This seems
Hi,

Your tx_restart = 1 should be when you dont have anything to send when
tx interrupt occurs so that you can start the pump again.
Currently you are setting it when you have something to send. so your
put char routine will load THR even though it is already sending
something.

Regards
Suvidh

--- In l..., "Tom" wrote:
>
> I've been stuck on this for a few days now. I was using the UART tx
> in a polled fashion, but it was wasting too much processing time. So
> I'm trying to set up an interrupt for txing. Rx works fine for the
> record.
> Basically what I have is whenever I want to print something using
> printf, I use putchar to put the data into a buffer. The THRE
> interrupt is supposed to fire to pull data out of the buffer and
> into the FIFO. But the THRE interrupt never fires. I've
> tried "priming the pump" and everything else I've seen on here. I've
> also checked to make sure that UART0_IER is being set when DLAB is
> 0.
> Heres some pertinent snippets of my code:
>
> int putchar (char c)
> {
> thcnt++;
> if (tx_restart == 1) {
> tx_restart = 0;
> UART0_THR = c;
> }
> else {
> tbuffer[tin] = c;
> tin++;
> if (tin >%5) {
> tin = 0;
> }
> }
> return c;
> }
>
> void com_initialize (void)
> {
> volatile char dummy;
> __DISABLE_INTERRUPT();
> SCB_PLLCFG = 0x03; // sets M to be 4 and P to be 1, sets
> CCLK to be about 59 MHz
> SCB_PLLCON = 0x03; // enables PLL and sets PLL to be
> clock for microcontroller
> SCB_PLLFEED = 0xAA; //
> SCB_PLLFEED = 0x55;
> GPIO_IOSET = 0x00FF0000; /* clear
> LEDs */
> /*------------
> Setup serial port registers.
> ------------*/
> PCB_PINSEL0 = 0x55400005; /* Enable RxD0 and TxD0 and JTAG*/
>
> VICVectAddr0 = (unsigned)sio_irq;
> VICVectCntl0 = 0x20 | 6; /* UART0 Interrupt */
> VICIntEnable |= 0x00000040; /* Enable UART0 Interrupt */
> UART0_LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */
> UART0_IER = 0x00; /* Disable UART0 Interrupts */
> UART0_FCR = 0x01; // enable fifos
>
> UART0_DLL = 0x00000008; // &0x00ff; // sets UART
> UART0_DLM = 0x00000008>>8; // ????
> UART0_LCR = 0x03; // sets DLAB to 0
> UART0_IER = 0x03; /* Enable UART1 RX and THRE Interrupts */
> __ENABLE_INTERRUPT();
> }
>
> void sio_irq (void)
> id = UART0_IIR;
> VICVectAddr = 0;
> if (id && 0x02)
> {
> if (tin != tout) {
> UART0_THR = tbuffer[tout];
> tout++;
> tx_restart = 1;
> if (tout >= 255) {
> out = 0;
> }
> }
> else {
> tx_restart = 0;
> }
> The first bit is my putchar routine, the second is where I
> initialize the UART, and the third is part of my irq.
>
> Any ideas?
>
suvidhk wrote:
>
> Hi,
>
> Your tx_restart = 1 should be when you dont have anything to send when
> tx interrupt occurs so that you can start the pump again.
> Currently you are setting it when you have something to send. so your
> put char routine will load THR even though it is already sending
> something.
>
Yes, you do need to be "interrupt aware", the transmit queue may have
data in it and is running. This is a fairly complete, interrupt driven,
serial port handler. NOTE the use of the boolean
"serialDriverPort0.txRunning":
============= begin =================static void suckFifo0Dry (void)
{// empty recieve fifo of all data present.
do { // suck the fifo dry.
// grab one char.
serialDriverPort0.rxBuffer[serialDriverPort0.rxWrQue] = U0RBR;
// can we mark new char, or do we lose it?
if (((serialDriverPort0.rxWrQue+1) &
serialDriverPort0.RXMASK) != serialDriverPort0.rxRdQue) {
// we can store it.
serialDriverPort0.rxWrQue = (serialDriverPort0.rxWrQue+1) &
serialDriverPo
serialDriverPort0.rxCount++;
}
} while (U0LSR & ULSR_RDR);
LedActivityBits.Led_RxPanel = True;
// 3/4 mark in queue we remove RTS signal
if (serialDriverPort0.rxCount > ((RXBUFFSIZE_serialPort0 / 4) * 3)) {
booleans.serial0RecvSuspended = True;
serial0RTS(False);
}
}

static void fillXmit0Fifo (void)
{
// anything else "to go" (fries with that? ;-) ?
serialDriverPort0.txRunning = False;
while (U0LSR & ULSR_THRE) {
// fill the transmit fifo.
if (serialDriverPort0.txRdQue != serialDriverPort0.txWrQue) {
U0THR = serialDriverPort0.txBuffer [serialDriverPort0.txRdQue];
serialDriverPort0.txRdQue = (serialDriverPort0.txRdQue+1) &
serialDriverPo
serialDriverPort0.txRunning = True;
LedActivityBits.Led_TxPanel = True;
} else break;
}
}

void serial0ISR (void)
{// serial port 1 interrupt handler.
volatile uint8_t IIR;
// loop until all have been processed.
while (!((IIR = U0IIR) & UIIR_NO_INT)) {
switch (IIR & UIIR_ID_MASK) {
case UIIR_CTI_INT: // Character Timeout Indicator
case UIIR_RDA_INT: // Receive Data Available
suckFifo0Dry ();
break;
case UIIR_THRE_INT: // Transmit Holding Register Empty
if (!serial0CTS() && serialDriverPort0.useCtsFlow)
suspendXmit0Output()
else fillXmit0Fifo();
break;
case UIIR_RLS_INT: // Receive Line Status
if (U0LSR & ULSR_BI) {
// flag interface as being down.
booleans.serial0breakInterrupt = True;
booleans.serial0IsNowDown = True;
}
break;
default: // unknowns, try to clear.
U0LSR; U0RBR;
break;
}
}
}

void serial0Putch (const ushort ch)
{ // send a char.
ushort txWrIdx;
unsigned flags_cpsr;
// wait until there is room.
txWrIdx = (serialDriverPort0.txWrQue+1) & serialDriverPort0.TXMASK;
while (txWrIdx == serialDriverPort0.txRdQue);
// disable interrupts while stuff char.
flags_cpsr = disableIRQ();
// transmitter running?
if (serialDriverPort0.txRunning == False) {
// stuff char into TX data register.
U0THR = (ch & 0xff);
serialDriverPort0.txRunning = True;
LedActivityBits.Led_TxPanel = True;
} else {
serialDriverPort0.txBuffer [serialDriverPort0.txWrQue] = (ch & 0xff);
serialDriverPort0.txWrQue = txWrIdx;
}
restoreIRQ(flags_cpsr);
}

============== snip =================
--
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net http://cyberiansoftware.com http://openzipit.org
"Windows? No thanks, I have work to do..."
----------------