cancel
Showing results for 
Search instead for 
Did you mean: 

Issue with timer overflow interrupt

jrheisey
Associate II
Posted on April 28, 2008 at 12:23

Issue with timer overflow interrupt

3 REPLIES 3
jrheisey
Associate II
Posted on April 25, 2008 at 00:54

See the code at the bottom of this message.

Note that WaitTimer::ForMicroseconds() is called during non-interrupt time.

The problem I see is that there is some time period where the first call to CounterValue() will return a value such as 0x0999fff6. The next call in the loop might then return something like 0x09990000. A value less than the previously returned value. This is due to the interrupt service routine not being called prior to the CNTR register incrementing to 0x0000 so the s_uHighTime value has not been incremented yet.

I would think that it would be more deterministic if the CNTR register is updated from 0xffff to 0x0000 *during* the overflow interrupt process.

Anyone experience this? Anyone have a workaround?

J.R. Heisey

void WaitTimer::ForMicroseconds(unsigned int uWaitDuration) {

// counter is configured for microseconds so no further

// calculation is necessary.

u32 uCount;

u32 uInitCount = CounterValue();

u32 uDeltaTime;

u32 uLastDeltaTime = 0;

do {

uCount = CounterValue();

uDeltaTime = uCount - uInitCount;

if(uLastDeltaTime > uDeltaTime) {

// detect rollover

return;

}

uLastDeltaTime = uDeltaTime;

} while(uDeltaTime < uWaitDuration);

}

MPCRAMFUNC u32 WaitTimer::CounterValue() {

return (s_uHighTime << 16) + m_Handle->CNTR;

}

void WaitTimer::ISR() {

if ( TIM_FlagStatus( m_Handle, TIM_TOF ) == SET )

{

s_uHighTime++;

TIM_FlagClear( m_Handle, TIM_TOF );

}

}

void WaitTimer::Initialize() {

/* Enable the timer module interrupts */

EIC_IRQChannelConfig( m_IrqChannel, ENABLE );

EIC_IRQChannelPriorityConfig( m_IrqChannel, MPC_IRQPriority_WaitTimer);

EIC_IRQConfig( ENABLE );

TIM_Init ( m_Handle );

// JRH, 2006/1/2 - The clock is 48 Mhz. A prescaler

// value of 48 gives us 1 microsecond resolution.

TIM_PrescalerConfig ( m_Handle, 48 );

// Enable the timer interrupts

// The implementation for ForMicroseconds() does not

// need the interrupt.

TIM_ITConfig ( m_Handle, TIM_TO_IT , ENABLE );

// Clear the timer register values to start new counting

TIM_CounterConfig ( m_Handle, TIM_CLEAR );

TIM_CounterConfig ( m_Handle, TIM_START );

}

jrheisey
Associate II
Posted on April 25, 2008 at 14:41

I've also discovered that the timing of the generation of the overflow interrupt is completely nondeterministic with regard to the transitioning of the CNTR value from 0xffff to 0x0000. I have seen the interrupt increment s_uHighTime while CNTR is still a value of 0xffff.

So to account for this I've modified my WaitTimer::CounterValue() function as such. Not a perfect solution but it is more deterministic.


MPCRAMFUNC u32 WaitTimer::CounterValue() { 
 register u16 uCount; 
 while((uCount = m_Handle->CNTR) == 0 || uCount == 0xffff) 
 {} 
 return (s_uHighTime << 16) + uCount; 
} 

kleshov
Associate II
Posted on April 28, 2008 at 12:23

I would think that it would be more deterministic if the CNTR register is updated from 0xffff to 0x0000 *during* the overflow interrupt process.

Even if that was the case, your function WaitTimer::CounterValue() would still fail, since the interrupt could happen _AFTER_ the value of the variable s_uHighTime is fetched from memory.

It is unreasonable to expect that the counter will overflow during interrupt processing, since that implies that the interrupt is triggered _BEFORE_ it overflows. There would be many problems with such an implementation of a timer. Exactly when should the interrupt trigger? What if interrupts are temporarily disabled? And so on...

Your function WaitTimer::CounterValue() should deal with the possibility of a timer overflow during its execution. I think the easiest way to do it is to compute the 32-bit value twice (don't forget to make s_uHighTime volatile), compare them and use either the first or the second one depending on whether the higher 16 bits changed.