EmbeddedRelated.com
Forums
Memfault Beyond the Launch

lpc2368 Timer0 Interrupt

Started by Andy Zammy April 6, 2009
Hello again,

i'm having real difficulty in getting the timer0 interrupt to work. as it is atm, it looks like the interrupt occurs, but then the program just feezes and does nothing. I set the interrupt to occur every millisecond, and i know the interrupt occurs when it should because in my full version of the program i'm sending a string out of the uart, and it stops when it should.
nothing else happens though. i've also checked that the VICVectAddr holds the right memory location.

i have based my code on this example for the lpc2148 i found at this website:
http://ghmicro.com/index.php/lpc2000/1-lpc2148/2-lpc-p2148-board?start=6

main.c

#include "LPC23xx.H"
#include "LCD.h"

/* Function Prototypes */
void initialise(void);
void init_leds(void);
void write_leds(int x);
void send_data(void);
/* End Prototypes */

/* Global Variables */
unsigned int i; /* Declare increment variable for delay */
volatile unsigned short int milisec;
/* End Global Variables */

int main(void)
{
initialise();
int led = 0x00;
while(1)
{
write_leds(led);
led++;
}
}

void initialise(void)
{
init_leds();
sw2_main_osc();
enable_timer0();
enableIRQ();
irq_init();
}

void init_leds(void) /* Function to Initialise the LEDs */
{
PINSEL4 = 0; /* GPIO functionality */
FIO2DIR = 0x000000FF; /* LED's 1-8 defined as Outputs */
}

void write_leds(int x) /* Function to write given value to LEDs */
{
FIO2CLR = 0xFF;
FIO2SET = x;
}

clock.c

#include "LPC23xx.H"

/* Function Prototypes */
void sw2_main_osc(void);
void enable_timer0(void);
/* End Prototypes */

/* Global Variables */
unsigned int j; /* Declare increment variable for delay */
extern volatile unsigned short int milisec;
/* End Global Variables */

/* Functions */

void enable_timer0(void)
{
PCLKSEL0 |= 0x04; /* set timer clock to equal cclk */
T0MR0 = 11999;
T0MCR = 0x03;
T0TCR = 0x02;
T0TCR = 0x01;
T0IR = 0xff;
}

void sw2_main_osc(void)
{
SCS = 0x20; /* (OSCRANGE = 0 (12MHz Crystal) OSCEN = 1 (start up osc) */
while ( !(SCS & 0x7F) ){} /* Wait untill osc stabalises */
CLKSRCSEL = 1; /* Select MAIN osc to be source for PLL */
}
/* End Functions */

interrupt.c

#include "LPC23xx.H"
#define IRQ_MASK 0x00000080

/* Function Prototypes */
void IRQ_Routine(void) __attribute__ ((interrupt("IRQ")));
void FIQ_Routine(void) __attribute__ ((interrupt("FIQ")));
void SWI_Routine(void) __attribute__ ((interrupt("SWI")));
void UNDEF_Routine(void) __attribute__ ((interrupt("UNDEF")));
void irq_init(void);
unsigned enableIRQ(void);
unsigned disableIRQ(void);
unsigned restoreIRQ(unsigned oldCPSR);
/* End Prototypes */

/* Global Variables */

/* End Global Variables */

/* Functions */
void irq_init(void)
{
VICVectAddr4 = (unsigned long)&IRQ_Routine;// Set Interrupt Vector
VICVectCntl4 = 0; // use it for Timer0 Interrupt (lowest priority)
VICIntEnable |= 0x10; // Enable Timer0 Interrupt
}

void IRQ_Routine(void)
{
FIO2SET = 0xFF;
T0IR = 0x01;
VICVectAddr = 0;
}

static inline unsigned asm_get_cpsr(void)
{
unsigned long retval;
asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ );
return retval;
}

static inline void asm_set_cpsr(unsigned val)
{
asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) );
}

unsigned enableIRQ(void)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr(_cpsr & ~IRQ_MASK);
return _cpsr;
}

