EmbeddedRelated.com
Forums

Problems w/ UART

Started by peterburdine October 7, 2004
First a question, you say the interrupt is not being generated, how do you
know?

A few comments on the code below, probably more when I get a chance to
review it properly.

At 05:12 PM 10/8/04 +0000, you wrote:
>It was a small oversite that it wasn't posted. I was copying stuff
>out that I didn't need. Since then I've made another small project
>that just tests the UART. I am now trying non vector irqs, and they
>still don't work. Here is the correct code: >#include <stdio.h>
>#include "../ArmCommon/types.h"
>#include "../ArmCommon/LPC22xx.h"
>#include "../ArmCommon/LPC2294.h"
>
>#define TX_BUFFER_LENGTH 64
>
>static Uint16 txBytes = 0;
>static Uint16 txRdPos = 0;
>static Uint16 txWrPos = 0;
>static Uint16 txBuffer[TX_BUFFER_LENGTH];
>
>void uart0_config (void);
>void uart0_isr (void);
>void uart0_sendByte (Uint8);
>int putchar(int);
>int main (void);
>
>void uart0_config (void) {
> // Setup UART 0
> U0LCR = 0x80; // Enable DLAB
> U0DLL = 0x36; // Setup BGR
> U0DLM = 0x00; // ~57.6 kbps @ 50 Mhz
> U0LCR = 0x03; // Enable UART 0, 8 bits, no parity, 1 stop bit
>
> // Reset the FIFOs
> U0FCR = 0xC7; // RX FIFO trigger level 8 chars, reset tx and rx fifo,
>enable fifos
>
> // Setup Interrupts for UART0
> VICIntSelect &= 0xFFFFBFFF; // Make sure 6 is IRQ not FIQ
> VICIntEnable |= (0x1 << 6); // Enable Uart0 Interrupt
> VICDefVectAddr = (unsigned long) uart0_isr; // Use Non vectored irq
> U0IER = 0x7;
>}
>
>void uart0_isr (void) __irq {
> Uint8 dummy;
> switch(U0LSR) {

This is potentially a very big problem. The LSR can (and often will) have
multiple bits set, so you will almost always be taking the default case
even if THRE is set (thus my question above). Given that the default case
is echoing whatever is in U0RBR (BTW what is CTI?) I would expect almost
anything if no characters had been sent to the port. Change this before
trying anything else. Then put a pin toggle or something in so you know if
the interrupt routine is ever reached.

> case 0x01: // RDR
> break;
> case 0x02: // OE
> break;
> case 0x04: // PE
> break;
> case 0x08: // FE
> break;
> case 0x10: // BI
> break;
> case 0x20: // THRE
> if(txBytes != 0) {
> U0THR = txBuffer[txRdPos];
> txRdPos++;
> txRdPos = (txRdPos ==
> TX_BUFFER_LENGTH)?0:txRdPos;
> txBytes--;
> }
> break;
> case 0x40: // TEMT
> break;
> case 0x80: // FXFE
> break;
> default: // Must be CTI
> uart0_sendByte(U0RBR); // Echo back

This is potentially a problem. I haven't looked at in detail but you are
using the same buffer routine here as you are in the non-interrupt
code. The ground is very unstable here.

> }
> dummy = U0IIR; // read to clear IRQ
UOIIR is what you should be using to determine the interrupt source. > dummy += 1; // keep compiler from optimizing dummy out

And if U0IIR is read and used you don't need this line. In fact if U0IIR
is declared volatile (as it should be) you only would need this line if the
compiler was buggy.

> VICVectAddr = 0;
>}
>
>void uart0_sendByte(Uint8 a) {
> // Wait until there is room in the buffer
> while(txBytes == TX_BUFFER_LENGTH);
> if(!(U0LSR&0x20)) { // Put it in the software buffer
> txBuffer[txWrPos] = a;
> txWrPos++;
> txWrPos = (txWrPos == TX_BUFFER_LENGTH)?0:txWrPos;
> txBytes++;
> }
> else { // If the chip buffer is empty, put it there
> U0THR = a;
> }
>}
>
>int putchar(int a) {
> uart0_sendByte((int)a);
> return 0;
>}
>
>int main (void) {
> PINSEL0 = PINSEL0_00_TxD | PINSEL0_01_RxD;
> VPBDIV = 0x1;
> uart0_config();
> printf("piece of crap!");
> while(1);
>}
>
>The output of this is "pep" when I run it on hardware. The Keil
>simulator of course says it is fine.

