3

I have a program that utilizes the Servo library and an external interrupt routine. From my understanding the Servo library uses a Timer1 interrupt to send pulses to the servo to maintain position. I am wondering what the impact is on the micros() count since it does not increment during an interrupt.

The external interrupt routine in my code is for a tachometer. It determines the time between pulses using micros(). I am concerned that the Servo library will cause drift of the millis() and micros() counters and make the speed inaccurate. The tachometer may have to sense a 10,000 RPM speed so about 167 Hz. Eventually I will implement PID control using servos and tachometer.

volatile unsigned long period;
unsigned long microseconds;

void setup(){
    Serial.begin(9600);
    pinMode(tachometerPin, INPUT);

    pinMode(led, OUTPUT);

    attachInterrupt(0, tachometer, RISING); // set external interrupt

    throttle.attach(throttlePin); // attach servo objects to pins
    fuel.attach(fuelPin);
    throttle.writeMicroseconds(throttle_idle); // set servo positions
    fuel.writeMicroseconds(fuel_neutral);
}
void loop(){
    Serial.println(calculateSpeed());
}

float calculateSpeed(){
    /* Calculate speed of engine in RPM */
    float s = 60.0/(period*0.000001);
    return(s);
}
void tachometer() {

    /* Determine time between rotations of engine (pulses from tachometer) */
    period = micros() - microseconds;
    microseconds = micros();
}
pdfj
  • 769
  • 1
  • 6
  • 12

1 Answers1

3

Even tachometer() and the function that updates millis() are called from an interrupt, so even it would "break" your code.

How much would this ruin your lecture? Not very much. Arduino 2009, with using a crystal for 16 MHz, has a drift of many seconds at day. Arduino Uno, which uses a less precise oscillator, would drift a lot more.

So, unless you are doing a lot of calculation in the interrupt routine, you are fine. Also remember that a micros() call cost you 4 µs (in fact you will see it growing 4 by 4... or this was to have Timer0 to overflow every second? Take a look at the timer prescaler; you can change it if it does not break the Servo lib. It will break millis() and micros(), and you can use Timer1 or Timer2, but Timer0 is the only 16-bit timer, so it depends on you), so this is your real precision killer.

I would suggest to eliminate the function call in the interrupt:

unsigned long tmpTime;
void tachometer() {

    /* Determine time between rotations of engine (pulses from tachometer) */
    tmpTime = micros();
    period = tmpTime - microseconds;
    microseconds = tmpTime;
}

And even attachInterrupt call an ISR that call your function. You may implement attachInterrupt by yourself so you implement the ISR directly (one fewer function call, and function calls are relatively expensive as you have to work with the stack and registers).

Also don't use micros(), which does a lot of things (read the Timer0 actual count, and calculate count to µs, and is a function call); just read the timer register directly and do the difference with that value, and do the translation of count to µs into loop(). That way it should be a lot faster.

I'm not writing code as you just have to open Arduino's library and look at what they do, and replicate it by yourself. It is quite easy as you have a working example, an ATmega datasheet and a lot of tutorials on timers, interrupts and even PWM.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lesto
  • 2,260
  • 2
  • 19
  • 26