Can't get LPC2148 timer match interrupts to work (SOLVED)

I’m an ARM newb. One at a time I’m going through the peripherals on my LPC-P2148 to learn how ARMs do it (played with HC11/12s, AVRs, MSP430s, lots of PICs before).

I wrote this little test program and can’t get the interrupt to fire. I’ve been over it and over it. Looks right to me (not that that means anything :wink: ).

I’m using Eclipse/yagarto.

Can anyone spot what I’m doing wrong here?

#include "LPC214x.h"
#define PLOCK 0x400
void init(void);
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")));

int main(void)
{
    IODIR0 = 0x30600000;
    IOCLR0 = 0x30600000;                    //LEDs off
    init();     
    T0MR0 = 0x00001388;                     //compare-hit count
    T0MCR = 0x0003;                         //interrupt and reset on MR0
    T0TCR = 0x01;                           //enable Timer0

    VICVectCntl0 = 0x00000030;              //use it for Timer 0 Interrupt:
    VICVectAddr0 = (unsigned)IRQ_Routine;   //set interrupt vector in 0
    VICIntEnable = 0x00000010;              //enable TIMER0 interrupt
    while(1);
}

void init(void)
{
    PLLCFG=0x24;                //set multiplier/divider values
    PLLFEED=0xaa;
    PLLFEED=0x55;
    PLLCON=0x01;                //enable PLL
    PLLFEED=0xaa;
    PLLFEED=0x55;
    while(!(PLLSTAT & PLOCK));  //wait for the PLL to lock to set frequency
    PLLCON=0x3;                 //connect the PLL as the clock source
    PLLFEED=0xaa;
    PLLFEED=0x55;
    MAMCR=0x02;                 //enable MAM
    MAMTIM=0x04;                //set number of clocks for flash memory fetch
    VPBDIV=0x01;                //set peripheral clock(pclk) to system clock(cclk)
}

void IRQ_Routine(void)
{
    IOPIN0 ^= 0x30600000;       //toggle 4 LEDs
    T0IR = 0x01;                //clear interrupt
    VICVectAddr0 = 0x00000000;  //end of interrupt - dummy write
}

void FIQ_Routine(void){
    while (1) ; 
}
void SWI_Routine(void){
    while (1) ; 
}
void UNDEF_Routine(void) {
	while (1) ;	
}

I think you should enable the TIM0 clock in PCONP.

gdisirio:
I think you should enable the TIM0 clock in PCONP.

Unfortunately that's not it. That's already enabled by default after reset. Of course I put a line in the program to enable it again :D, just in case, but no change.

This is the initialization code I use:

Source: http://chibios.svn.sourceforge.net/view … iew=markup

Header: http://chibios.svn.sourceforge.net/view … iew=markup

BTW: In the package there are complete demos for your board, this is just the initialization code.

Giovanni

You likely need to enable interrupts using the global interrupt flags in the CPSR register. I’m not sure if Yagarto does this for you in the startup code.

You need some routines like these:

/* Enable and disable functions “ripped” from a sample by R O Software.

  • Copyright 2004, R O SoftWare

  • No guarantees, warrantees, or promises, implied or otherwise.

  • May be used for hobby or commercial purposes provided copyright

  • notice remains intact. */

#include “VIClowlevel.h”

#define IRQ_MASK 0x00000080

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;

}

/* end of R O code */

Mike

manton:
You likely need to enable interrupts using the global interrupt flags in the CPSR register. I’m not sure if Yagarto does this for you in the startup code.

I think that stuff is done in the crt.s startup code. I pasted the one I'm using at bottom of this post.

I’ve tried a lot of stuff in my program. In the debugger I can see that the timer is working and the timer interrupt flag gets set properly on a match. But no interrupt occurs! Something is awry. :grin:

My current test program:

#include "LPC214x.h"
#define PLOCK 0x400
void init(void);
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")));

int main(void)
{
    int i;
    IODIR0 = 0x30600000;
    IOCLR0 = 0x30600000;                    //LEDs off
    init();
    T0TCR = 0x02;                           //reset counter
    T0IR = 0xff;
    T0MCR = 0x0003;                         //interrupt and reset on MR0
    T0MR0 = 0x0000ffff;                     //compare-hit count

    VICVectCntl0 = 0x00000024;              //use it for Timer 0 Interrupt:
    VICVectAddr0 = (unsigned)&IRQ_Routine;   //set interrupt vector in 0
    VICIntEnable = 0x00000010;              //enable TIMER0 interrupt

    T0TCR = 0x01;                           //enable Timer0

    while(1){
        i=0;
        for(i=0;i<1000;i++);
        i=1;
    }
}

void IRQ_Routine(void)
{
    int i;
    IOSET0 = 0x30600000;        //4 LEDs blink
    for(i=0;i<0x0000ffff;i++);
    IOCLR0 = 0x30600000;
    T0IR = 0x01;                //clear interrupt
    VICVectAddr0 = 0;           //end of interrupt - dummy write
}