There are multiple examples of working serial interrupt code around, some
in the files section and the newlib support on
http://www.aeolusdevelopment.com has some as well. Most are GNU based, but
the concepts and pitfalls are the same for all compilers.

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



An Engineer's Guide to the LPC2100 Series


--- In , Robert Adsett <subscriptions@a...> wrote:
> First a question, you say the interrupt is not being generated, how
do you
> know?
>
While I was debugging w/ JTAG, I put a breakpoint in the isr, it was
never reached. I'd turn on an LED if one existed for my dev board. >
> This is potentially a very big problem. The LSR can (and often
will) have
> multiple bits set, so you will almost always be taking the default case
> even if THRE is set (thus my question above). Given that the
default case
> is echoing whatever is in U0RBR (BTW what is CTI?) I would expect
almost
> anything if no characters had been sent to the port. Change this
before
> trying anything else. Then put a pin toggle or something in so you
know if
> the interrupt routine is ever reached.
>
I think that it would rarely take the default case since it would
match the first one and then break.

In previous version I tried enabling all of the interrupts in U0IIR,
the ISR still didn't work, so I was trying something different here.

CTI is character time out. It will generate the RBR interrupt even if
the threshold has not been reached after 3.5-4.5 character times, so
you know you have data waiting. >
> This is potentially a problem. I haven't looked at in detail but
you are
> using the same buffer routine here as you are in the non-interrupt
> code. The ground is very unstable here.
I removed the vectored irq setup, still doesn't work.

>
> And if U0IIR is read and used you don't need this line. In fact if
U0IIR
> is declared volatile (as it should be) you only would need this line
if the
> compiler was buggy.