unsigned disableIRQ(void)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr(_cpsr | IRQ_MASK);
return _cpsr;
}

unsigned restoreIRQ(unsigned oldCPSR)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr((_cpsr & ~IRQ_MASK) | (oldCPSR & IRQ_MASK));
return _cpsr;
}

void FIQ_Routine(void)
{
while (1) ;
}
void SWI_Routine(void)
{
while (1) ;
}
void UNDEF_Routine(void)
{
FIO2SET = 0xff;
while (1) ;
}
/* End Functions */

i'm usnsure what to do next.
any help would be appreaciated.

thanks,
zammy



An Engineer's Guide to the LPC2100 Series

bump. i still haven't been able to get anywhere with this. any suggestions would be really appreciated.

zammy

________________________________
From: Andy Zammy
To: lpc2000
Sent: Monday, April 6, 2009 10:21:48 AM
Subject: lpc2368 Timer0 Interrupt
Hello again,

i'm having real difficulty in getting the timer0 interrupt to work. as it is atm, it looks like the interrupt occurs, but then the program just feezes and does nothing. I set the interrupt to occur every millisecond, and i know the interrupt occurs when it should because in my full version of the program i'm sending a string out of the uart, and it stops when it should.
nothing else happens though. i've also checked that the VICVectAddr holds the right memory location.

i have based my code on this example for the lpc2148 i found at this website:
http://ghmicro.com/index.php/lpc2000/1-lpc2148/2-lpc-p2148-board?start=6

main.c

#include "LPC23xx.H"
#include "LCD.h"

/* Function Prototypes */
void initialise(void);
void init_leds(void);
void write_leds(int x);
void send_data(void);
/* End Prototypes */

/* Global Variables */
unsigned int i; /* Declare increment variable for delay */
volatile unsigned short int milisec;
/* End Global Variables */

int main(void)
{
initialise();
int led = 0x00;
while(1)
{
write_leds(led);
led++;
}
}

void initialise(void)
{
init_leds();
sw2_main_osc();
enable_timer0();
enableIRQ();
irq_init();
}

void init_leds(void) /* Function to Initialise the LEDs */
{
PINSEL4 = 0; /* GPIO functionality */
FIO2DIR = 0x000000FF; /* LED's 1-8 defined as Outputs */
}

void write_leds(int x) /* Function to write given value to LEDs */
{
FIO2CLR = 0xFF;
FIO2SET = x;
}

clock.c

#include "LPC23xx.H"

/* Function Prototypes */
void sw2_main_osc(void);
void enable_timer0(void);
/* End Prototypes */

/* Global Variables */
unsigned int j; /* Declare increment variable for delay */
extern volatile unsigned short int milisec;
/* End Global Variables */

/* Functions */

void enable_timer0(void)
{
PCLKSEL0 |= 0x04; /* set timer clock to equal cclk */
T0MR0 = 11999;
T0MCR = 0x03;
T0TCR = 0x02;
T0TCR = 0x01;
T0IR = 0xff;
}

void sw2_main_osc(void)
{
SCS = 0x20; /* (OSCRANGE = 0 (12MHz Crystal) OSCEN = 1 (start up osc) */
while ( !(SCS & 0x7F) ){} /* Wait untill osc stabalises */
CLKSRCSEL = 1; /* Select MAIN osc to be source for PLL */
}
/* End Functions */

interrupt.c

#include "LPC23xx.H"
#define IRQ_MASK 0x00000080

/* Function Prototypes */
void IRQ_Routine(void) __attribute__ ((interrupt("IRQ")));
void FIQ_Routine(void) __attribute__ ((interrupt("FIQ")));
void SWI_Routine(void) __attribute__ ((interrupt("SWI")));
void UNDEF_Routine(void) __attribute__ ((interrupt("UNDEF")));
void irq_init(void);
unsigned enableIRQ(void);
unsigned disableIRQ(void);
unsigned restoreIRQ(unsigned oldCPSR);
/* End Prototypes */