void init(void)
{
    PLLCFG=0x24;                //set multiplier/divider values
    PLLFEED=0xaa;
    PLLFEED=0x55;
    PLLCON=0x01;                //enable PLL
    PLLFEED=0xaa;
    PLLFEED=0x55;
    while(!(PLLSTAT & PLOCK));  //wait for the PLL to lock to set frequency
    PLLCON=0x3;                 //connect the PLL as the clock source
    PLLFEED=0xaa;
    PLLFEED=0x55;
    MAMCR=0x02;                 //enable MAM
    MAMTIM=0x04;                //set number of clocks for flash memory fetch
    VPBDIV=0x01;                //set peripheral clock(pclk) to system clock(cclk)
}

void FIQ_Routine(void){
    while (1) ; 
}
void SWI_Routine(void){
    while (1) ; 
}
void UNDEF_Routine(void) {
	while (1) ;	
}

My current crt.s:

/* ***************************************************************************************************************
	crt.s						STARTUP  ASSEMBLY  CODE 
								-----------------------

	Module includes the interrupt vectors and start-up code.
  *************************************************************************************************************** */

/* Stack Sizes */
.set  UND_STACK_SIZE, 0x00000004		/* stack for "undefined instruction" interrupts is 4 bytes  */
.set  ABT_STACK_SIZE, 0x00000004		/* stack for "abort" interrupts is 4 bytes                  */
.set  FIQ_STACK_SIZE, 0x00000004		/* stack for "FIQ" interrupts  is 4 bytes         			*/
.set  IRQ_STACK_SIZE, 0X00000004		/* stack for "IRQ" normal interrupts is 4 bytes    			*/
.set  SVC_STACK_SIZE, 0x00000004		/* stack for "SVC" supervisor mode is 4 bytes  				*/

#.set _MEMMAP,		0xE01FC040

/* 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
.func   _startup

_startup:

# Exception 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 (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  */

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, =_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

.endfunc
.end

Your startup code sets up stacks like it should, but it leaves all interrupts disabled (this is what the comments indicate, and my limited understanding of ARM assembler would agree that the comments are correct). So you will need to enable interrupts in your code.

It would be quite normal for the startup code to leave interrupts disabled, as there is a bunch of initialization that needs to be done before they can be enabled.

Mike

manton:
Your startup code sets up stacks like it should, but it leaves all interrupts disabled (this is what the comments indicate, and my limited understanding of ARM assembler would agree that the comments are correct). So you will need to enable interrupts in your code.

It would be quite normal for the startup code to leave interrupts disabled, as there is a bunch of initialization that needs to be done before they can be enabled.

Really!?!? :shock: That's crazy! What the hell good is it with no interrupts? :?

So where do I go to find out how to enable them? What’s involved? You say “a bunch of initialization”. I kind of thought I’d already done that…

You have done the initialization in main(), which the startup code does not handle, which is why interrupts are not normally enabled in the startup code. It is the requirement of main() to enable interrupts after the system configuration has been completed.

If you look at the code I posted earlier, you will find routines in it for enabling and disabling interrupts. After you have configured the VIC and set up your timer, call the enableIRQ() function.

Mike

[/quote]

manton:
You have done the initialization in main(), which the startup code does not handle, which is why interrupts are not normally enabled in the startup code. It is the requirement of main() to enable interrupts after the system configuration has been completed.

If you look at the code I posted earlier, you will find routines in it for enabling and disabling interrupts. After you have configured the VIC and set up your timer, call the enableIRQ() function.

Mike

Thanks Mike. I probably won't get around to digging into it till Sunday or Monday. I'll probably have some more dumb questions then. :grin:

After a couple hours of confusion and fumbling with it on Monday, I got it working. The confusion was good though. I learned some things I might not have learned had it just worked right away.

Yee Haw!! I have interrupts! Now I can get this thing doing something.

Thanks again Mike. :smiley:

Hey futz. glad to hear you got it going. any chance of uploading your code how it is now so i can see what you did to fix it?

I’ve just got my dev board and succesfully made my first LED blinky code. Now i want to play with interrupts and would be heaps helpful i could go over you code.

Thanks! :slight_smile:

MattyZee:
Hey futz. glad to hear you got it going. any chance of uploading your code how it is now so i can see what you did to fix it?

I’ve just got my dev board and succesfully made my first LED blinky code. Now i want to play with interrupts and would be heaps helpful i could go over your code.

If I find time tonight I'll post a few new pages on my site. I wrote some ultra-simple demo code for PWM and A/D last night too. And I have some simple RTC code I wrote last week that needs to be posted. Next up is RTC w/interrupts so it's more useful.

MattyZee:
Any chance of uploading your code how it is now so i can see what you did to fix it?

It has been posted to [[my site](http://ghmicro.com). Hit the LPC-P2148 link on the home page. Lots of little newb programs for the 2148 as I get each part figured out and working. If you're looking for advanced topics, this isn't the site for you. :grin:](http://ghmicro.com)

HI Fuiz, I have the same problem, can you please send me the final code (JanzOndrikovic@gmail.com), which works? Iam working on my diploma thesis and this problem is a part of it. Or does anybody have the interrupt working code from Futz web?, because I cannot find the code at this webpage.

Thank you very much… Jan (Czech Republic)