It is defined as volatile (using keil's development tools).



At 05:55 PM 10/8/04 +0000, you wrote:
>--- In , Robert Adsett <subscriptions@a...> wrote:
> > First a question, you say the interrupt is not being generated, how
>do you
> > know?
> >
>While I was debugging w/ JTAG, I put a breakpoint in the isr, it was
>never reached. I'd turn on an LED if one existed for my dev board.

OK, but where in the interrupt?
> >
> > This is potentially a very big problem. The LSR can (and often
>will) have
> > multiple bits set, so you will almost always be taking the default case
> > even if THRE is set (thus my question above). Given that the
>default case
> > is echoing whatever is in U0RBR (BTW what is CTI?) I would expect
>almost
> > anything if no characters had been sent to the port. Change this
>before
> > trying anything else. Then put a pin toggle or something in so you
>know if
> > the interrupt routine is ever reached.
> >
>I think that it would rarely take the default case since it would
>match the first one and then break.

No, switch does not work that way. It is an exact match, not a bit
mask. And having multiple bits set is not only possible but likely, at the
very least TEMT and THRE are often on simultaneously. >In previous version I tried enabling all of the interrupts in U0IIR,
>the ISR still didn't work, so I was trying something different here.
>
>CTI is character time out. It will generate the RBR interrupt even if
>the threshold has not been reached after 3.5-4.5 character times, so
>you know you have data waiting.

Ah, there is no separate LSR status bit for character timeout. All that
happens is IIR gets set to indicate a received character and trigger an
interrupt. LSR will simply indicate via RBR. The LSR does not 'pay
attention' to the threshold level, RBR will be set as long as at least one
character is waiting.
> >
> > This is potentially a problem. I haven't looked at in detail but
>you are
> > using the same buffer routine here as you are in the non-interrupt
> > code. The ground is very unstable here.
>I removed the vectored irq setup, still doesn't work.

This is not a matter of vectored or not. > >
> > And if U0IIR is read and used you don't need this line. In fact if
>U0IIR
> > is declared volatile (as it should be) you only would need this line
>if the
> > compiler was buggy.
>
>It is defined as volatile (using keil's development tools).

Then the increment is unneeded (and could be optimized out anyway). In any
case there is virtually no chance of this working until you fix the switch
statement (well eliminate the switch statement).

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





> OK, but where in the interrupt?
The first line of code that should always be executed.

>
> No, switch does not work that way. It is an exact match, not a bit
> mask. And having multiple bits set is not only possible but likely,
at the
> very least TEMT and THRE are often on simultaneously.
>
You are correct, I was just being stupid.

> > > This is potentially a problem. I haven't looked at in detail but
> >you are
> > > using the same buffer routine here as you are in the non-interrupt
> > > code. The ground is very unstable here.
> >I removed the vectored irq setup, still doesn't work.
>
> This is not a matter of vectored or not.

If I removed the vectored setup, then there is only one interrupt left
to call the isr, so there should not be a problem. I gave up on that and started over, and this is what I think I am
doing. I start by disabling all interrupts globally and for the uart.
Then I enable only the RBR interrupt, then the uart vic. In the main
I print out 123 by using a for loop as a delay. I setup the rbr isr
to print out '!' if it receives a character. Now the problem is when
I send 2 characters the processor resets. Is there something wrong w/
my code?

#include <stdio.h>
#include "../ArmCommon/types.h"
#include "../ArmCommon/LPC22xx.h"
#include "../ArmCommon/LPC2294.h"

#define TX_BUFFER_LENGTH 64

static Uint16 txBytes = 0;
static Uint16 txRdPos = 0;
static Uint16 txWrPos = 0;
static Uint16 txBuffer[TX_BUFFER_LENGTH];

void uart0_config (void);
void uart0_isr (void);
void uart0_sendByte (Uint8);
int putchar(int);
int main (void);
Uint32 irqBackup;

void disableInterrupts (void);
void enableInterrupts (void);

void disableInterrupts (void) {
irqBackup = VICIntEnable;
VICIntEnable = 0;
}
void enableInterrupts (void) {
VICIntEnable = irqBackup;
}

void uart0_config (void) {
volatile Uint8 x;
// Setup UART 0
U0LCR = 0x80; // Enable DLAB
U0DLL = 0x36; // Setup BGR
U0DLM = 0x00; // ~57.6 kbps @ 50 Mhz
U0LCR = 0x03; // Enable UART 0, 8 bits, no parity, 1 stop bit

// Reset the FIFOs
U0FCR = 0xC7; // RX FIFO trigger level 8 chars, reset tx and rx fifo,
enable fifos

// Clear any old irqs
x = U0LSR;
x = U0RBR;
x = U0IIR;

// Setup Interrupts for UART0
U0IER = 0x1; // Only RBR IRQ on
VICIntEnable |= (0x1 << 6); // Enable Uart0 Interrupt
VICVectAddr6 = (Uint32) uart0_isr;
VICVectCntl6 = 0x20 | 6;
}

void uart0_isr (void) __irq {
volatile Uint8 x;
uart0_sendByte('!');

// Clear IRQ
x = U0LSR;
x = U0IIR;
x = U0RBR;
VICVectAddr = 0;
}

void uart0_sendByte(Uint8 a) {
// Wait until there is room in the buffer
while(txBytes == TX_BUFFER_LENGTH);
if(U0LSR&0x20) { // If the chip buffer is empty, put it there
U0THR = a;
}
else { // Put in software buffer
disableInterrupts();
txBuffer[txWrPos] = a;
txWrPos++;
txWrPos = (txWrPos == TX_BUFFER_LENGTH)?0:txWrPos;
txBytes++;
enableInterrupts();
}
}

int putchar(int a) {
uart0_sendByte((int)a);
return 0;
}

int main (void) {
Uint16 x;
PINSEL0 = PINSEL0_00_TxD | PINSEL0_01_RxD;
VPBDIV = 0x1;
VICIntSelect = 0; // Only IRQs, no FIQs
VICIntEnClr = 0xFFFFFFFF; // Disable all irqs
uart0_config();
// Proof that it can send more than one character
printf("1");
for(x = 0; x < 1000; x++);
printf("2");
for(x = 0; x < 1000; x++);
printf("3");

while(1);
}



At 08:44 PM 10/8/04 +0000, you wrote:
> > OK, but where in the interrupt?
>The first line of code that should always be executed.
This may seem obtuse, but first line of C or asm? A good optimizer on the
ARM may change the order of execution in non-obvious ways. > > > > This is potentially a problem. I haven't looked at in detail but
> > >you are
> > > > using the same buffer routine here as you are in the non-interrupt
> > > > code. The ground is very unstable here.
> > >I removed the vectored irq setup, still doesn't work.
> >
> > This is not a matter of vectored or not.
>
>If I removed the vectored setup, then there is only one interrupt left
> to call the isr, so there should not be a problem.

It doesn't matter if it is 1 interrupt or 10 the potential for a problem
still exists, you are calling the same buffer for both the interrupt and
mainline code.
>I gave up on that and started over, and this is what I think I am
>doing. I start by disabling all interrupts globally and for the uart.
> Then I enable only the RBR interrupt, then the uart vic. In the main
>I print out 123 by using a for loop as a delay. I setup the rbr isr
>to print out '!' if it receives a character. Now the problem is when
>I send 2 characters the processor resets. Is there something wrong w/
>my code?

Resetting may actually be a good thing here ;) At least it suggests to me
that you might actually be getting your interrupt this time. >#include <stdio.h>
>#include "../ArmCommon/types.h"
>#include "../ArmCommon/LPC22xx.h"
>#include "../ArmCommon/LPC2294.h"
>
>#define TX_BUFFER_LENGTH 64
>
>static Uint16 txBytes = 0;
>static Uint16 txRdPos = 0;
>static Uint16 txWrPos = 0;
>static Uint16 txBuffer[TX_BUFFER_LENGTH];
>
>void uart0_config (void);
>void uart0_isr (void);
>void uart0_sendByte (Uint8);
>int putchar(int);
>int main (void);
>Uint32 irqBackup;
>
>void disableInterrupts (void);
>void enableInterrupts (void);
>
>void disableInterrupts (void) {
> irqBackup = VICIntEnable;
> VICIntEnable = 0;
>}
>void enableInterrupts (void) {
> VICIntEnable = irqBackup;
>}

I'd feel a lot better about these if you were really disabling and
restoring interrupts at the CPU level rather than just the VIC flags, but I
don't think that's your problem source. >void uart0_config (void) {
> volatile Uint8 x;
> // Setup UART 0
> U0LCR = 0x80; // Enable DLAB
> U0DLL = 0x36; // Setup BGR
> U0DLM = 0x00; // ~57.6 kbps @ 50 Mhz
> U0LCR = 0x03; // Enable UART 0, 8 bits, no parity, 1 stop bit
>
> // Reset the FIFOs
> U0FCR = 0xC7; // RX FIFO trigger level 8 chars, reset tx and rx fifo,
>enable fifos
>
> // Clear any old irqs
> x = U0LSR;
> x = U0RBR;
> x = U0IIR;
>
> // Setup Interrupts for UART0
> U0IER = 0x1; // Only RBR IRQ on
> VICIntEnable |= (0x1 << 6); // Enable Uart0 Interrupt
> VICVectAddr6 = (Uint32) uart0_isr;
> VICVectCntl6 = 0x20 | 6;
>}
>
>void uart0_isr (void) __irq {
> volatile Uint8 x;
> uart0_sendByte('!');

There are problems here. First you never empty the buffer so the buffer
becomes blocked very quickly. Now watch what happens when you send a
string of bytes through that will fill the buffer up.

- First byte gets written at point A--
- Second byte comes along right behind it and also gets written at point
A-- since the first byte has moved into the shift register.
- The rest of the buffer gets filled up via the else clause (Point B--) and
then
- the next call sits in the infinite loop at point C--
- Shortly after the UART finally catches up and finishes sending the first
character and moves the second character into the shift register while
flagging an interrupt.
- The interrupt fires and the first thing that occurs is a call to
uart0_sendByte which goes into an infinite loop at point C-- since the
buffer is full.

You get two characters out, one interrupt fires and you never exit the
interrupt routine. Unless you have a watchdog enabled I would expect this
to hang, not reset.

Unless the interrupt stack isn't setup correctly. I'm not familiar with
the startup code for your compiler so someone else might be better suited
to suggest if that is an issue. > // Clear IRQ
> x = U0LSR;
> x = U0IIR;
> x = U0RBR;
> VICVectAddr = 0;
>}
>
>void uart0_sendByte(Uint8 a) {
> // Wait until there is room in the buffer

C--

> while(txBytes == TX_BUFFER_LENGTH);

A---

> if(U0LSR&0x20) { // If the chip buffer is empty, put it there
> U0THR = a;
> }

B--

> else { // Put in software buffer
> disableInterrupts();
> txBuffer[txWrPos] = a;
> txWrPos++;
> txWrPos = (txWrPos == TX_BUFFER_LENGTH)?0:txWrPos;
> txBytes++;
> enableInterrupts();
> }
>}
>
>int putchar(int a) {
> uart0_sendByte((int)a);
> return 0;
>}
>
>int main (void) {
> Uint16 x;
> PINSEL0 = PINSEL0_00_TxD | PINSEL0_01_RxD;
> VPBDIV = 0x1;
> VICIntSelect = 0; // Only IRQs, no FIQs
> VICIntEnClr = 0xFFFFFFFF; // Disable all irqs
> uart0_config();
> // Proof that it can send more than one character
> printf("1");
> for(x = 0; x < 1000; x++);
> printf("2");
> for(x = 0; x < 1000; x++);
> printf("3");
>
> while(1);
>}

Remember there are three places to disable/enable interrupts from the UART

- the uart itself
- the VIC peripheral
- the CPU

To avoid subtle phase problems (especially when first starting to work with
interrupts) it makes sense as a simple precaution to disable interrupts at
the CPU level when enabling or disabling them at the other levels.

Again I'd suggest looking at other UART code for this processor (take a
look at the pointers from an earlier post). " '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


