LPC UART0 interrupt problems

Hi, everyone.

I am a newbie in LPC2000. Now I have a LPC2210 DVK. I want to use the uart0 to receive data in the interrupt way, not polling. But things do not work well. Now the LPC2210 CPU can receive some data, several bytes, after that, it does not work any more. Any one can help me? Thank you!

The startup part of the project is OK, and I can send data to the PC with the UART0 using the polling way. I run the program in the internal ram. And I use the super terminal on a PC to send some data to the UART0.

I use cygwin to compile my project.

Here’s the source code.

#define BUF_SIZE 2048

#define bps 19200

static char Buf[BUF_SIZE];

static uint32 Hard = 0;

static volatile uint32 End = 0;

#define IRQ_Enable() asm volatile(" mrs r0, cpsr\n"\

" bic r0, r0, #0x80\n"\

" msr cpsr_c, r0")

#define ISR_ENTRY() asm volatile(“sub lr, lr, #4\n”\

“stmfd sp!, {r0-r12, lr}\n”\

“mrs r1, spsr\n”\

“stmfd sp!, {r1}”)

#define ISR_EXIT() asm volatile(“ldmfd sp!, {r1}\n”\

“msr spsr_c, r1\n”\

“ldmfd sp!, {r0-r12, pc}^”)

//the exception handler

void UART0_Exception(void) attribute ((interrupt(“IRQ”)));

void UART0_Exception(void)

{

ISR_ENTRY();

uint8 temp = U0IIR;

while ((U0LSR & 0x00000001) != 0)

{

Buf[End++] = U0RBR;

if (End >= BUF_SIZE)

{

End = 0;

}

}

VICVectAddr = 0;

ISR_EXIT();

}

void CommInit(void)//initial the UART0

{

uint16 Fdiv;

PINSEL0 = (PINSEL0 & 0xfffffff0) | 0x05;

U0LCR = 0x80;

Fdiv = (Fpclk / 16) / bps;

U0DLM = Fdiv / 256;

U0DLL = Fdiv % 256;

U0LCR = 0x03;

U0IER = 0x00;

U0FCR = 0x87;

Hard = 0;

End = 0;

VICIntEnClr = 0xffffffff;

VICIntSelect &= 0xffffffbf;

VICVectAddr14 = (uint32)UART0_Exception;

VICVectCntl14 = (0x20 | 0x06);

VICIntEnable = 1 << 6;

U0IER = 0x01;

}

char GetCh(void)// get a character from the buffer

{

char temp;

while (Hard == End);

temp = Buf[Hard++];

if (Hard >= BUF_SIZE)

{

Hard = 0;

}

SendChar(temp);

return temp;

}

void SendChar(char data)

{

while((U0LSR & 0x00000020) == 0);

U0THR = data;

}

//initial the CPU, clock, etc.

void TargetResetInit(void)

{

MEMMAP = 0x02;//Remap to internal RAM

PLLCON = 1;

#if ((Fcclk / 4) / Fpclk) == 1

VPBDIV = 0;

#endif

#if ((Fcclk / 4) / Fpclk) == 2

VPBDIV = 2;

#endif

#if ((Fcclk / 4) / Fpclk) == 4

VPBDIV = 1;

#endif

#if (Fcco / Fcclk) == 2

PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);

#endif

#if (Fcco / Fcclk) == 4

PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);

#endif

#if (Fcco / Fcclk) == 8

PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);

#endif

#if (Fcco / Fcclk) == 16

PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);

#endif

PLLFEED = 0xaa;

PLLFEED = 0x55;

while((PLLSTAT & (1 << 10)) == 0);

PLLCON = 3;

PLLFEED = 0xaa;

PLLFEED = 0x55;

MAMCR = 0;

#if Fcclk < 20000000

MAMTIM = 1;

#else

#if Fcclk < 40000000

MAMTIM = 2;

#else

MAMTIM = 3;

#endif

#endif

MAMCR = 2;

VICIntEnClr = 0xffffffff;

VICVectAddr = 0x00000000;

VICIntSelect = 0x00000000;

}

