Программа тахометра

Ардуино: тахометр на прерываниях

Если двигатель вращается достаточно медленно, мы можем обойтись подходом, который обычно используют для считывания сигнала кнопки. Например, так:

...
int rot = 0; // переменная для хранения количества оборотов
void loop(){
    if( digitalRead(2) ){ // если датчик сработал
        rot++; // прибавляем единичку к счетчику
        Serial.println(rot);
    }
}

Эта программа будет сносно считать количество оборотов и выводить их в последовательный порт. Но у такого решения есть большой недостаток. Если кроме подсчета оборотов в суперцикле loop будут другие операции, и не дай бог вызовы функции delay, то мы можем легко пропустить очередное срабатывание датчика! То есть в момент, когда щель на диске будет очередной раз пролетать мимо луча, контроллер будет занят какими-то другими делами и пропустит срабатывание.

Даже если в loop не будет ничего кроме digitalRead, мы всё равно рискуем пропустить срабатывание датчика при измерении скорости быстрых двигателей. Процедура digitalRead медленная. Что же делать?

Прерывания

Прерывания — это специальная аппаратная функция микроконтроллеров, которая позволяет прерывать выполнение основной программы по сигналу на определенных входах. Например, у Ардуино Уно есть два прерывания №0 и №1 на соответствующих выводах D2 и D3 (обычные цифровые ноги 2 и 3). 0 и 1 — это номера самих прерываний! Не путать с нумерацией ног Ардуино.

Если прерывание активировано, то при появлении на соответствующем контакте высокого уровня, программа мгновенно останавливается, и запускается так называемый обработчик прерывания. После выполнения кода обработчика, управление передается основной программе ровно в том же месте, где она была прервана.

Важное замечание. Код обработчика не должен выполняться слишком долго, иначе есть риск нарушить работу других прерываний и основной программы. А еще прерывание не должно вызываться слишком часто, иначе микроконтроллер только и будет, что висеть непрерывно в функции обработчика. В общем при работе с прерываниями есть много тонкостей, но мы не будем в них сильно углубляться на этом уроке.

Напишем программу для подсчета количества оборотов при помощи прерывания №0.

const byte interruptPin = 2; // контакт к которому подключен датчик
long int rot = 0; // переменная для хранения количества оборотов

// функция-обработчик, которая вызывается во время прерывания
void detect() {
    rot++;  
}

void setup() {
    Serial.begin(9600);
  
    // настроим контакт D2 на вход, да ещё с подтяжкой к питанию
    pinMode(interruptPin, INPUT_PULLUP);
    // активируем прерывание и свяжем его с функцией detect
    attachInterrupt(digitalPinToInterrupt(interruptPin), detect, RISING);
}

void loop() {
    Serial.println(rot);
    delay(100);
}

Функция digitalPinToInterrupt преобразует номер контакта Ардуино в номер прерывания. Вызов digitalPinToInterrupt(2) вернёт 0. Можно было явно написать:

attachInterrupt(0, detect, RISING);

Загрузим эту программу на Ардуино и откроем монитор последовательного порта.

Увидим вереницу чисел — это и есть количество оборотов. Каждые 100мс в порт вываливается текущее число оборотов, которое пока для нас бесполезно. Вот если бы у нас вращалось колесо робота, то умножив количество оборотов на длину окружности колеса мы бы получили пройденное расстояние! Обязательно попробуем это сделать в уроке про контроль движения робота.

А пока перейдем к скорости вращения.

Расчёт скорости

Изменим программу. Каждые 100мс будем считать скорость по формуле:

V = rot/dt

где rot — количество оборотов, dt — время, пройденное с последнего расчёта.

const byte interruptPin = 2;
unsigned int rot = 0;
unsigned long int tm;
unsigned long int spd = 0;
unsigned int dt = 0;

void detect() {
    rot++; // прибавляем единичку к счётчику обротов
    dt = millis() - tm; // вычисляем время с последнего расчёта
    if( dt >= 100 ){ // если прошло 100мс или более, то начинаем расчёт
        spd = rot*60000/dt;
        rot = 0; // обнуляем счётчик
        tm = millis(); // запоминаем время расчёта
    }
}

void setup() {
    Serial.begin(9600);
  
    pinMode(interruptPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(interruptPin), detect, RISING);

    tm = millis();
}

void loop() {
    Serial.println(spd);
    delay(100);
}

Откуда взялось 60000? Дело в том, что формула V = rot/dt даст нам скорость в единицах об/мс. Ведь dt у нас в миллисекундах. Чтобы превратить миллисекунды в секунды нужно dt поделить на 1000. А чтобы секунды в минуты — еще поделить на 60. Вот и получается: V = rot * 60000/dt.

Загрузим программу и откроем плоттер для изображения наших данных в виде графика. Если мотор подключен к лабораторному источнику питания, можно менять напряжение и смотреть как меняется скорость вращения.

Тахометр на Ардуино график

Также на графике виден шум — это колебания скорости вращения. К сожалению, коллекторный двигатель постоянного тока — машина не совершенная и его скорость имеет свойство «плавать». Но это тема совсем другого разбирательства.


Изменено:

Ардуино: тахометр на прерываниях: Один комментарий

  1. Здравствуйте, спасибо за статью. Можете рассказать поподробнее: почему скорость вращения якоря колеблеться?

Добавить комментарий

Ваш адрес email не будет опубликован.