Модуль захвата ввода

Таймер-счётчик 1 дополнительно оснащён устройством захвата ввода, которое может срабатывать по фронтам внешнего сигнала, подаваемого на вывод ICP1 (вывод Digital с номером 8), либо по переднему фронту импульса, либо по заднему. Вместо цифрового входа ICP1 модуль захвата ввода может использовать как триггер сигнал от аналогового компаратора.

При возникновении заданного события модуль захвата ввода копирует в 16-битный регистр ICR1 (пара 8-битных регистров ICR1H и ICR1L) текущее значение регистра TCNT1 и устанавливает флаг прерывания ICF1 (вектор прерывания TIMER1_CAPT_vect). Это значение можно использовать для вычисления частоты сигналов, циклов заполнения ШИМ, и т.п.

Если во время обработки прерывания происходит очередное событие модуля захвата ввода, то значение регистра ICR1 изменяется. Поэтому при обработке прерывания оно должно быть считано в первую очередь. Сперва считывается младший байт из регистра ICR1L, затем старший байт - ICR1H, поскольку при чтении значения из регистра ICR1L значение регистра ICR1H буферизируется. Это нужно для того, чтобы при возможном изменении значения регистра ICR1 между чтением ICR1L и ICR1H не получилось так, что младший байт ICR1 оказался бы из одного события, а старший из другого.

Запись значений в регистр ICR1 доступна только при использовании его для задания верхней границы для таймера 1. При использовании модуля захвата ввода он доступен только для чтения.

Примечание. Не во всех 16-битных регистрах используется буферизация при чтении и записи. Поэтому порядок чтения и записи 16-битных регистров имеет значение. При чтении 16-битного регистра в микроконтроллерах AVR8 сперва должен быть прочитано значение регистра младшего байта, затем старшего. При записи наоборот - сперва записывается значение регистра старшего байта, затем младшего.

Пример измерения частоты с помощью модуля захвата ввода

В данном примере я использовал как генератор входящих импульсов второй Arduino, загрузив в него скетч примера к статье Режим ШИМ с коррекцией фазы и частоты, ограничив верхнюю частоту значением в 7812 Гц (задав 512 вместо 1 в параметрах функции map()).

Измерения проводятся интервалами по 2 секунды. Таймер-счётчик 1 работает в нормальном режиме, при отсутствии сигнала загорается встроенный светодиод.

Результат измерений передаётся в Монитор порта.

Скетч использует библиотеку VEduino.

Код скетча (скачать):

#include <ve_avr.h> // Используется библиотека VEduino


#define LED DEV_GPIOB, 5 // Встроенный светодиод Arduino UNO


volatile int overflow_counter = 0;

volatile float mean_frequency = 0.;

volatile int frequency_count = 0;

volatile float mean_deviation = 0.;

volatile int deviation_count = 0;

bool b_first = true;


void setup()

{

setModeOutput(LED); // Пин светодиода в режим вывода

Serial.begin(57600);

DEV_TIMER1.setClockSelect(TimerW::Prescaler_1); // 16МГц / 1 = 16 МГц. Таймер-счётчик 1 будет

// увеличивать значение регистра TCNT1 на единицу

// каждые 62.5 наносекунды.

DEV_TIMER1.setWaveGenMode(TimerW::Normal); // Нормальный режим таймера-счётчика

DEV_TIMER1.setInputCaptureEdge(TimerW::Rising); // Событие захвата ввода по переднему фронту

DEV_TICTRL1.inputCaptIntEnable(); // Разрешить прерывание захвата ввода

DEV_TICTRL1.overflowIntEnable(); // Разрешить прерывание по переполнению

interrupts(); // Разрешить прерывания

}


ISR(TIMER1_OVF_vect) // Обработчик прерывания по переполнению

{

++overflow_counter;

}


ISR(TIMER1_CAPT_vect) // Обработчик прерывания захвата ввода

{

int capture = DEV_TIMER1.inputCapture();

DEV_TIMER1.setCounter(0);

if (capture == 0) {

setHigh(LED);

return;

}

else {

setLow(LED);

}

capture += 75; // Поправка на погрешность измерения

if (b_first) { // Первое измерение

mean_frequency = 16e6 / (((float) overflow_counter) * 65536 + capture);

overflow_counter = 0;

++frequency_count;

b_first = false;

}

else {

float frequency = 16e6 / (((float) overflow_counter) * 65536 + capture);

overflow_counter = 0;

mean_frequency += frequency;

++frequency_count;

float mean_freq = mean_frequency / frequency_count;

mean_deviation += fabs(frequency - mean_freq);

++deviation_count;

}

}


void loop()

{

delay(2000);

if (frequency_count == 0) {

Serial.println("No data");

return;

}

float mean_freq = mean_frequency / frequency_count; // Средняя частота

float mean_dev = mean_deviation / deviation_count; // Среднее отклонение в Гц

float perc_dev = mean_dev * 100 / mean_freq; // Среднее отклонение в %

Serial.print(mean_freq);

Serial.print(" +/- ");

Serial.print(mean_dev);

Serial.print(" Hz, +/- ");

Serial.print(perc_dev);

Serial.println("%");

mean_frequency = 0.;

mean_deviation = 0.;

frequency_count = 0;

deviation_count = 0;

b_first = true;

}