// in the main function. things seems to be:

int main()

{

char tempdata;

TargetResetInit();

CommInit();

IRQ_Enable();

while(1)

{

tempdata = GetCh();

SendChar(tempdata);

}

return 0;

}

Regards,

Kevin

Maybe I should also post the setup source code here.

/* ***************************************************************************************************************

crt.s STARTUP ASSEMBLY CODE


Module includes the interrupt vectors and start-up code.

*************************************************************************************************************** */

/* Port Configure Address */

.set PINSEL2, 0xe002c014

.set BCFG0, 0xffe00000

.set BCFG1, 0xffe00004

.set BCFG2, 0xffe00008

/* Stack Sizes */

.set UND_STACK_SIZE, 0x00000080 /* stack for “undefined instruction” interrupts is 4 bytes */

.set ABT_STACK_SIZE, 0x00000080 /* stack for “abort” interrupts is 4 bytes */

.set FIQ_STACK_SIZE, 0x00000080 /* stack for “FIQ” interrupts is 4 bytes */

.set IRQ_STACK_SIZE, 0X00000080 /* stack for “IRQ” normal interrupts is 0x80 bytes */

.set SVC_STACK_SIZE, 0x00000080 /* stack for “SVC” supervisor mode is 4 bytes */

/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */

.set MODE_USR, 0x10 /* Normal User Mode */

.set MODE_FIQ, 0x11 /* FIQ Processing Fast Interrupts Mode */

.set MODE_IRQ, 0x12 /* IRQ Processing Standard Interrupts Mode */

.set MODE_SVC, 0x13 /* Supervisor Processing Software Interrupts Mode */

.set MODE_ABT, 0x17 /* Abort Processing memory Faults Mode */

.set MODE_UND, 0x1B /* Undefined Processing Undefined Instructions Mode */

.set MODE_SYS, 0x1F /* System Running Priviledged Operating System Tasks Mode */

.set I_BIT, 0x80 /* when I bit is set, IRQ is disabled (program status registers) */

.set F_BIT, 0x40 /* when F bit is set, FIQ is disabled (program status registers) */

.text

.arm

.global Reset_Handler

.global _startup

.global Run

.func _startup

_startup: ldr PC, Reset_Addr

ldr PC, Undef_Addr

ldr PC, SWI_Addr

ldr PC, PAbt_Addr

ldr PC, DAbt_Addr

nop /* Reserved Vector (holds Philips ISP checksum) */

