EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Strange Problem with Interrupts on LPC2119

Started by buckeyes1997 December 14, 2009
Hello

I am fairly new to this family of processors having mostly worked with AVR in the past. Anyway I have a temperature control application which is mostly running HOWEVER. I have two interrupts UART0 and Timer0. Timer happens about 2Hz and the UART0 is on received characters.

I am setting the setpoint via serial commands as well as my PC software polls results via the serial. The system runs fine for anywhere from a few seconds to a few minutes before it goes into a strange state and sits there. If I dont poll for results with the ">e" command from my windows app it runs indefinately. I think I am getting stuck in an undefined interrupt or something??

I am using Keil with GCC.
-------------------------------

startup.s section for interrupt vectors

Vectors: LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP /* Reserved Vector */
LDR PC, [PC, #-0xFF0]
LDR PC, FIQ_Addr

Reset_Addr: .word Reset_Handler
Undef_Addr: .word Reset_Handler
SWI_Addr: .word Reset_Handler
PAbt_Addr: .word Reset_Handler
DAbt_Addr: .word Reset_Handler
.word 0 /* Reserved Address */
IRQ_Addr: .word IRQ_Handler
FIQ_Addr: .word Reset_Handler

Undef_Handler: B Reset_Handler
SWI_Handler: B Reset_Handler
PAbt_Handler: B Reset_Handler
DAbt_Handler: B Reset_Handler
IRQ_Handler: B IRQ_Handler
-------------------------

Main Code:
/*****************************************************/
/*
Temperature Controller Test Firmware v1.0

*/
/*****************************************************/
#include
#include
#include
#include

void init_ADC(void);
void init_PWM(void);
void TI0_irq(void) __attribute__ ((interrupt("IRQ")));
void TI_setup(void);
void uart0_irq(void) __attribute__((interrupt("IRQ")));
void initFiq (void);
extern void init_serial0 (void);
extern int putchar (int ch);
extern int getchar (void);

float error;
volatile int sFlag;
int cFlag;
volatile int UCount;
volatile char Buffer[10];
unsigned int U0IIR_copy;
unsigned int val;
float volt;
float sumerror;
float setpoint;
volatile float Pterm;
volatile float Iterm;
volatile float Dterm;
int Iflag;

int main(void)
{
Pterm000;
Iterm0 ;
Dterm=0 ;
Iflag=0;
cFlag=0;
sFlag=0;
UCount=0;
setpoint=0.0;
PINSEL0 = 0x00000000;
IODIR0 = 0x7BFFFFFC;
IOSET0 = 0x03C00000;
init_ADC();
init_PWM();
init_serial0();
TI_setup();

// Initial UART0 = 9600,N,8,1
printf("\nTemperature Controller v1.0\n");
while(1) // Loop Continue
{
IOSET0 = 0x03C00000;
if(sFlag!=0)
{
switch (Buffer[0])
{
case 'e' :
case 'E' :
{
IOCLR0 = 9<<22;
printf("%2.3f,%2.3f\n",volt,error);
sFlag=0;
break; }

case 'p' :
case 'P' :
{
Buffer[0]2; //replace 's' with space to pass atof()
double result=atof((char *)Buffer);
printf("\nOld Pterm=%7.1f New Pterm= %7.1f\n",Pterm,result);
Pterm=result;
sFlag=0;
break; }

case 'i' :
case 'I' :
{
Buffer[0]2; //replace 's' with space to pass atof()
double result=atof((char *)Buffer);
printf("\nOld Iterm= %7.1f New Iterm= %7.1f\n",Iterm,result);
Iterm=result;
sFlag=0;
break; }

case 'd' :
case 'D' :
{
Buffer[0]2; //replace 's' with space to pass atof()
double result=atof((char *)Buffer);
printf("\nOld Dterm= %7.1f New Dterm= %7.1f\n",Dterm,result);
Dterm=result;
sFlag=0;
break; }

case 'S':
case 's' : {
Buffer[0]2; //replace 's' with space to pass atof()
double result=atof((char *)Buffer);
setpoint=result;
printf("\nNew Setpoint= %f\n",result);
sFlag=0;
break; }
default: {
printf("\nNot VALID!!");
sFlag=0;
break; }
}
}

}
}

void uart0_irq(void)
{

//check for a character from the UART0 RX FIFO
if((U0LSR & 0x01) == 0x01) //if there is a character
{
U0IIR_copy = U0IIR;
U0IIR_copy=U0RBR;

if(U0IIR_copy!)
{
if(U0IIR_copy=='>')
{
UCount=0;
cFlag=1;
}
else
{
Buffer[UCount]=U0IIR_copy;
UCount+=1;
}
if(UCount>)
{
UCount=0;
sFlag=0;
cFlag=0;
}
}
else
{
if((cFlag==1) & (UCount!=0))
{
sFlag=1;
cFlag=0;
}
}
VICVectAddr = 0; //dummy write to re-enable interrupt for next time
}
else
{
U0IIR_copy = U0MSR;
U0IIR_copy = U0LSR;
U0IIR_copy = U0IIR;
U0IIR_copy=U0RBR;
VICVectAddr = 0; //dummy write to re-enable interrupt for next time
}

}
void TI_setup(void)
{

T0MR0 = 15000000; // Compare-hit at 10mSec (-1 reset "tick")
T0MCR = 3; // Interrupt and Reset on MR0
T0TCR = 1; // Timer0 Enable

VICVectAddr0 = (unsigned)TI0_irq; // set interrupt vector in 0
VICVectCntl0 = 0x24; // use it for Timer 0 Interrupt:
VICIntEnable |= (1<<4); // Enable Timer0 Interrupt

}

void TI0_irq(void)
{
IOCLR0 = 4<<22;

do // Loop Read ADC0
{
val = ADDR; // Read A/D Data Register
}
while ((val & 0x80000000) == 0);
val = (val >> 6) & 0x03FF; // Shift ADC Result to Integer
volt = val * 3.01 / 1023.0;

float Perror;
float PID;
error=setpoint-volt;

if((error>-.1) & (error<.1))
{
sumerror+=error;
}
else
{
sumerror=sumerror/2.0;
}
Perror=error*Pterm;

if(Perror<0)Perror=0;

PID=Perror+(sumerror*Iterm);

if(PID>0x3ff)PID=0x3ff;
if(PID<0){PID=0;}

PWMMR2 = (int) PID; // Update PWM2(High Period = 0..1023 Cycle)
PWMLER = 0x00000004;

// printf("%2.3f, %2.3f\n",volt,error); // , %d, %4.0f, %4.0f \n",(volt),error,PWMMR2,Perror,sumerror*100);

T0IR = 1; // Clear interrupt flag by writing 1 to Bit 0
VICVectAddr = 0; // Acknowledge Interrupt (rough?)
}

void init_PWM(void)
{
// Initial PWM2 (GPIO-0.7) By Set PINSEL0[15:14]
PINSEL0 &= 0xFFFF3FFF; // Select PWM2 Pin Connect P0.7
PINSEL0 |= 0x00008000;
// Initial PWM2 = 28.8KHz (29.4912/1024(.800 KHz)
// Period = 34.722 uS
PWMPR = 2; // 35uS Period -> 28.80KHz
//Initial PWM2 = Double Edge PWM
// Set Output By Match-0
// Reset Output By Match-2
PWMPCR &= 0xFFFFFFFB; // PWMSEL2 = "1" = Single Edge Control
PWMPCR |= 0x00000400; // PWMENA2 = "1" = Enable PWM2
PWMMCR = 0x00000002; // On Match0 = Reset Counter
PWMMR0 = 0x00000400; // Set PWM2 Rate(29.4912MHz/1024)
PWMMR2 = 0x00000000; // Set Default PWM2 High Pulse 0 duty (0..512 Cycle)
PWMLER = 0x00000005; // Enable Shadow Latch For Match 0,2
PWMTCR = 0x00000002; // Reset Counter and Prescaler
PWMTCR = 0x00000009; // Enable Counter and PWM + Release Counter From Reset
PWMMR2 = 0x00000000; // Update PWM2(High Period = 0..1023 Cycle)
PWMLER = 0x00000004;

}
void init_ADC(void)
{
// Initial ADC0 (GPIO-0.27) By Set PINSEL1[23:22]
PINSEL1 &= 0xFF7FFFFF; // Select ADC0 Pin Connect P0.27
PINSEL1 |= 0x00400000;

// Initial ADC0 (ADCR=0x01210601)
ADCR &= 0x00000000; // Clear All Bit Control
ADCR |= 0x00000001; // Select ADC = AIN0
ADCR |= 0x00000e00; // ADC Clock = VBP(PCLK) / 7
ADCR |= 0x00010000; // Busrt = 1 = Conversion Continue
ADCR &= 0xFFF1FFFF; // CLKS = 000 = 10Bit : 11 Cycle Clock Conversion
ADCR |= 0x00200000; // PDN = 1 = Active ADC Module
ADCR &= 0xFF3FFFFF; // TEST[1:0] = 00 = Normal Mode
ADCR &= 0xF7FFFFFF; // EDGE = 0 = Conversion on Falling Edge
ADCR |= 0x01000000; // START = 001 = Start Conversion Now
// Start Test Read ADC0 and Display on UART0 //
}

An Engineer's Guide to the LPC2100 Series

Hi,

Just a general suggestion: don't put data processing on interrupt service routine. I don't think you system is hang anyway, it just that it lost some of the timer interrupts, so it looks like not-responsive / hang.

Also, the easier way to check if it goes to exception mode (Undefined, data abort, prefetch abort, etc) is by connecting the interrupt handler to it. Eg:
------------------
Vectors: LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP /* Reserved Vector */
LDR PC, [PC, #-0xFF0]
LDR PC, FIQ_Addr

Reset_Addr: .word Reset_Handler
Undef_Addr: .word Blink_LED0
SWI_Addr: .word Reset_Handler
PAbt_Addr: .word Blink_LED1
DAbt_Addr: .word Blink_LED2
.word 0 /* Reserved Address */
IRQ_Addr: .word IRQ_Handler
FIQ_Addr: .word Reset_Handler
------------------

Of course, you must provide the functions (Blink_LED0(), Blink_LED1(), and Blink_LED2()) somewhere. The other choice is to put exception handler that'll print the R14 (LR register) and the current exception state to the terminal, so you can track which address that causes the exception.

Regards,
-daniel
From: l... [mailto:l...] On Behalf Of buckeyes1997
Sent: Tuesday, December 15, 2009 5:59 AM
To: l...
Subject: [lpc2000] Strange Problem with Interrupts on LPC2119


Hello

I am fairly new to this family of processors having mostly worked with AVR in the past. Anyway I have a temperature control application which is mostly running HOWEVER. I have two interrupts UART0 and Timer0. Timer happens about 2Hz and the UART0 is on received characters.

I am setting the setpoint via serial commands as well as my PC software polls results via the serial. The system runs fine for anywhere from a few seconds to a few minutes before it goes into a strange state and sits there. If I dont poll for results with the ">e" command from my windows app it runs indefinately. I think I am getting stuck in an undefined interrupt or something??

I am using Keil with GCC.
-------------------------------

startup.s section for interrupt vectors

Vectors: LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP /* Reserved Vector */
LDR PC, [PC, #-0xFF0]
LDR PC, FIQ_Addr

Reset_Addr: .word Reset_Handler
Undef_Addr: .word Reset_Handler
SWI_Addr: .word Reset_Handler
PAbt_Addr: .word Reset_Handler
DAbt_Addr: .word Reset_Handler
.word 0 /* Reserved Address */
IRQ_Addr: .word IRQ_Handler
FIQ_Addr: .word Reset_Handler

Undef_Handler: B Reset_Handler
SWI_Handler: B Reset_Handler
PAbt_Handler: B Reset_Handler
DAbt_Handler: B Reset_Handler
IRQ_Handler: B IRQ_Handler

----------------------

Main Code:

/*****************************************************/
/*
Temperature Controller Test Firmware v1.0

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

#include
#include
#include
#include

void init_ADC(void);
void init_PWM(void);
void TI0_irq(void) __attribute__ ((interrupt("IRQ")));
void TI_setup(void);
void uart0_irq(void) __attribute__((interrupt("IRQ")));
void initFiq (void);
extern void init_serial0 (void);
extern int putchar (int ch);
extern int getchar (void);

-------------cut----------------------
buckeyes1997 wrote:
> if(U0IIR_copy!)

Surely you did mean RBR here, didn't you.

--

Timo

U0IIR_copy is a register copy of RBR. I saw that was done in one of the online examples so I just used it. It seems to work.

I like the idea of either printing to terminal or LED_Blink which interrupt case its getting stuck in.

I guess once I find that, I will need to just clear the random interrupt to continue on?

Thanks, I really appreciate all the help.

matt

--- In l..., tike64@... wrote:
>
> buckeyes1997 wrote:
> > if(U0IIR_copy!)
>
> Surely you did mean RBR here, didn't you.
>
> --
>
> Timo
>

buckeyes1997 wrote:
> U0IIR_copy is a register copy of RBR. I saw that was done in one of the
> online examples so I just used it. It seems to work.

Ok, I didn't see that. That's a very good start for an obfuscated code
contest success ;)

I don't see init_serial0 either. If it enables extra interrupts, I think
you could get an interrupt loop.

When the system freezes, do you know which one freezes, the LPC2119
program or the PC program?

--

Timo

The init_serial0 routine is below. It is definately the LPC2119 that freezes. It always goes to the same state as noted by LED's on the board that normally flash during timer0 interrupt and during uart0 interrpt. When it freezes both LEDs light and stay lit until I reset the board.

I will test for which interrupt is being fired accidentally and post back shortly. It almost seems like a collision between the two interrupts because when only 1 (either one) runs it never hiccups.

Thanks
Matt

void init_serial0 (void)
{
PINSEL0 &= 0xFFFFFFF0; // Reset P0.0,P0.1 Pin Config
PINSEL0 |= 0x00000005;
// Select P0.0 = TxD(UART0)
//PINSEL0 |= 0x00000004; // Select P0.1 = RxD(UART0)

/*U0LCR &= 0xFC; // Reset Word Select(1:0)
U0LCR |= 0x03; // Data Bit = 8 Bit
U0LCR &= 0xFB; // Stop Bit = 1 Bit
U0LCR &= 0xF7; // Parity = Disable
U0LCR &= 0xBF;*/
U0LCR = 0x3; // Disable Break Control
U0LCR |= 0x80; // Enable Programming of Divisor Latches

// U0DLM:U0DLL = 29.4912MHz / [16 x Baud]
// = 29.4912MHz / [16 x 9600]
// = 192 = 0x00C0
U0DLM = 0x00; // Program Divisor Latch(192) for 9600 Baud
U0DLL = 0x10;

U0LCR &= 0x7F; // Disable Programming of Divisor Latches

U0FCR |= 0x01; // FIF0 Enable
U0FCR |= 0x02; // RX FIFO Reset
U0FCR |= 0x04; // TX FIFO Reset
U0FCR &= 0x3F;

U0IER = 0x00000001; //enable rx data available interrupt
VICVectCntl1 = 0x00000026;
VICDefVectAddr=(unsigned)uart0_irq;
VICVectAddr1 = (unsigned)uart0_irq;
VICIntEnable |= 0x00000040;

}
--- In l..., Timo wrote:
>
> buckeyes1997 wrote:
> > U0IIR_copy is a register copy of RBR. I saw that was done in one of the
> > online examples so I just used it. It seems to work.
>
> Ok, I didn't see that. That's a very good start for an obfuscated code
> contest success ;)
>
> I don't see init_serial0 either. If it enables extra interrupts, I think
> you could get an interrupt loop.
>
> When the system freezes, do you know which one freezes, the LPC2119
> program or the PC program?
>
> --
>
> Timo
>

A couple of further items worth checking:

Check, that ADDR is defined as volatile. Otherwise the loop in the timer
irq may lock up. What happens, if you remove the check and loop altogether?

UM says that START bits must be zero when you use BURST.

--

Timo

--- In l..., "buckeyes1997" wrote:
>
> The init_serial0 routine is below. It is definately the LPC2119 that freezes. It always goes to the same state as noted by LED's on the board that normally flash during timer0 interrupt and during uart0 interrpt. When it freezes both LEDs light and stay lit until I reset the board.
>
> I will test for which interrupt is being fired accidentally and post back shortly. It almost seems like a collision between the two interrupts because when only 1 (either one) runs it never hiccups.
>
> Thanks
> Matt

A couple of thoughts that may not help a bit: Why are you pointing the default interrupt vector to the UART interrupt handler?

Next, IIRC you defined the interrupt handlers with the

__attribute__ ((interrupt("IRQ")));

whereas

__attribute__ ((interrupt))

would be more appropriate.

Interrupts don't nest (at least not by default) so you want to get in and get out of the handlers. And don't count on the library code to protect the stack. There are plenty of examples where even the interrupt handlers don't protect it properly.

One way to do this is to set a flag in the interrupt routine to tell the main code it is time to do the calculation. If you're waiting a second or so between updates, a few milliseconds one way or another won't matter.

That uart interrupt handler is somewhat unusual. Get in, grab the char, put it in a circular buffer and get out. Grabbing the chars from the buffer is the responsiblity of main(). If you make the buffer size a power of 2, the wrap around calculation is simply masking off the index bits.

None of this may help at all. OTOH, what you have isn't working either.

The proper way to solve this nonsense is to write a little interrupt prolog and epilog in boot.s. This bit of code allows complete nesting of interrupts and all interrupt handlers are written without the __attribute__ stuff. Just normal C functions. There is some possibility that the code would be portable!

But get what you have working first.

Richard

I have made everything volatile now!!! same problem.

volatile float error;
volatile int sFlag;
volatile int cFlag;
volatile int UCount;
volatile char Buffer[10];
volatile unsigned int U0IIR_copy;
volatile unsigned int val;
volatile float volt;
volatile float sumerror;
volatile float setpoint;
volatile float Pterm;
volatile float Iterm;
volatile float Dterm;

I read that the volatile protects against compiler optimizing away variables so I turned -O0 in uVision to prevent any optimizations...just in case.

Thanks but I'm still stuck.

--- In l..., Timo wrote:
>
> A couple of further items worth checking:
>
> Check, that ADDR is defined as volatile. Otherwise the loop in the timer
> irq may lock up. What happens, if you remove the check and loop altogether?
>
> UM says that START bits must be zero when you use BURST.
>
> --
>
> Timo
>

I added some debugging to the other vectors and it never gets to any of them. At least it never prints the string to the serial port. It turns my two LEDS on and it just sits there.

It has to be getting to one of those conditions because if I setup the vectors in the startup.s to all be Reset_Handler then it never has the problem but randomly resets the board instead of locking up. It then continues running fine until the next hiccup.

I read online that -O0 optimizations could be an issue so I have set them to no optimizations in uVision.

Any suggestions???

----------startup.s vector section----------
Vectors: LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP /* Reserved Vector */
LDR PC, [PC, #-0xFF0]
LDR PC, FIQ_Addr

Reset_Addr: .word Reset_Handler
Undef_Addr: .word Undef_Handler
SWI_Addr: .word SWI_Handler
PAbt_Addr: .word PAbt_Handler
DAbt_Addr: .word DAbt_Handler
.word 0 /* Reserved Address */
IRQ_Addr: .word IRQ_Handler
FIQ_Addr: .word FIQ_Handler

Undef_Handler: B Undef_Handler
SWI_Handler: B SWI_Handler
PAbt_Handler: B PAbt_Handler
DAbt_Handler: B DAbt_Handler
IRQ_Handler: B IRQ_Handler

-----------main.c debugging section-------------
void Undef_Handler(void)
{
printf("\nUndef_Handler\n");
VICVectAddr = 0;
}

void SWI_Handler(void)
{
printf("\nSWI_Handler\n");
VICVectAddr = 0;
}

void PAbt_Handler(void)
{
printf("\nPabt_Handler\n");
VICVectAddr = 0;
}

void DAbt_Handler(void)
{
printf("\nDabt_Handle\n");
VICVectAddr = 0;
}

void FIQ_Handler(void)
{
printf("\nFIQ_Handler\n");
VICVectAddr = 0;
}

--- In l..., Daniel Widyanto wrote:
>
> Hi,
>
> Just a general suggestion: don't put data processing on interrupt service routine. I don't think you system is hang anyway, it just that it lost some of the timer interrupts, so it looks like not-responsive / hang.
>
> Also, the easier way to check if it goes to exception mode (Undefined, data abort, prefetch abort, etc) is by connecting the interrupt handler to it. Eg:
> ------------------
> Vectors: LDR PC, Reset_Addr
> LDR PC, Undef_Addr
> LDR PC, SWI_Addr
> LDR PC, PAbt_Addr
> LDR PC, DAbt_Addr
> NOP /* Reserved Vector */
> LDR PC, [PC, #-0xFF0]
> LDR PC, FIQ_Addr
>
> Reset_Addr: .word Reset_Handler
> Undef_Addr: .word Blink_LED0
> SWI_Addr: .word Reset_Handler
> PAbt_Addr: .word Blink_LED1
> DAbt_Addr: .word Blink_LED2
> .word 0 /* Reserved Address */
> IRQ_Addr: .word IRQ_Handler
> FIQ_Addr: .word Reset_Handler
> ------------------
>
> Of course, you must provide the functions (Blink_LED0(), Blink_LED1(), and Blink_LED2()) somewhere. The other choice is to put exception handler that'll print the R14 (LR register) and the current exception state to the terminal, so you can track which address that causes the exception.
>
> Regards,
> -daniel


The 2024 Embedded Online Conference