Have a look at UT040803A.zip in the files section on Yahoo.
It contains operational uart code for both polled and interrupt
modes of operation.

Regards
-Bill Knight
www.theARMPatch.com



Thanks, Bill.

I was about to email you about that actually. I had just changed all
the code necessary for it to compile on the Keil's compiler. The
strange thing is that it doesn't work with their simulator, but it
works on the real hardware. All the code I wrote worked on the
simulator, but not the real hardware. Guess I'll have to complain.

Would you like me to email you the changed code for you to post or for
reference? I think the only things that I needed to change were the
inline assembly and I had to make the unions in uartRegs_t non-anonymous.

--Peter

--- In , "Bill Knight" <BillK@t...> wrote:
> Have a look at UT040803A.zip in the files section on Yahoo.
> It contains operational uart code for both polled and interrupt
> modes of operation.
>
> Regards
> -Bill Knight
> www.theARMPatch.com




Writing 0 to VICVECTADDRESS release interrupt priority level into the
VIC see ARM VIC primecell specification

Je

--- In , Robert Adsett <subscriptions@a...> wrote:
> At 02:33 PM 10/8/04 +0000, you wrote:
> >I do now have the VicVectAddr = 0;, but that doesn't help if it
> >doesn't get called.
>
> Speaking of which, I just reviewed your code. You don't appear to be
> actually calling your uart initialization code anywhere (I don't see
how
> you would get your baud rate right if you hadn't but...). Also you
should
> probably disable interrupts before disabling a specific interrupt via
> VICIntEnClr
>
> 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