After some study, trial and error, I created a simple project for a NXP LPC1768, to test its timer interrupt. This project contains only startup_LPC17xx.s, system_LPC17xx.c, C-main.c, and timer.c.
C-main.c:
#include "lpc17xx.h" #include "timer.h" extern uint32_t SystemFrequency; int main(void) { SystemInit(); Timer_Init_n_Enable( LPC_TIM0, (SystemFrequency/8-1) ); NVIC_EnableIRQ(TIMER0_IRQn); /* LED Init */ LPC_GPIO2->FIODIR |= 0x000000FF; /* P2.0 to P2.7 LEDs defined as output */ while(1); }
timer.c:
#include "lpc17xx.h" #include "timer.h" void TIMER0_IRQHandler(void) { LPC_TIM0->IR = (1u<<0); // Reset the MR0 Interrupt; Writing a zero has no effect. LPC_GPIO2->FIOPIN ^= (1<<7); // Toggle LED. } void Timer_Init_n_Enable( LPC_TIM_TypeDef * TimerX, uint32_t TimerInterval ) { TimerX->MR0 = TimerInterval; TimerX->MCR |= (1u<<0) | (1u<<1); // Interrupt and Reset on MR0 TimerX->TCR |= (1u<<0); // Enable Timer Counter and Prescale Counter }
This project seemed to work.
But if I change the timer.c to:
void TIMER0_IRQHandler(void) { LPC_GPIO2->FIOPIN ^= (1<<7); // Toggle LED. LPC_TIM0->IR = (1u<<0); // Reset the MR0 Interrupt; Writing a zero has no effect. }
It doesn't work.
If I change the timer.c to:
void TIMER0_IRQHandler(void) { LPC_GPIO2->FIOPIN ^= (1<<7); // Toggle LED. LPC_TIM0->IR = (1u<<0); // Reset the MR0 Interrupt; Writing a zero has no effect. NVIC_ClearPendingIRQ(TIMER0_IRQn); }
It seemed to works.
I guess that, it doesn't work because I clear the LPC_TIM0->IR interrupt flag too late, so the NVIC generates a pending interrupt for LPC_TIM0->IR.
I don't have a LPC23xx now, so I can't verify my guess. I guess that, it will still work:
void Timer0Handler(void) __irq { FIO2PIN ^= (1<<7); // Toggle LED. T0IR = 1; /* Clear Interrupt Flag */ VICVectAddr = 0; /* Acknowledge Interrupt */ }
I think it is usual that, programmer needs to do something to identify which kind of interrupt was triggered, before clear the interrupt flag.
So, if my understanding is correct, how do I know that, I did NOT clear the interrupt flag too late within an ISR?
It is quite frustrated that, I can't even handle a simple timer interrupt properly. Maybe my understanding has never been correct.
Re-Correction for the above link
Removed the last ¤tviews=569
my.st.com/.../Flat.aspx
I'm not sure about any need for a line: VICVectAddr = 0; /* Acknowledge Interrupt */
It is needed for LPC23xx but not used in the NXP code bundle for the 17xx. But the LPC23xx has very weak integration with the NVIC requiring it to lookup the jump vector while the LPC17xx/Cortex-M3 have strong NVIC integration with dedicated interrupt vectors.
The 17xx code bundle have timer interrupt handlers looking like (reindented since their code look lousy):
void TIMER0_IRQHandler (void) { if ( LPC_TIM0->IR & (0x1<<0) ) { LPC_TIM0->IR = 0x1<<0; /* clear interrupt flag */ timer0_m0_counter++; } if ( LPC_TIM0->IR & (0x1<<1) ) { LPC_TIM0->IR = 0x1<<1; /* clear interrupt flag */ timer0_m1_counter++; } if ( LPC_TIM0->IR & (0x1<<4) ) { LPC_TIM0->IR = 0x1<<4; /* clear interrupt flag */ timer0_capture0++; } if ( LPC_TIM0->IR & (0x1<<5) ) { LPC_TIM0->IR = 0x1<<5; /* clear interrupt flag */ timer0_capture1++; } return; }
Interesting that they have a "return" in the function - lousy developer may think all functions need a return.
But anyway - you always need to take care of the individual interrupt sources within a device. For an UART, you may manage some interrupt sources by just reading th receive data register or writing to the transmit holding register. But for the timer, you need to acknowledge exactly what sources within a timer you handled.
Since the timer can have multiple compare/match active, you can get new bits set in the timer while you already are in your ISR - so by clearing the events you did handle, the NVIC will know if it needs to issue yet another interrupt or if you managed to pick up all pending events from the device.