Обеспокоен дрейфом значения micros() в программе Arduino

У меня есть программа, которая использует библиотеку Servo и внешнюю процедуру прерывания. Насколько я понимаю, библиотека Servo использует прерывание Timer1 для отправки импульсов сервоприводу для поддержания положения. Мне интересно, как это влияет на счетчик micros(), поскольку он не увеличивается во время прерывания.

Процедура внешнего прерывания в моем коде предназначена для тахометра. Он определяет время между импульсами с помощью micros(). Я обеспокоен тем, что библиотека Servo вызовет дрейф счетчиков millis() и micros() и сделает скорость неточной. Тахометру, возможно, придется определять скорость 10 000 об/мин, то есть около 167 Гц. Со временем я реализую ПИД-управление с помощью сервоприводов и тахометра.

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();
}

person pdfj    schedule 25.03.2014    source источник


Ответы (1)


Даже tachometer() и функция, которая обновляет millis(), вызываются из прерывания, так что даже это "сломает" ваш код.

Насколько это испортит вашу лекцию? Не очень много. Arduino 2009 года с использованием кристалла на 16 МГц имеет многосекундный дрейф в сутки. Arduino Uno, в котором используется менее точный осциллятор, будет дрейфовать намного сильнее.

Итак, если вы не выполняете много вычислений в процедуре прерывания, все в порядке. Также помните, что вызов micros() стоит вам 4 мкс (на самом деле вы увидите, что он увеличивается на 4 на 4... или это должно было заставить Timer0 переполняться каждую секунду? Взгляните на предварительный делитель таймера; вы можете изменить его, если он не ломает Servo lib.Он ломает millis() и micros(), и вы можете использовать Timer1 или Timer2, но Timer0 - единственный 16-битный таймер, так что это зависит от вас), так что это ваша реальная точность убийца.

Я бы предложил исключить вызов функции в прерывании:

unsigned long tmpTime;
void tachometer() {

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

И даже attachInterrupt вызывает ISR, который вызывает вашу функцию. Вы можете реализовать attachInterrupt самостоятельно, чтобы реализовать ISR напрямую (на один вызов функции меньше, а вызовы функций относительно дороги, так как вам приходится работать со стеком и регистрами).

Также не используйте micros(), который делает много вещей (считывает фактический счетчик Timer0 и вычисляет счетчик до мкс и является вызовом функции); просто прочитайте регистр таймера напрямую и сделайте разницу с этим значением, а также выполните преобразование счетчика в мкс в loop(). Таким образом, это должно быть намного быстрее.

Я не пишу код, так как вам просто нужно открыть библиотеку Arduino, посмотреть, что они делают, и воспроизвести это самостоятельно. Это довольно просто, так как у вас есть рабочий пример, таблица данных ATmega и множество руководств по таймерам, прерываниям и даже PWM.

person Lesto    schedule 26.03.2014