Hi, I wrote a simple PWM code and it was working fine. But when I tried to enable the match interrupts, the code just get gets stuck inside the interrupt handler while stepping in the debugger.
Can someone help on this, what is wrong with my interrupt initialization?
#include <LPC23xx.H> #include <stdio.h> #define I_Bit 0x80 #define F_Bit 0x40
#define SYS32Mode 0x1F #define IRQ32Mode 0x12 #define FIQ32Mode 0x11 static unsigned long sysreg; #define IENABLE __asm { MRS sysreg, SPSR; MSR CPSR_c, #SYS32Mode } #define IDISABLE __asm { MSR CPSR_c, #(IRQ32Mode|I_Bit); MSR SPSR_cxsf, sysreg } #define PLOCK 0x04000000 #define PWMMR0I (1<<0) #define PWMMR0R (1<<1) #define PWMMR1I (1<<3) #define PWMMR2I (1<<6) #define PWMMR3I (1<<9) #define PWMMR4I (1<<12) #define PWMMR5I (1<<15) #define PWMMR6I (1<<18) #define MR1I_FLAG (1<<1) #define MR2I_FLAG (1<<2) void initClocks(void); void initTimer0(void); void setupPLL0(void); void feedSeq(void); void connectPLL0(void); void initPWM(void); void initClocks(void); __irq void PWM1Handler(void); void initPWM(void);
int main() { initClocks(); initPWM(); for(;1;); }
void PWM1Handler(void) __irq { unsigned int regVal; unsigned int i=0; regVal = PWM1IR; IENABLE; if (PWM1IR&PWMMR0I) { printf("match 0 occured"); i++; } else if (PWM1IR&PWMMR1I) printf("match 1 occured"); else if (PWM1IR&PWMMR2I) printf("match 2 occured"); else if (PWM1IR&PWMMR3I) printf("match 3 occured"); else if (PWM1IR&PWMMR4I) printf("match 4 occured"); else if (PWM1IR&PWMMR5I) printf("match 5 occured"); else if (PWM1IR&PWMMR6I) printf("match 6 occured"); PWM1IR = regVal; IDISABLE; VICVectAddr=0; }
void initClocks() { setupPLL0(); feedSeq(); //sequence for locking PLL to desired freq. connectPLL0(); feedSeq(); //sequence for connecting the PLL as system clock }
void initPWM(void) { PCONP |= (1<<6); PCLKSEL0 |= ((1<<12)|(0<<13)); PINSEL4 |= 0x00000555; PINSEL4 &= ~0x0000AAA; PWM1TCR = 0x02; PWM1PR = 59999; PWM1MCR = 0x0004924B; PWM1PCR = (1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13)|(1<<14); PWM1MR0 = 10000; PWM1MR1 = 1000; PWM1MR2 = 2000; PWM1MR3 = 3000; PWM1MR4 = 4000; PWM1MR5 = 6000; PWM1MR6 = 7000; PWM1LER = 0x7F;
VICVectAddr8 = (unsigned)PWM1Handler; VICVectPriority8 = 0x0F; VICIntEnable |= (1<<8);
PWM1TCR = 0x09; }
//---------PLL Related Functions :---------------
void setupPLL0(void) { PLLCON = 0x01; PLLCFG = 0x04 | (1<<16); SCS |= 0x20; while( !(SCS & 0x40) ); CLKSRCSEL = 0x01; }
void feedSeq(void) { PLLFEED = 0xAA; PLLFEED = 0x55; }
void connectPLL0(void) { while( !( PLLSTAT & PLOCK )); PLLCON = 0x03; }
Thanks for the quick response. You were right, I had mistakenly set the PWM clock higher than the CPU clock that's why interrupts were not executed.
I have corrected and now its working.
I have one more doubt regarding a code from NXP itself and it uses two statements inside the interrupt subroutine (IENABLE and IDISABLE) which I don't understand. Though I am not using them I would like to their use.
Please help me understand the statements and their use.
#include <LPC23xx.H> #include <stdio.h> #define I_Bit 0x80 #define F_Bit 0x40 #define SYS32Mode 0x1F #define IRQ32Mode 0x12 #define FIQ32Mode 0x11 static unsigned long sysreg; #define IENABLE __asm { MRS sysreg, SPSR; MSR CPSR_c, #SYS32Mode } #define IDISABLE __asm { MSR CPSR_c, #(IRQ32Mode|I_Bit); MSR CPSR_cxsf, sysreg } #define PLOCK 0x04000000 #define PWMMR0I (1<<0) #define PWMMR1I (1<<1) #define PWMMR2I (1<<2) #define PWMMR3I (1<<3) #define PWMMR4I (1<<8) #define PWMMR5I (1<<9) #define PWMMR6I (1<<10) #define MR1I_FLAG (1<<1) #define MR2I_FLAG (1<<2) void initClocks(void); void initTimer0(void); void setupPLL0(void); void feedSeq(void); void connectPLL0(void); void initPWM(void); //void initClocks(void); __irq void PWM1Handler(void); void initPWM(void); int main() { FIO0DIR1 |= 0xFF; initClocks(); initPWM(); for(;1;); } void PWM1Handler(void) __irq { unsigned int regVal; unsigned int i=0; //IENABLE; regVal = PWM1IR; if (PWM1IR&PWMMR0I) FIO0PIN1 = (1<<0); else if (PWM1IR&PWMMR1I) FIO0PIN1 = (1<<1); else if (PWM1IR&PWMMR2I) FIO0PIN1 = (1<<2); else if (PWM1IR&PWMMR3I) FIO0PIN1 = (1<<3); else if (PWM1IR&PWMMR4I) FIO0PIN1 = (1<<3); else if (PWM1IR&PWMMR5I) FIO0PIN1 = (1<<4); else if (PWM1IR&PWMMR6I) FIO0PIN1 = (1<<5); PWM1IR |= regVal; //IDISABLE; VICVectAddr=0; return; } void initPWM(void) { PCONP |= (1<<6); PCLKSEL0 |= ((1<<12)|(1<<13)); PINSEL4 |= 0x00000555; PINSEL4 &= ~0x0000AAA; PWM1TCR = 0x02; PWM1PR = 59999; PWM1MCR = 0x0004924B; PWM1PCR = (1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13)|(1<<14); PWM1MR0 = 1000; PWM1MR1 = 100; PWM1MR2 = 200; PWM1MR3 = 300; PWM1MR4 = 400; PWM1MR5 = 500; PWM1MR6 = 600; PWM1LER = 0x7F; VICVectAddr8 = (unsigned int)PWM1Handler; VICVectPriority8 = 0x0F; VICIntEnable |= (1<<8); PWM1TCR = 0x09; } //---------PLL Related Functions :-------------- void initClocks() { setupPLL0(); feedSeq(); //sequence for locking PLL to desired freq. connectPLL0(); feedSeq(); //sequence for connecting the PLL as system clock } void setupPLL0(void) { PLLCON = 0x01; PLLCFG = 0x04 | (1<<16); SCS |= 0x21; while( !(SCS & 0x40) ); CLKSRCSEL = 0x01; CCLKCFG = 0x00; } void feedSeq(void) { PLLFEED = 0xAA; PLLFEED = 0x55; } void connectPLL0(void) { while( !( PLLSTAT & PLOCK )); PLLCON = 0x03; }
You need to decide if you want new interrupt handlers to be trigger before this code exits.
It gives trickier code when you have to assume an ISR can be interrupted. But if done correctly a high priority interrupt can get better latency by not needing to wait for a low priority interrupt to end.
AN10381 Nesting of interrupts in the LPC2000 www.nxp.com/.../AN10381.pdf
4.2.1 C code
#include <LPC213x.H> /* LPC21xx definitions */ // Macro for enabling interrupts, moving to System mode and relevant stack operations #define IENABLE /* Nested Interrupts Entry */ \ __asm { MRS LR, SPSR } /* Copy SPSR_irq to LR */ \ __asm { STMFD SP!, {LR} } /* Save SPSR_irq */ \ __asm { MSR CPSR_c, #0x1F } /* Enable IRQ (Sys Mode) */ \ __asm { STMFD SP!, {LR} } /* Save LR */ \ // Macro for disabling interrupts, switching back to IRQ and relevant stack operations #define IDISABLE /* Nested Interrupts Exit */ \ __asm { LDMFD SP!, {LR} } /* Restore LR */ \ __asm { MSR CPSR_c, #0x92 } /* Disable IRQ (IRQ Mode) */ \ __asm { LDMFD SP!, {LR} } /* Restore SPSR_irq to LR */ \ __asm { MSR SPSR_cxsf, LR } /* Copy LR to SPSR_irq */ \
I am able to enable the nesting of interrupts by using the IENABLE and IDISABLE statements as shown below i.e. while inside an interrupt if a higher priority interrupt comes up program will go to execute that high priority ISR and come back but when it comes back or if there is no higher priority interrupt, it loops at IENABLE never executes the low priority ISR (i.e. PC diesnt reach the code written after IENABLE line, rather it keeps looping from the first line of low priority ISR to IENABLE line). I think there is something wrong with my IENABLE/IDISABLE definitions.
static unsigned long sysreg; #define I_Bit 0x80 #define F_Bit 0x40 #define SYS32Mode 0x1F #define IRQ32Mode 0x12 #define FIQ32Mode 0x11 #define IENABLE __asm { MRS sysreg, SPSR; MSR CPSR_c, #SYS32Mode } #define IDISABLE __asm { MSR CPSR_c, #(IRQ32Mode|I_Bit); MSR SPSR_cxsf, sysreg } ..... ..... main code ..... ..... __irq void GPIO_INT_ISR(void) // Priority 0xE { if(IO2_INT_STAT_R&(1<<0)) FIO1PIN3=~FIO1PIN3; IO2_INT_CLR = 0x01; VICVectAddr=0; return; } __irq void T2ISR(void) // Priority 0xF { long int regVal; IENABLE; //CPU never goes below this line !!! though it can execute GPIO_INT_ISR and come back if that event occurs. regVal = T2IR; //Read current IR value if(T2IR&(1<<3)) //Check for Interrupt on MR3, If yes set all other External Match Outputs T2EMR |= 0x07; T2IR = regVal; //Write back to IR to clear Interrupt Flag IDISABLE; VICVectAddr = 0x0; //This is to signal end of interrupt execution return; }
@John Linq I also tried different IENABLE/IDISABLE definitions which you mentioned but it gave alot of errors telling that LR and SP are undefined.
Note: I am using LPC2378.
Your enable/disable IRQ can't work for this task.
As written, they might be used by the main loop to turn off interrupts when implementing a critical section.
With interrupt nesting you will corrupt LR if the enable/disable code doesn't make a copy - all interrupt handlers shares the same register. But you absolutely must not try to save state to a global variables - an int variable isn't able to store a new value for every level of nesting. That's what the stack is good for.
I think it might be that the Keil code was for their older compiler. Alas, I don't have access to the code I'm using right now.
If I understood correctly you mean to say that I should change the IENABLE/IDISABLE definitions as mentioned by John Linq. In that code the stack is used to store the state.
But When I do that, I get compilation error that LR and SP are undefined. How do we address this error??
Not sure I can help much more without access to my own code. But there is no way the code you are using can work. Nested interrupts means that you must be able to store multiple copies of the register values.