Использование прерываний в Arduino (начало)
Перевод, оригинальная статья Arduino Interrupts
Часто при работе с проектами на микроконтроллерах требуется запускать фоновую функцию через равные промежутки времени. Это часто реализуется установкой аппаратного таймера для выработки прерывания. Это прерывание запускает программу обработки прерываний (Interrupt Service Routine, ISR) для управления периодическим прерыванием. В настоящей статье я описываю установку 8-битного таймера 2 для выработки прерываний на микроконтроллере ATMega168 Arduino. Я пройдусь по этапам, требуемым для установки программы обработки прерываний и внутри нее самой.
Arduino подразумевает процессор ATMega168 (ссылка на спецификацию). Этот микроконтроллер имеет несколько систем ввода-вывода, которые доступны каждому пользователю Arduino, поскольку библиотека Arduino облегчает их использование. К примеру, цифровой ввод-вывод, ШИМ, аналого-цифровые входы и последовательный порт. ATMega168 также имеет три внутренних аппаратных таймера. Хотя библиотека Arduino позволяет использовать некоторые свойства таймеров, нельзя напрямую использовать таймер для выработки периодических прерываний.
Прерывания?
Как подсказывает название, прерывания – это сигналы, прерывающие нормальное течение программы. Прерывания обычно используются для аппаратных устройств, требующих немедленной реакции на появление событий. Например, система последовательного порта или UART (универсальный асинхронный приемопередатчик) микроконтроллера должен быть обслужен при получении нового символа. Если этого не сделать быстро, новый символ может быть потерян.
• В Википедии есть хороший обзор о прерываниях.
При поступлении нового символа UART генерирует прерывание. Микроконтроллер останавливает выполнение основной программы (вашего приложения) и перескакивает на программу обработки прерываний (ISR), предназначенную для данного прерывания. В данном случае это прерывание по полученному символу. Эта ISR захватывает новый символ из UART, помещает в буфер, затем очищает прерывание и выполняет возврат. Когда ISR выполняет возврат, микроконтроллер возвращается в основную программу и продолжает её с точки вызова. Все это происходит в фоновом режиме и не влияет напрямую на основной код вашего приложения.
Если у вас запускается много прерываний или прерывания генерирует быстродействующий таймер, ваша основная программа будет выполняться медленнее, так как микроконтроллер распределяет свое машинное время между основной программой и всеми функциями обработки прерываний.
Вы можете задуматься, почему бы не просто проверять новый символ время от времени, вместо использования такого сложного процесса прерывания. Давайте вычислим пример, чтобы увидеть, насколько важны процессы прерывания. Скажем, у вас есть последовательный порт со скоростью передачи данных 9600 бод. Это означает, что каждый бит символа посылается с частотой 9600 Гц или около 10 кГц. На каждый бит уходит 100 мкс. Около 10 бит требуется, чтобы послать один символ, так что мы получаем один полный символ каждую миллисекунду или около того. Если наш UART буферизован, мы должны извлечь последний символ до завершения приема следующего, это дает нам на всю работу 1 мс. Если наш UART не буферизован, мы должны избавиться от символа за 1 бит или 1 мкс. Рассмотрим для начала буферизованный пример.
Мы должны проверять получение байта быстрее, чем каждую миллисекунду, чтобы предотвратить потерю данных. Применительно к Arduino это означает, что наша функция цикла должна обращаться для чтения статуса UART и возможно, байта данных, 1000 раз в секунду. Это легко выполнимо, но сильно усложнит код, который вам нужно написать. До тех пор, пока ваша функция цикла не требует больше 1 мс до завершения, вам это может сойти с рук. Но представьте, что вам нужно обслуживать несколько устройств ввода-вывода, или что необходимо работать на гораздо большей скорости передачи. Видите, какие неприятности это вскоре может принести.
С прерываниями вам не нужно отслеживать поступление символа. Аппаратура подает сигнал с помощью прерывания, и процессор быстро вызовет ISR, чтобы вовремя захватить символ. Вместо выделения огромной доли процессорного времени на проверку статуса UART, вы никогда не должны проверять статус, вы просто устанавливаете аппаратное прерывание и выполняете необходимые действия в ISR. Ваша главная программа напрямую не затрагивается, и от аппаратного устройства не требуется особых возможностей.
Прерывание по таймеру
В настоящей статье я сосредоточусь на использовании программного таймера 2 для периодических прерываний. Исходная идея состояла в использовании этого таймера для генерации частоты биений в звуковых проектах Arduino. Чтобы выводить тон или частоту нам нужно переключать порт ввода-вывода на согласованной частоте. Это можно делать с использованием циклов задержки. Это просто, но означает, что наш процессор будет занят, ничего не выполняя, но ожидая точного времени переключения вывода. С использованием прерывания по таймеру мы можем заняться другими делами, а вывод пусть переключает ISR, когда таймер подаст сигнал, что время пришло.
Нам нужно только установить таймер, чтобы подавал сигнал с прерыванием в нужное время. Вместо прокрутки бесполезного цикла для задержки по времени, наша главная программа может делать что-то другое, например, контролировать датчик движения или управлять электроприводом. Что бы ни требовалось нашему проекту, больше нам не нужно процессорное время для получения задержек.
Я опишу в ISR в общем только то, что касается прерываний таймера 2. Более подробно об об использовании прерываний в процессорах AVR можно прочитать в руководстве пользователя avr-libc(англ). На данном этапе не требуется полного понимания, но, в конечном счете, вы можете захотеть получить возможность ускорить использование прерываний, раз это важный инструмент для приложений на микроконтроллерах.