Аналоговый компаратор Arduino

В данной статье рассказывается об использовании прерываний в скетчах для Arduino. Всё написанное также верно и для клонов Arduino, например Freeduino.

В статье встречаются технические термины, для понимания которых могут быть не бесполезны статьи Порты ввода-вывода Arduino, Как подключить к Arduino... и Использование прерываний Arduino.

Запрет прерываний и пропущенные события аналогового компаратора Arduino

Спросив себя, что произойдёт в случае, если прерывания запретить, произошло событие прерывания, а затем прерывания разрешить, то вызовется ли после разрешения прерывания функция-обработчик, я решил исследовать этот вопрос при помощи аналогового компаратора Arduino, который в данном случае удобен тем, что в триггере аналогового компаратора отсутствует так называемый дребезг контактов, с которым можно было бы столкнуться, исследуя данный вопрос на примере прерываний по нажатию кнопки.

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

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

Схема конфигурации Arduino для демонстрации работы прерывания аналогового компаратора

Ниже приведён исходный код скетча:

/*

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

Автор (D) Андрей Шаройко <vanyamboe@gmail.com>, 2012

http://sites.google.com/site/vanyambauseslinux/electronics/analogovyj-komparator-arduino

*/

#define INTEN_LED_PIN 13 // К выводу Digital 13 подключен светодиод "прерывания разрешены"

#define INTEN_SW_PIN 8 // К выводу Digital 8 подключен переключатель "разрешить/запретить прерывания"

#define ANACOMP_LED_PIN 12 // К выводу Digital 12 подключен светодиод "состояние компаратора"

volatile bool updateLed = false;

volatile bool anacompState = false;

// Настройка скетча

void setup()

{

pinMode(INTEN_LED_PIN, OUTPUT); // Контакт светодиода INTEN_LED в режим вывода

pinMode(INTEN_SW_PIN, INPUT); // Контакт переключателя INTEN_SW в режим ввода

pinMode(ANACOMP_LED_PIN, OUTPUT); // Контакт светодиода ANACOMP_LED в режим вывода

anaCompSetup(); // Настройка аналогового компаратора

}

// Итерация основного цикла программы

void loop()

{

if (digitalRead(INTEN_SW_PIN) == HIGH) // В зависимости от положения переключателя

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

else // или

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

if (SREG & 0x80) // Если прерывания разрешены

digitalWrite(INTEN_LED_PIN, HIGH); // включить светодиод INTEN_LED

else // В противном случае

digitalWrite(INTEN_LED_PIN, LOW); // выключить светодиод INTEN_LED

if (updateLed) { // Если произошло прерывание аналогового компаратора

updateLed = false; // Отметить, что данное событие обработано

if (anacompState) // И если компаратор в состоянии логической единицы

digitalWrite(ANACOMP_LED_PIN, HIGH); // включить светодиод ANACOMP_LED

else // В противном случае

digitalWrite(ANACOMP_LED_PIN, LOW); // выключить светодиод ANACOMP_LED

}

}

// Настройка аналогового компаратора

void anaCompSetup()

{

ACSR |= _BV(ACIE); // Разрешить прерывание аналогового компаратора

DIDR1 |= _BV(AIN1D) | _BV(AIN0D); // Выключить цифровые входы контактов Digital 6 и 7 (для снижения энергопотребления)

}

// Обработчик прерывания аналогового компаратора

ISR(ANALOG_COMP_vect)

{

anacompState = ACSR & _BV(ACO); // Запомнить состояние аналогового компаратора

updateLed = true; // Обновить состояние светодиода ANACOMP_LED

}

Кликните по ссылке, чтобы скачать исходный код скетча.

Логика работы данной схемы следующая. К положительному входу компаратора (AIN1, вывод Digital 7) подключено напряжение +3.3 Вольт, а на отрицательный вывод компаратора (AIN0, Digital 6) вращением ручки потенциометра подаётся управляющее напряжение от 0 до 5 Вольт.

Сам аналоговый компаратор Arduino работает следующим образом: если напряжение на положительном входе компаратора превышает напряжение на отрицательном входе, то выход компаратора (в данном случае бит ACO регистра ACSR) переключается в состояние логической 1, а если не превышает, то выход компаратора переключается в состояние логического 0.

