Skip to main content
Visitor II
April 28, 2008
Question

Issue with timer overflow interrupt

  • April 28, 2008
  • 3 replies
  • 1060 views
Posted on April 28, 2008 at 12:23

Issue with timer overflow interrupt

    This topic has been closed for replies.

    3 replies

    jrheiseyAuthor
    Visitor II
    April 24, 2008
    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 );

    }

    jrheiseyAuthor
    Visitor II
    April 25, 2008
    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; 
    } 
    

    Visitor II
    April 28, 2008
    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.