Strange behavior of LPC43xx Timer0/1/2/3 interrupt

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Strange behavior of LPC43xx Timer0/1/2/3 interrupt

1,600 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by jokn on Fri Jan 04 11:54:11 MST 2013
Strange behavior of LPC43xx Timer0/1/2/3 interrupt

I'm just testing the timer0 of lpc4330 and I was wondering about that yet it seems at first ran twice as fast as expected.
I spend a lot of time and finally I found out that the problem is clearing the interrupt request bit (LPC_TIMER0->IR)

If my interrupt service routine is very short and especially has not subroutine call , clearing of IR bits fails and the interrupt repeats again.
In the second  run of the interrupt clearing of the IR bit is successful.

void TIMER0_IRQHandler (void)
{
if(msec)msec--;
   LPC_TIMER0->IR = 1;  // this fails in the first run of interrupt
                       // on second run the bit will be cleared
}

It doesn’t mutter if I insert a delay like this,

   for (icnt = 0; icnt < 100; ++icnt);
   LPC_TIMER0->IR = 1;

Or like this

  LPC_TIMER0->IR = 1;
  for (icnt = 0; icnt < 100; ++icnt);

But if I insert a dummy subroutine call, the problem disappears

void dummy () {}

   dummy();
   LPC_TIMER0->IR = 1;

Does anyone have an explanation for the strange behavior?

My timer initialization looks as follows.

void LPC_Timer0_Init (void)
{
  LPC_TIMER0->TCR = 2;          // Reset and Disable Timer
  LPC_TIMER0->CTCR = 0;
  LPC_TIMER0->CCR = 0;          // No Capture
  LPC_TIMER0->TC = 0;           // Counter = 0
  LPC_TIMER0->PC = 0;           // Prescale Counter = 0
  LPC_TIMER0->PR = 0;           // Prescale Register = 0 (no Prescaler)

  LPC_TIMER0->MR[0] = SystemCoreClock / 1000 ;
  LPC_TIMER0->MCR = 3;          // Interrupt & Reset TC on match
  LPC_TIMER0->EMR = 0;
  LPC_TIMER0->IR = 0xFFFFFFFF;  // Clear interrupt pending
  NVIC_EnableIRQ(TIMER0_IRQn);
  LPC_TIMER0->TCR = 1;          // Start Timer0

}
Labels (1)
0 Kudos
Reply
4 Replies

1,476 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Wouter on Thu Jan 10 07:50:45 MST 2013
Hmm, I would have expected that the DSB would have helped...

I'm not sure why adding a call in the ISR solves the issue, but adding this call does change the way the ISR exits:
If a call is made from the ISR, then on entering the ISR, LR is pushed onto the stack, and on exiting, LR is popped backed to the PC (POP {pc}).
If no call is made from the ISR, the LR does not have to be pushed onto the stack, and the ISR exits by branching to the value of LR (BX lr).
Perhaps updating the PC by stack takes some more time compared to a normal branch...
0 Kudos
Reply

1,476 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by jokn on Thu Jan 10 04:50:46 MST 2013
Hi Wouter,

The __DSB() right after clearing IR dos not solve that problem. A short delay does.
But that was not really, what I wondering about.
The problem disappears also, if I insert a empty branch instruction just before clearing IR !!!
Then I don’t need any delay after clearing IR.

void empty_func (void) {}
void TIMER0_IRQHandler (void) {
if(msec)msec--;
empty_func();
LPC_TIMER0->IR = 1;
}

Please try it ourself.
Do you have an explantion for this effect?
0 Kudos
Reply

1,476 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Wouter on Thu Jan 10 03:37:34 MST 2013
Hi jokn,

Most likely the problem is that it takes a few clock cycles before the IR bit really is cleared.

For longer ISR's it's sufficient to just clear the IR flag at the beginning.

If the ISR is really short and the clearing is done near the end of the ISR, try adding a __DSB(); instruction right after LPC_TIMER0->IR = 1;. The DSB instruction ensures all outstanding memory transfers are completed before executing the next instruction.

Regards,
Wouter
0 Kudos
Reply

1,476 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pnhnkvr on Fri Jan 04 15:47:47 MST 2013
Hi,

Clear the IR flag primarily. At least with LPC17xx mcus you cannot clear interrupt flag at the end of the ISR (at least with short routines, i.e. depending how many registers must the cpu pop from stack)

I guess that compiler will optimize your attempt to insert a delay loop (of course depending on your compiler optimization settings). If addition delay is required, try to add volatile NOP
0 Kudos
Reply