При переключении компаратора из одного состояния в другое может происходить вызов вектора прерывания ANALOG_COMP_vect, которое разрешается или запрещается управляющим битом ACIE регистра ACSR. По умолчанию событие прерывания происходит при любом изменении состояния выхода компаратора, но с помощью битов ACIS1 и ACIS0 можно настроить микроконтроллер таким образом, чтобы оно происходило либо только по переднему, либо только заднему фронту (передний фронт - переход от 0 к 1, задний - от 1 к 0):

Режимы прерывания аналогового компаратора

После того, как скетч был скомпилирован и загружен в микроконтроллер, я собрал схему и стал изучать поведение микроконтроллера. При переключении переключателя INTEN_SW выключался и выключался светодиод INTEN_EN, показывающий, что прерывания разрешаются и запрещаются в зависимости от положения переключателя. Разрешив прерывания, я начал вращать регулятор потенциометра, признаться несколько даже зачарованный тем, как светодиод ANACOMP_LED при этом включается и выключается.

Затем я вращением регулятора включил светодиод ANACOMP_LED и установкой переключателя INTEN_SW запретил прерывания - светодиод ANACOMP_LED, как и положено, продолжает светиться. Вращение регулятора никак не сказывается на состоянии светодиода ANACOMP_LED, что сигнализирует о том, что вектор прерывания ANALOG_COMP_vect не вызывается.

Установив регулятор в положение, при котором светодиод ANACOMP_LED не должен светиться, я переключил переключил переключатель INTEN_SW в положение "прерывания разрешены". В момент переключения светодиод ANACOMP_LED сразу же погас. Повращав регулятор в обратную сторону, я смог снова его включить.

Запрет прерываний и пропущенные события внешнего прерывания INT0

Таким образом, как только прерывания были снова разрешены, произошёл вызов вектора прерывания ANALOG_COMP_vect для обработки пропущенного события прерывания аналогового компаратора. Если перечитать внимательно пункт 22.3.2 даташита микроконтроллера ATmega328p, то там написано, что произошло это по той причине, что в это время был установлен флаг прерывания ACI (бит 4 регистра ACSR). И соответственно, следующий вопрос, который у меня сразу же появился в связи с этим - это вопрос, что произошло бы в ситуации, если бы были пропущены два события с разными приоритетами обработки прерываний, например, помимо события прерывания ANALOG_COMP_vect, имеющего приоритет 24, оказалось бы пропущено и событие например INT0_vect (например, нажатие на кнопку, подключенную к выводу Digital 2).

Чтобы это выяснить, я решил добавить в схему кнопку и ещё один светодиод, а в скетч - ещё одну функцию-обработчик прерывания. В результате получилась следующая схема:

Схема конфигурации Arduino для демонстрации обработки прерываний аналогового компаратора и внешнего прерывания INT0

Кликнув по ссылке, можно скачать исходный код скетча.

Скомпилировав скетч и загрузив его в микроконтроллер, я подключил схему, чтобы исследовать её поведение. При разрешённых прерываниях, если подать на вход INT0 микроконтроллера (вывод Digital 2 Arduino) импульс переднего фронта (переход напряжения от логического 0 к логической 1), светодиод INT0REQ_LED включается и светит некоторое время, задаваемое в скетче количеством циклов функции loop() с помощью константы INT0LEDCNT_MAX, после чего автоматически выключается, когда счётчик циклов int0LedCnt становится равным 0.

Если запретить прерывания переключателем INTEN_SW, и сгенерировать события аналогового компаратора или внешнего прерывания INT0, то при разрешении прерываний соответствующие события наступают, то есть происходит вызов функций-обработчиков прерываний. То есть и вызов вектора прерывания аналогового компаратора, и вызов вектора прерывания внешнего прерывания INT0 происходят в зависимости от состояния соответствующих флагов статуса в управляющих регистрах периферийных устройств микроконтроллера.

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

Добавив в функцию сброс флагов прерывания INT0_vect и ANALOG_COMP_vect перед разрешением прерываний, я смог пронаблюдать и вовсе завораживающее поведение схемы - светодиод ANACOMP_LED, включенный или выключенный до запрета прерываний, так и продолжает светиться или наоборот - не включается и в положении регулятора, при котором он как бы уже "должен" не светиться или наоборот. То есть переключение переключателя INTEN_SW не отражается на состоянии схемы напрямую, что делает его поведение виртуальным - он уже переключает поведение программы, а не устройства.

Кликнув по ссылке, можно скачать исходный код версии скетча со сбросом флагов прерываний.

См. также

Автор: Андрей Шаройко <vanyamboe@gmail.com>