2008-04-28 03:23 AM
Issue with timer overflow interrupt
2008-04-24 03:54 PM
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 ); }2008-04-25 05:41 AM
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;
}
2008-04-28 03:23 AM
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.