Accurate microseconds

Arduino projects on the go
Post Reply
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Accurate microseconds

Post by Martin »

The micros() timer on our 8-bit Arduinos only resolves to 4-microsecond intervals - try Serial.print(micros()); The result is always a multiple of 4. And on 8 MHz models it's even worse as the resolution drops to 8 microseconds.

This isn't ideal for measuring or creating the sorts of pulse widths we tend to use. If you're not using Timer2 for anything else, then the snippet below provides a 1-microsecond resolution replacement for micros(). It works on all 16MHz and 8MHz ATmega328p chips - which covers most of the basic Arduinos. Just use accurateMicros() anywhere you would have previously used micros(). You can rename the function to something shorter, of course. The standard libraries, as far as I know, only use Timer2 for analogWrite() (PWM-simulated analog output) on pins 3 and 11, and for the tone() function.

Code: Select all

 void setup() {
   // put your setup code here, to run once:
  TCCR2A = 0x00;
  TCCR2B = 0x02; // /8 clock prescale
  TIMSK2 |= 0x01;
}

volatile uint32_t timer2cycles = 0UL;
ISR(TIMER2_OVF_vect) {
  ++timer2cycles;
}

uint32_t accurateMicros(void) {
  uint8_t sreg = SREG;
  cli();
  uint32_t u = timer2cycles;
  uint8_t t = TCNT2;
  if (TIFR2 & 0x01) {
    t = TCNT2;
    timer2cycles = ++u;
    TIFR2 |= 0x01;
  }
  SREG = sreg;
  return F_CPU <= 8000000 ? (u << 8) | t : (u << 7) | (t >> 1);
}

void loop() {
  // put your main code here, to run repeatedly:
}
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Accurate microseconds

Post by MaxZ »

Hi Martin,

I am writing some code for a servo travel expander, and I want to include your accurate microseconds timer.

A question: the "accurateMicroseconds" function clears the interrupt with cli(). I am using the interrupt vector on P0 to capture the pulse timing from the receiver (gleaned from one of Phil's sketches), is there going to be a conflict there?

And is it going to work on a ATtiny85?

Cheers,
Max.
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Accurate microseconds

Post by Martin »

Although it's doing a cli(), the code that saves and restores the SREG register effectively does an sei() to reenable the interrupts. By using the SREG way of doing it, the function can be called from anywhere - even from another function or interrupt that has itself disabled interrupts.

Assuming you've called the function from normal code, then interrupts are briefly switched off, while the function samples timer2 and corrects it if rollover has occurred, so it could introduce a slight jitter into the P0 interrupt vector timing. But only by about a microsecond, and even that only occasionally when the P0 interrupt tries to occur during the short interval when interrupts are disabled.

The code as written won't work on an ATtiny85, which doesn't have a timer2, but could probably be modified to work from timer0 or timer1. Your P0 interrupt vector code may also need a slight rewrite, because the ATtinys have slightly different interrupt handling compared to ATmegas.
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Accurate microseconds

Post by MaxZ »

Thanks Martin,
Martin wrote: 14 Sep 2021, 13:26 The code as written won't work on an ATtiny85, which doesn't have a timer2, but could probably be modified to work from timer0 or timer1.
Could you please help me with that modification, I've tried to work my way into timer coding on several occasions, but I am getting lost every time :( Or is it as simple as changing every 2 to 1 or 0 ?
Martin wrote: 14 Sep 2021, 13:26 Your P0 interrupt vector code may also need a slight rewrite, because the ATtinys have slightly different interrupt handling compared to ATmegas.
As I said, I gleaned the code from one of Phil's sketches, actually the V-mixer which was written for the Digispark, so I'm guessing I am ok there.

Code: Select all

ISR(PCINT0_vect)
{
  if (PINB & 1 << In) { // interrupt on input pin
    if (InWas == 0) {
      timer = accurateMicroseconds();
      InWas = 1;
    }
  } else {
    if (InWas == 1) {
      pulse = ((volatile int)accurateMicroseconds() - timer);
      InWas = 0;
      sync = 1;
    }
  }

}
Cheers,
Max.
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Accurate microseconds

Post by Martin »

Hi Max,

Sorry, I don't have time right now to set up and test an ATtiny test bed. Have you tried getting your project going using the ordinary, built-in micros() function? I would do that first. Once you get it working, you may find that it has all the performance you need, and only if you're seeing too much jitter on the servo outputs will it be necessary to look for a more accurate micros() replacement.
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Accurate microseconds

Post by MaxZ »

Hi Martin,

Sorry, I didn't realise you had to set up a test. I will do it as you suggested, and try the micros () function first.

Thanks again, cheers,
Max.
Post Reply