ldr PC, [PC,#-0xFF0] /* see page 71 of “Insiders Guide to the Philips ARM7-Based Microcontrollers” by Trevor Martin */

ldr PC, FIQ_Addr

Reset_Addr: .word Reset_Handler /* defined in this module below */

Undef_Addr: .word UNDEF_Routine /* defined in main.c */

SWI_Addr: .word SWI_Routine /* defined in main.c */

PAbt_Addr: .word UNDEF_Routine /* defined in main.c */

DAbt_Addr: .word UNDEF_Routine /* defined in main.c */

IRQ_Addr: .word IRQ_Routine /* defined in main.c */

FIQ_Addr: .word FIQ_Routine /* defined in main.c */

.word 0 /* rounds the vectors and ISR addresses to 64 bytes total */

.=.+0x1C0 /* skip past Philips ISP ram usage (all the way to 0x40000200) */

Reset_Handler:

/* Setup a stack for each mode - note that this only sets up a usable stack

for User mode. Also each mode is setup with interrupts initially disabled. */

ldr r0, =PINSEL2

ldr r1, =0x0f814914

str r1, [r0]

ldr r0, =BCFG0

ldr r1, =0x1000ffef

str r1, [r0]

ldr r0, =BCFG1

ldr r1, =0x1000ffef

str r1, [r0]

ldr r0, =_stack_end

msr CPSR_c, #MODE_UND|I_BIT|F_BIT /* Undefined Instruction Mode */

mov sp, r0

sub r0, r0, #UND_STACK_SIZE

msr CPSR_c, #MODE_ABT|I_BIT|F_BIT /* Abort Mode */

mov sp, r0

sub r0, r0, #ABT_STACK_SIZE

msr CPSR_c, #MODE_FIQ|I_BIT|F_BIT /* FIQ Mode */

mov sp, r0

sub r0, r0, #FIQ_STACK_SIZE

msr CPSR_c, #MODE_IRQ|I_BIT|F_BIT /* IRQ Mode */

mov sp, r0

sub r0, r0, #IRQ_STACK_SIZE

msr CPSR_c, #MODE_SVC|I_BIT|F_BIT /* Supervisor Mode */

mov sp, r0

sub r0, r0, #SVC_STACK_SIZE

msr CPSR_c, #MODE_SYS|I_BIT|F_BIT /* User Mode */

mov sp, r0

/* copy .data section (Copy from ROM to RAM) */

ldr R1, =_etext

ldr R2, =_data

ldr R3, =_edata

1: cmp R2, R3

ldrlo R0, [R1], #4

strlo R0, [R2], #4

blo 1b

/* Clear .bss section (Zero init) */

mov R0, #0

ldr R1, =_bss_start

ldr R2, =_bss_end

2: cmp R1, R2

strlo R0, [R1], #4

blo 2b

/* Enter the C code */

b main

UNDEF_Routine:

b UNDEF_Routine

SWI_Routine:

b SWI_Routine

IRQ_Routine:

b IRQ_Routine

FIQ_Routine:

b FIQ_Routine

.endfunc

.end

I can’t quickly tell from what you have there what the problem is or could be.

I’d reccomend comparing what you have to some other interrupt driven UART examples, like the ones found at:

http://www.siwawi.arubi.uni-kl.de/avr_p … _projects/

Just from reading the code, no tests:

The ISR_ENTRY() and ISR_EXIT() macros contans almost the same

code that the compiler generates automatically when you specify

attribute ((interrupt(“IRQ”)))

You can check this by a looking at the assembly code listing.

So you are pushing the registers onto the stack twice, but they will

only be popped once since the interrupt handler will exit at the

end of our ISR_EXIT(), this will overflow the small IRQ stack after a few interrupts.

If you use attribute ((interrupt(“IRQ”))) then the compiler generates all the necessary entry and exit code.

If you want to do this yourself declare interrupthandler with

attribute ((naked))

Then you must handle all the entry and exit code as in your ISR_ENTRY and ISR_EXIT macros.

Regards

Magnus

mlu:
Just from reading the code, no tests:

The ISR_ENTRY() and ISR_EXIT() macros contans almost the same

code that the compiler generates automatically when you specify

attribute ((interrupt(“IRQ”)))

Magnus

I’ve read a few ‘coder beware’ comments about trusting the IRQ attribute. The gist is IRQ attribute is awsome when it works, but people have found bugs in it in different versions of the gcc compiler, and find it’s buggy for some chips.

I am using the IRQ attribute on one project (for LPC2148) and it seems to work fine. I’m also using the IRQ_ENTRY/IRQ_EXIT on another project for the same chip. I myself haven’t encountered that problem.

My point is that using both at the same time is probably a very bad idea, but the actual code generated must be checked.

I use the attribute ((interrupt(“IRQ”))) for arm7 interrupt coding and it works well.

I can also agree that some of the generated entry/exit code feels suboptimal, but I

probably would introduce more errors and waste more time by trying to write my own.

Regards

Magnus

Hi,

I read the document about GCC compiler, which is Using and Porting the GNU Compiler Collection by Richard M.Stallman last updated 22 June 2001(for GCC 3.1), and it says the compiler itself will not save the registers’ value into the stack; users must do it themselves. Am I right? If any mistake, please correct me.

Regards,

Kevin

Hello

Where in the document did you find that ?

The gcc complier 3.4, 4.01 and 4.1 does generate interrupt handler entry and exit code,

saving and restoring all registers that are used in the interrupt handler to the stack.

And it works for me without problems, I have have also checked the generated asembly code and it looks ok.

There has been reports of incorrect code with earlier wersions of gcc, but that is fixed.

Regards

Magnus