/* Global Variables */

/* End Global Variables */

/* Functions */
void irq_init(void)
{
VICVectAddr4 = (unsigned long)&IRQ_Routine;// Set Interrupt Vector
VICVectCntl4 = 0; // use it for Timer0 Interrupt (lowest priority)
VICIntEnable |= 0x10; // Enable Timer0 Interrupt
}

void IRQ_Routine(void)
{
FIO2SET = 0xFF;
T0IR = 0x01;
VICVectAddr = 0;
}

static inline unsigned asm_get_cpsr(void)
{
unsigned long retval;
asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ );
return retval;
}

static inline void asm_set_cpsr(unsigned val)
{
asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) );
}

unsigned enableIRQ(void)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr(_cpsr & ~IRQ_MASK);
return _cpsr;
}

unsigned disableIRQ(void)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr(_cpsr | IRQ_MASK);
return _cpsr;
}

unsigned restoreIRQ(unsigned oldCPSR)
{
unsigned _cpsr;

_cpsr = asm_get_cpsr();
asm_set_cpsr((_cpsr & ~IRQ_MASK) | (oldCPSR & IRQ_MASK));
return _cpsr;
}

void FIQ_Routine(void)
{
while (1) ;
}
void SWI_Routine(void)
{
while (1) ;
}
void UNDEF_Routine(void)
{
FIO2SET = 0xff;
while (1) ;
}
/* End Functions */

i'm usnsure what to do next.
any help would be appreaciated.

thanks,
zammy



--- In l..., Andy Zammy wrote:
>
> bump. i still haven't been able to get anywhere with this. any suggestions would be really appreciated.
>

Well, what happens when you put a debugger breakpoint at the start of the interrupt handler?

There are those in this group that maintain that they can get these complex, embedded systems working without a debugger, but I really do not see how, especially with interrupts and possibly an OS .

Rgds,
Martin

Andy Zammy wrote:
> bump. i still haven't been able to get anywhere with this. any suggestions would be really appreciated.
What's the size of your IRQ stack? I bet it's too small, so
overflows into your System/User stack, trashing it.

--- In l..., "mjames_doveridge" wrote:

> Well, what happens when you put a debugger breakpoint at the start of the interrupt handler?
>
> There are those in this group that maintain that they can get these complex, embedded systems working without a debugger, but I really do not see how, especially with interrupts and possibly an OS .
>
> Rgds,
> Martin

Unfortunately i do not have access to a jtag debugger and i doubt i will be able to get a hold of one. I will look into it though.

--- In l..., Jan Brittenson wrote:

> What's the size of your IRQ stack? I bet it's too small, so
> overflows into your System/User stack, trashing it.

the IRQ stack is currently 0x80. i tried making it bigger but it didn't seem to make any difference.



--- In l..., Andy Zammy wrote:

> > What's the size of your IRQ stack? I bet it's too small, so
> > overflows into your System/User stack, trashing it.
>
> the IRQ stack is currently 0x80. i tried making it bigger but it didn't seem to make any difference.

In a previous post you said that your code is based on an LPC2148 example.

Did you take the whole project, and then modified it for the LPC2368?
If yes, did you modify the LDR instruction in the startup code at address 0x18?
The LPC2148 should have something like "LDR PC,[PC,#-0xFF0]", while for the LPC2368 this should be "LDR PC,[PC,#-0x120]".

Just an idea. This is a pretty common mistake...

Rolf

> In a previous post you said that your code is based on an LPC2148 example.
>
> Did you take the whole project, and then modified it for the LPC2368?
> If yes, did you modify the LDR instruction in the startup code at address 0x18?
> The LPC2148 should have something like "LDR PC,[PC,#-0xFF0]", while for the LPC2368 this should be "LDR PC,[PC,#-0x120]".
>
> Just an idea. This is a pretty common mistake...
>
> Rolf

