Go Down

Topic: "High" resolution timer interrupts with timer0 (Read 2031 times) previous topic - next topic

Njay

Jan 25, 2013, 06:05 am Last Edit: Jan 25, 2013, 04:49 pm by Njay Reason: 1
I needed a "high" resolution (a few us) and low jitter interrupt for a project on an Arduino Diecimila compatible board. Because there are other things my code must do and that can take some time, I couldn't just do the thing with micros() on the main loop, I really needed an interrupt because of the low latency requirement.

Timer1 and Timer2 were busy PWM'ming a few things and only Timer0 was free; but you know, Timer0 is busy keeping the basic time functions working. I end up cooking a solution to have a high resolution "timer" interrupt from Timer0 without disturbing any of the timekeeping functions, although it can not coexist simultaneously with analogWrite() on pins 5/6.

The trick is to use the Compare Match registers and their interrupts, setting OCR0x to the next TCNT target value at the beginning of the interrupt, thus being able to achieve at best a 4us periodic interrupt. We keep pushing OCR0x ahead of TCNT0 as to trigger a new interrupt "soon".
Because writing to OCR0x registers is buffered when the Timer is configured for PWM, we need to take it off of that mode and set the "Normal mode", which won't affect the time functions since they rely on the overflow interrupt at counter wraparound of the 8 bit value.

In code this translates to something like what you see at the end of this post (some fragments from my project). Since there are 2 compare match interrupts, we can have 2 different interrupts ticking in multiples of 4us. It can also be used to create "one shot" timers. The jitter part can still be optimized (if your time periods allow) by making sure that the OCR0x never match the overflow interrupt (prevent OCR0x from being around 0x00).

I also found that micros() is rather slow for my application, so I end up using TCNT0 directly most of the time.

I did this in an ATmega168, can probably work on the 328 and on the 8, didn't check the details. If you know of some better way of doing this or see any problem with this, please do share.

Cheers


Quote
void setup ()
{
    (...)
    // Setup the timer(0) interrupt for timming and Monitor ring bus comms.
    // "Attaching" to interrupts on timer0 which is used by the Arduino lib. We
    // enable the compare match with OCR0A interrupt and use that for our
    // needed purposes. PWM on arduino pins 5 and 6 cannot be used.
    unsigned char  oldSREG = SREG;
    cli();
    TCCR0A &= ~(_BV(WGM01) | _BV(WGM00));       // set normal mode to allow update of OCR0x at any moment
    enableRxTimer(true);
    SREG = oldSREG;
    (...)
}

static inline void enableRxTimer (bool state)
{
    // Enable/disable the Output Compare Match interrupt
    TIMSK0 = (TIMSK0 & ~_BV(OCIE0A)) | (state? _BV(OCIE0A) : 0);
    TIFR0 |= _BV(OCR0A);     // make sure the intr flag starts cleared
}

// We can't takeover timer0 and implement our delay/timming
// functions, so we use the compare match interrupt and setup
// OCR0A to cause an intr in multiples of 4us. Arduino libs
// configure timer0 such that each TCNT0 count corresponds to
// 4us (timer 0 prescaler is set to 64 -> 16MHz/64 => 4us).
ISR(TIMER0_COMPA_vect)
{
    OCR0A = TCNT0 + 2;      // causing an interrupt every (2+1) x 4us = 12us
    (...)
}

Go Up