I didn't use a startup file for the 2148, there was only the c code on that site anyway (afaik), i used my own startup file. I have very little understanding about startup and linker files, so there could be a problem in there. I know that interrupts aren't enabled in my startup code, which is why i needed the EnableIRQ functions with the assembly in it. Here is my startup code, just in case anyone needs to look at it:

# *** Startup Code (executed after Reset) ***
# Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

.equ Mode_USR, 0x10
.equ Mode_FIQ, 0x11
.equ Mode_IRQ, 0x12
.equ Mode_SVC, 0x13
.equ Mode_ABT, 0x17
.equ Mode_UND, 0x1B
.equ Mode_SYS, 0x1F

.equ I_Bit, 0x80 /* when I bit is set, IRQ is disabled */
.equ F_Bit, 0x40 /* when F bit is set, FIQ is disabled */
# Stack Configuration

.equ Top_Stack, 0x40004000
.equ UND_Stack_Size, 0x00000004
.equ SVC_Stack_Size, 0x00000004
.equ ABT_Stack_Size, 0x00000004
.equ FIQ_Stack_Size, 0x00000004
.equ IRQ_Stack_Size, 0x00000080
.equ USR_Stack_Size, 0x00000400
# Starupt Code must be linked first at Address at which it expects to run.

.text
.arm

.global _startup
.func _startup
_startup:
# Exception Vectors
# Mapped to Address 0.
# Absolute addressing mode must be used.
# Dummy Handlers are implemented as infinite loops which can be modified.

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, IRQ_Addr
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
FIQ_Handler: B FIQ_Handler
# Reset Handler

Reset_Handler:

# Initialise Interrupt System
# ...
# Setup Stack for each mode

LDR R0, =Top_Stack

# Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND|I_Bit|F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

# Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

# Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

# Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

# Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

# Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR
MOV SP, R0

# Setup a default Stack Limit (when compiled with "-mapcs-stack-check")
SUB SL, SP, #USR_Stack_Size
# Relocate .data section (Copy from ROM to RAM)
LDR R1, =_etext
LDR R2, =_data
LDR R3, =_edata
LoopRel: CMP R2, R3
LDRLO R0, [R1], #4
STRLO R0, [R2], #4
BLO LoopRel
# Clear .bss section (Zero init)
MOV R0, #0
LDR R1, =__bss_start__
LDR R2, =__bss_end__
LoopZI: CMP R1, R2
STRLO R0, [R1], #4
BLO LoopZI
# Enter the C code
ADR LR, __main_exit
LDR R0, =main
BX R0

__main_exit: B __main_exit
.size _startup, . - _startup
.endfunc
.end



--- In l..., Andy Zammy wrote:
>
> 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, IRQ_Addr
> 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
> FIQ_Handler: B FIQ_Handler
>

With this code you will end up in an infinite loop when an IRQ interrupt occurs!

Change the line containing
LDR PC, IRQ_Addr
to
LDR PC, [PC, #-0x120]

This will fetch the handler address from the VIC (VICAddress), and jump to the handler all in one instruction.

Rolf

--- In l..., "rolf_meeser" wrote:
>
> With this code you will end up in an infinite loop when an IRQ interrupt occurs!
>
> Change the line containing
> LDR PC, IRQ_Addr
> to
> LDR PC, [PC, #-0x120]
>
> This will fetch the handler address from the VIC (VICAddress), and jump to the handler all in one instruction.
>
> Rolf

wow, i've read those comments loads of times and not once did it ever hit me. i can see now how the handlers branch to themselves. dow! i don't really know any assembly so nothing is obvious to me at this point.

the interrupt now works, thanks very much for the fix Rolf, and thanks everyone for their time and posts!
zammy



I'm currently trying to use the INT0 to trigger FIQ. It's the same problem as last time: I'm confused because i don't know what offset to give the FIQ_Handler line in the startup file. I would have said #-0x134 but that didn't work when i tried it.

Could someone please explain how this works?

Thanks for your time,
Zammy


Memfault Beyond the Launch