Прерывания, прерывания и еще раз прерывания. таймеры

Прерывания, прерывания и еще раз прерывания. таймеры

В этой статье будет рассмотрено использование таймеров в МК и способ подсоединения кнопок к нему.Сначала немного теории.

В МК ATMega16 есть три таймера/счетчика – два 8-битных (Timer/Counter0, Timer/Counter2) и один 16-битный (Timer/Counter1). Каждый из них содержит специальные регистры, одним из которых является счетный регистр TCNTn (n – это число 0, 1 или 2). Каждый раз, когда процессор выполняет одну команду, содержимое этого регистра увеличивается на единицу (либо каждые 8, 64, 256 или 1024 тактов). Потому он и называется счетным. Помимо него, есть еще и регистр сравнения OCRn (Output Compare Register), в который мы можем сами записать какое-либо число. У 8-битного счетчика эти регистры 8-битные. По мере выполнения программы содержимое TCNTn растет и в какой-то момент оно совпадет с содержимым OCRn. Тогда (если заданы специальные параметры) в регистре флагов прерываний TIFR (Timer/Counter Interrupt Flag Register) один из битов становится равен единице и процессор, видя запрос на прерывание, сразу же отрывается от выполнения бесконечного цикла и идет обслуживать прерывание таймера. После этого процесс повторяется.

Ниже представлена временная диаграмма режима CTC (Clear Timer on Compare). В этом режиме счетный регистр очищается в момент совпадения содержимого TCNTn и OCRn, соответственно меняется и период вызова прерывания.

Это далеко не единственных режим работы таймера/счетчика. Можно не очищать счетный регистр в момент совпадения, тогда это будет режим генерации широтно-импульсной модуляции, который мы рассмотрим в следующей статье. Можно менять направление счета, т. е. содержимое счетного регистра будет уменьшаться по мере выполнения программы. Также возможно производить счет не по количеству выполненных процессором команд, а по количеству изменений уровня напряжения на «ножке» T0 или T1 (режим счетчика), можно автоматически, без участия процессора, менять состояние ножек OCn в зависимости от состояния таймера. Таймер/Счетчик1 умеет производить сравнение сразу по двум каналам – А или В.

Далее представлена функциональная схема таймера/счетчика0:

Для запуска таймера нужно выставить соответствующие биты в регистре управления таймером TCCRn (Timer/Counter Control Register), после чего он сразу же начинает свою работу.

Мы рассмотрим лишь некоторые режимы работы таймера. Если вам потребуется работа в другом режиме, то читайте Datasheet к ATMega16 – там все подробнейше по-английски написано, даны даже примеры программ на С и ассемблере (недаром же он занимает 357 страниц печатного текста!).

Теперь займемся кнопками.

Если мы собираемся использовать небольшое количество кнопок (до 9 штук), то подключать их следует между «землей» и выводами какого-либо порта микроконтроллера. При этом следует сделать эти выводы входами, для чего установить соответствующие биты в регистре DDRx и включить внутренний подтягивающий резистор установкой битов в регистре PORTx. При этом на данных «ножках» окажется напряжение 5 В. При нажатии кнопки вход МК замыкается на GND и напряжение на нем падает до нуля (а может быть и наоборот – вывод МК замкнут на землю в отжатом состоянии). При этом меняется регистр PINx, в котором хранится текущее состояние порта (в отличие от PORTx, в котором установлено состояние порта при отсутствии нагрузки, т. е. до нажатия каких-либо кнопок). Считывая периодически состояние PINx, можно определить, что нажата кнопка.

ВНИМАНИЕ! Если соответствующий бит в регистре DDRx будет установлен в 1 для вашей кнопки, то хорошее нажатие на кнопку может привести к небольшому пиротехническому эффекту – возникновению дыма вокруг МК. Естественно, МК после этого придется отправить в мусорное ведро…

Перейдем к практической части. Создайте в IAR новое рабочее пространство и новый проект с именем, например, TimerButton. Установите опции проекта так, как это описано в предыдущей статье. А теперь наберем следующий небольшой код.

Давайте посмотрим, как это работает. В функциях init_timern задаются биты в регистрах TCCRn, OCRn и TIMSK, причем такой способ может кому-нибудь показаться странным или незнакомым. Придется объяснить сначала, что означает запись «(1 © KERNELCHIP 2006 — 2021

Прерывания по таймеру

Мы с вами уже много раз рассматривали конструкцию таймера на millis() , который позволяет наладить логику работы кода по таймерам. Минусом этого способа является необходимость постоянно опрашивать конструкцию таймера, чтобы проверять, не сработал ли он. Соответственно код в главном цикле должен быть “прозрачным”, то есть не содержать задержек, долгих замкнутых циклов и просто блокирующих кусков. Если для таймеров с длинным периодом (минута, 5 секунд) это не так критично, то для выполнения действий с высокой строго заданной частотой любая маленькая задержка в главном цикле может стать большой проблемой! Выходом из ситуации может стать прерывание по таймеру. Вспомните урок про аппаратные прерывания: прерывание позволяет “выйти” из любого выполняемого на данный момент участка кода в основном цикле, выполнить нужный блок кода, который находится внутри прерывания, и вернуться туда, откуда вышли, и продолжить выполнение. Таким образом это практически параллельное выполнение задач. В этом уроке мы научимся делать это по аппаратному таймеру. Для чего использовать прерывания по таймеру?

  • Генерация сигналов
  • Измерение времени
  • Параллельное выполнение задач
  • Выполнение задачи через строго заданный период времени
  • И многое другое

Таймеры

Прерывания генерируются отдельным аппаратным таймером, который находится в микроконтроллере где-то рядом с вычислительным ядром. Аппаратный таймер, он же счётчик, занимается очень простой задачей: считает “тики” тактового генератора (который задаёт частоту работы всей системы) и, в зависимости от режима работы, может дёргать ногами или давать сигнал на микроконтроллер при определённых значениях счётчика. Таким образом “разрешение” работы таймера – один тик (такт) задающего генератора, при 16 МГц это 0.0625 микросекунды. Второй важный момент для понимания: таймер-счётчик работает и считает импульсы параллельно вычислительному ядру. Именно поэтому генерация ШИМ сигнала даже на высокой частоте абсолютно не влияет на выполнение кода – оно всё происходит параллельно. В ардуино нано (atmega328) у нас три таких таймера, и каждый может активировать независимое прерывание по своему периоду. Что касается счета времени: функции millis() и micros() как раз таки работают на прерывании таймера 0. Если перенастроить таймер 0 – у нас пропадёт корректный счёт времени (и, возможно, ШИМ на пинах 5 и 6). Некоторые библиотеки также используют прерывания таймеров, например Servo использует первый, а встроенная функция tone() – второй. Также мы обсуждали в уроках что таймеры занимаются генерацией ШИМ сигнала на своих пинах, и при перенастройке таймера ШИМ может отключиться, начать работать в другом режиме или изменить частоту. В отличие от генерации ШИМ сигнала и аппаратных прерываний, управление прерываниями по таймерам не реализовано разработчиками Ардуино в ядре и стандартных библиотеках, поэтому работать с прерываниями будем при помощи сторонних библиотек. Можно работать с таймером напрямую, как описано в даташите, но это не входит в данный курс уроков. Для первого и второго таймеров можно найти старые библиотеки, называются timerOne и timerTwo. У меня есть своя библиотека – GyverTimers, которая позволяет гибко настроить все таймеры на atmega328 (Arduino UNO/Nano) и atmega2560 (Arduino Mega). Скачать библиотеку можно по прямой ссылке, также про нее есть отдельная страница у меня на сайте, с описанием, документацией и примерами. Рассмотрим основные инструменты библиотеки.

Библиотека GyverTimers

Таблица таймеров ATmega328p

Таймер Разрядность Частоты Периоды Выходы Пин Arduino Пин МК
Timer0 8 бит 61 Гц.. 1 МГц 16 384.. 1 мкс CHANNEL_A D6 PD6
CHANNEL_B D5 PD5
Timer1 16 бит 0.24 Гц.. 1 МГц 4 200 000.. 1 мкс CHANNEL_A D9 PB1
CHANNEL_B D10 PB2
Timer2 8 бит 61 Гц.. 1 МГц 16 384.. 1 мкс CHANNEL_A D11 PB3
CHANNEL_B D3 PD3

Таблица таймеров ATmega2560

Таймер Разрядность Частоты Периоды Выходы Пин Arduino Пин МК
Timer0 8 бит 61 Гц.. 1 МГц 16 384.. 1 мкс CHANNEL_A 13 PB7
CHANNEL_B 4 PG5
Timer1 16 бит 0.24 Гц.. 1 МГц 4 200 000.. 1 мкс CHANNEL_A 11 PB5
CHANNEL_B 12 PB6
CHANNEL_C 13 PB7
Timer2 8 бит 61 Гц.. 1 МГц 16 384.. 1 мкс CHANNEL_A 10 PB4
CHANNEL_B 9 PH6
Timer3 16 бит 0.24 Гц.. 1 МГц 4 200 000.. 1 мкс CHANNEL_A 5 PE3
CHANNEL_B 2 PE4
CHANNEL_C 3 PE5
Timer4 16 бит 0.24 Гц.. 1 МГц 4 200 000.. 1 мкс CHANNEL_A 6 PH3
CHANNEL_B 7 PH4
CHANNEL_C 8 PH5
Timer5 16 бит 0.24 Гц.. 1 МГц 4 200 000.. 1 мкс CHANNEL_A 46 PL3
CHANNEL_B 45 PL4
CHANNEL_C 44 PL5

Максимальный период

В таблице выше приведены диапазоны для 16 МГц тактирования. Для другого системного клока максимальный период считается по формуле, где F_CPU – системная частота в Гц:

  • 8 бит таймеры: (1000000UL / F_CPU) * (1024 * 256)
  • 16 бит таймеры: (1000000UL / F_CPU) * (1024 * 65536)

Настройка частоты/периода

  • setPeriod(период); – установка периода в микросекундах и запуск таймера. Возвращает реальный период в мкс (точность ограничена разрешением таймера).
  • setFrequency(частота); – установка частоты в Герцах и запуск таймера. Возвращает реальную частоту в Гц (точность ограничена разрешением таймера).
  • setFrequencyFloat(частота float); – установка частоты в Герцах и запуск таймера, разрешены десятичные дроби. Возвращает реальную частоту (точность ограничена разрешением таймера).

Контроль работы таймера

  • pause(); – приостановить счёт таймера, не сбрасывая счётчик
  • resume(); – продолжить счёт после паузы
  • stop(); – остановить счёт и сбросить счётчик
  • restart(); – перезапустить таймер (сбросить счётчик)

Прерывания

  • enableISR(канал, фаза); – запустить прерывания на выбранном канале с выбранным сдвигом фазы. Если ничего не указывать, будет выбран канал A и фаза 0
    • Канал – CHANNEL_A , CHANNEL_B или CHANNEL_С (см. таблицу выше!)
    • Фаза – численное значение 0-359
  • disableISR(канал); – отключить прерывания на выбранном канале. Если ничего не указывать, будет выбран канал A

Библиотека даёт прямой доступ к прерыванию без “Ардуиновских” attachInterrupt, что позволяет сократить время вызова функции-обработчика прерывания. Прерывание с настроенной частотой будет обрабатываться в блоке вида ISR(канал) <> , пример:

Аппаратные выходы

  • outputEnable(канал, режим); – включить управление аппаратным выходом таймера
    • Канал: CHANNEL_A или CHANNEL_B (+ CHANNEL_C у ATmega2560, см. таблицу таймеров).
    • Режим: TOGGLE_PIN , CLEAR_PIN , SET_PIN (переключить/выключить/включить пин по срабатыванию таймера)
  • outputDisable(канал); – отключить выход таймера
    • Канал: CHANNEL_A или CHANNEL_B (+ CHANNEL_C у Mega2560, см. таблицу таймеров)
  • outputState(канал, состояние); – вручную сменить состояние канала. Например для установки каналов в разное состояние для запуска генерации двухтактного меандра.
    • Канал: CHANNEL_A или CHANNEL_B (+ CHANNEL_C у ATmega2560, см. таблицу таймеров).
    • Состояние: HIGH или LOW

Важно: при генерации меандра реальная частота будет в два раза меньше заданной из-за особенности работы самого таймера. См. примеры с меандром.

Сдвиг фазы (с 1.6)

При помощи phaseShift(source, angle) можно сдвинуть прерывания или переключения пинов на выбранном канале source по фазе angle – угол сдвига в градусах от 0 до 360.

  • У 8-битных таймеров можно задать сдвиг только у второго канала ( CHANNEL_B )
  • У 16-битных можно двигать все три канала

Настройка по умолчанию

При помощи метода setDefault() можно сбросить настройки таймера на “Ардуиновские” умолчания: частоту и режим работы.

Embedder’s life

Краткое пособие по микроконтроллерам AVR. Часть 2.

Прерывания. Таймеры/счетчики.

Таймеры — еще один классический модуль, присутствующий практически во всех МК. Он позволяет решать множество задач, самая распространная из которых — задание стабильных временных интервалов. Второе по популярности применение — генерация ШИМ (о нем далее) на выводе МК. Несмотря на то, что, как уже было сказано, применение таймеров отнюдь не ограничивается этими задачами, здесь будут рассмотрены только эти две как наиболее распространенные.

Сам по себе таймер представляет собой двоичный счетчик, подключенный к системе тактирования микроконтроллера через дополнительный делитель. К нему, в свою очередь, подключены блоки сравнения (их может быть много), которые способны выполнять разные полезные функции и генерировать прерывания — в зависимости от настроек. Упрощенно устройство таймера можно представить следующим образом:

Настройка таймера, как и всей остальной периферии, производится через его регистры.

Генерация временных интервалов с помощью таймера.

Прерывания.

Из названия явствует, что главным назначением блоков сравнения является постоянное сравнение текущего значения таймера со значением, заданным в регистре OCRnX. Уже упоминалось, что имена регистров часто несут в себе глубокий сакральный смысл — и регистры сравнения не являются исключением. Так, n обозначает номер таймера, X — букву (тоже способ нумерации, блоков сравнения может быть много) регистра сравнения. Таким образом, OCR1A можно понять как Output Compare Register of 1 st timer, unit A. К слову, искушенному эмбеддеру это даcт возможность предположить, что, возможно, существует таймер 0 и регистр сравнения B…

Читайте также  Соединение интернет кабеля между собой

Итак, блоки сравнения могут генерировать прерывания при каждом совпадении значения таймера (к слову, оно находится в регистре TCNTnTimer/CouNTer #n) с заданым числом. Читателю уже должно быть знакомо понятие прерывания, однако на всякий случай освежим его в памяти, а заодно и поговорим о том, как его описать на С. Так вот, вышесказанное значит, что, как только случится описанное событие, процессор сохранит номер текущей команды в стеке и перейдет к выполению специально определенного кода, а после вернется обратно. Все происходит почти так же, как и при вызове обычной функции, только вызывается она на аппаратном уровне. Объявляются такие функции с помощью макроса, объявленного в avr/interrupt.h (ISR — «Interrupt Service Routine», «обработчик прерывания»):

Каждому прерыванию (естесственно, их много) соответствует т.н. вектор прерывания — константа, также объявленная в avr/interrupt. Например, обработчик прерывания по совпадению значения таймера со значением регистра OCR1A будет иметь следующий вид:

Несомненно, проницательный читатель уже догадался, каким образом формируются имена векторов. Тем не менее, полный список этих констант можно посмотреть в документации на avr-libc (библиотека стандартных функций для AVR-GCC).

Итак, для того, чтобы получить функцию, вызываемую через точные промежутки времени, нам осталось только сконфигурировать блок сравнения. Нужные настройки находятся в регистрах TCCR1B, TIMSK1 и, конечно, OCR1A. Здесь нам придется обратиться к даташиту на ATmega48.

Даташит (от англ. datasheet) — файл технической документации, описание конкретного прибора (микросхемы, транзистора и т.д.). Содержит всю информацию о характеристиках и применении компонента. Почти всегда имеет формат PDF. Обычно гуглится как « pdf».

Последние три бита управляют предделителем, упомянутым в самом начале (остальные же нас пока не интересуют):

Сконфигурируем таймер так, чтобы прерывания происходили два раза в секунду. Выберем предделитель 64; для этого установим биты CS11 и CS10:

Тогда частота счета составит 8МГц/64=125КГц, т.е. каждые 8мкС к значению TCNT1 будет прибавляться единица. Мы хотим, чтобы прерывания происходили с периодом 500мС. Очевидно, что за это время таймер досчитает до значения 500мС/8мкС=62500, или 0xF424. Таймер 1 — шестнадцатибитный, так что все в порядке.

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

Осталось только разрешить прерывание по совпадению — за него отвечает бит в регистре TIMSK1:

Про него написано следующее:

Итак, устанавливаем нужное значение:

Кроме того, следует помнить, что перед использованием прерываний необходимо их глобально разрешить вызовом функции sei(). Для глобального запрета прерываний служит функция cli(). Эти функции устанавливают/очищают бит I в регистре SREG, управляя самой возможностью использования такого механизма, как прерывания. Регистры же вроде TIMSKn — не более чем локальные настройки конкретного модуля.

Как уже упоминалось, прерывание может возникнуть в любой момент, прервав программу в любом месте. Однако существуют случаи, когда это нежелательно. Механизм глобального запрета/разрешения прерываний позволяет решить эту проблему.

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

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

Генерация ШИМ с помощью таймера.

При определеных настройках блоки сравнения позволяют организовать аппаратную генерацию ШИМ-сигнала на ножках МК, обозначенных как OСnX:

ШИМ (PWM) — Широтно-Импульсная Модуляция (Pulse Width Modulation). ШИМ-сигнал представляет собой последовательность прямоугольных импульсов с изменяющейся длительностью:

Для ШИМ вводятся две родственные характеристики — коэффициент заполнения (duty cycle, D) и скважность — величина, обратная коэффицинту заполнения. Коэффициент заполнения представляет собой отношение времени импульса к длительности периода:

Коэффициент заполнения часто выражается в процентах, но так же распространена запись в десятичных дробях.

Значение ШИМ для народного хозяйства заключается в том, что действующее значение напряжения такого сигнала прямо пропоционально коэффициенту заполнения:

— с этим интегралом пособие смотрится солиднее; зависимость же выражается следующей формулой:

Uavg — среднее значение напряжения (тут — оно же действующее);
D — коэффициент заполнения;
Up-p — амплитуда импульса.

Таким образом, ШИМ является простым способом получить аналоговый сигнал с помощью микроконтроллера — для этого такую последовательность импульсов надо подать на фильтр низких частот (который, кстати, и является физическим воплощением интеграла, записанного выше).

Наиболее употребительным режимом ШИМ является т.н. Fast PWM (об остальных режимах можно прочесть непосредственно в документации), поэтому рассмотрим его. В этом случае блоки сравнения работают следующим образом: с обнулением таймера на выход OCnX подается высокий уровень; как только таймер досчитает до числа, записанного в OCRnX, OCnX переводится в состояние низкого уровня. Все это повторяется с периодом переполнения счетчика. Получается, что ширина выходного импульса зависит от значения OCRnX, а выходная частота равна тактовой частоте таймера, поделенной на его максимальное значение. Рисунок из даташита поясняет сказанное:

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

Настройка блока сравнения для генерации ШИМ.

Здесь нам опять поможет документация. Итак, сначала надо перевести блок сравнения в режим генерации ШИМ и выбрать интересующий выход из доступных. Эти настройки доступны в регистре TCCR0A:

Нас интересуют биты WGMxx и COMnXn. Про них сказано следующее:

Т.е., нас интересуют биты WGM00 и WGM01 — Fast PWM mode,

а также COM0A1 — non-inverting PWM на выводе OC0A. Настраиваем:

Естесственно, кроме этого выбранная ножка должна быть настроена на выход с помощью регистра DDR соответствующего порта.

Далее стоит инициализировать регистр OCR0A, например, значением 128 — 50% коэффициент заполнения:

И, наконец, включить таймер, выбрав делитель. Тут все так же:


Обычно для ШИМ выбирается максимально возможная частота (для того, чтобы получить максимальное качество выходного сигнала). Т.е., целесообразно установить минимальное значение делителя:

На этом этапе настройка ШИМ завершается, и на выбранной ножке можно увидеть сигнал.

Как упомянуто выше, ШИМ — простой способ получения аналогового сигнала с помощью МК. Например, можно организовать плавное мигание светодиода (в этом случае роль интегратора-ФНЧ выполняет глаз наблюдателя, так что светодиод можно подключить к ножке МК через обычный резистор).

Некоторые моменты в предлагаемом примере требуют пояснения.

В списке включаемых файлов присутствует загадочный stdint.h — в этом файле объявлены типы с явно указанной разрядностью, например

uint8_tunsigned 8-bit integer type
uint16_tunsigned 16-bit integer type
uint32_tunsigned 32-bit integer type
int8_t — signed 8-bit integer type

и так далее. Такие типы способствуют единообразию и удобочитаемости программы. Кроме того, гарантируется, что при портировании кода разрядность данных останется указанной. И, кстати, uint8_t писать гораздо быстрее, чем unsigned char.

Модификатор volatile означает, что компилятору запрещается оптимизировать данную переменную. Например, если скомпилировать следующий пример:

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

Arduino и прерывания таймера

Привет, Хабр! Представляю вашему вниманию перевод статьи «Timer interrupts» автора E.

Предисловие

Плата Arduino позволяет быстро и минимальными средствами решить самые разные задачи. Но там где нужны произвольные интервалы времени (периодический опрос датчиков, высокоточные ШИМ сигналы, импульсы большой длительности) стандартные библиотечные функции задержки не удобны. На время их действия скетч приостанавливается и управлять им становится невозможно.

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

В этой статье обсуждаются таймеры AVR и Arduino и то, как их использовать в Arduino проектах и схемах пользователя.

Что такое таймер?

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

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

Итак, предположим, что имеется устройство, которое должно что-то делать, например мигать светодиодом каждые 5 секунд. Если не использовать таймеры, а писать обычный код, то надо установить переменную в момент зажигания светодиода и постоянно проверять не наступил ли момент ее переключения. С прерыванием по таймеру вам достаточно настроить прерывание, и затем запустить таймер. Светодиод будет мигать точно вовремя, независимо от действий основной программы.

Как работает таймер?

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

Вы можете проверять этот флаг вручную или можете сделать таймерный переключатель — вызывать прерывание автоматически в момент установки флага. Подобно всяким другим прерываниям вы можете назначить служебную подпрограмму прерывания (Interrupt Service Routine или ISR), чтобы выполнить заданный код, когда таймер переполнится. ISR сама сбросит флаг переполнения, поэтому использование прерываний обычно лучший выбор из-за простоты и скорости.

Чтобы увеличивать значения счетчика через точные интервалы времени, таймер надо подключить к тактовому источнику. Тактовый источник генерирует постоянно повторяющийся сигнал. Каждый раз, когда таймер обнаруживает этот сигнал, он увеличивает значение счетчика на единицу. Поскольку таймер работает от тактового источника, наименьшей измеряемой единицей времени является период такта. Если вы подключите тактовый сигнал частотой 1 МГц, то разрешение таймера (или период таймера) будет:

T = 1 / f (f это тактовая частота)
T = 1 / 1 МГц = 1 / 10^6 Гц
T = (1 ∗ 10^-6) с

Таким образом разрешение таймера одна миллионная доля секунды. Хотя вы можете применить для таймеров внешний тактовый источник, в большинстве случаев используется внутренний источник самого чипа.

Типы таймеров

В стандартных платах Arduino на 8 битном AVR чипе имеется сразу несколько таймеров. У чипов Atmega168 и Atmega328 есть три таймера Timer0, Timer1 и Timer2. Они также имеют сторожевой таймер, который можно использовать для защиты от сбоев или как механизм программного сброса. Вот некоторые особенности каждого таймера.

Timer0:
Timer0 является 8 битным таймером, это означает, что его счетный регистр может хранить числа вплоть до 255 (т. е. байт без знака). Timer0 используется стандартными временными функциями Arduino такими как delay() и millis(), так что лучше не запутывать его если вас заботят последствия.

Timer1:
Timer1 это 16 битный таймер с максимальным значением счета 65535 (целое без знака). Этот таймер использует библиотека Arduino Servo, учитывайте это если применяете его в своих проектах.

Timer2:
Timer2 — 8 битный и очень похож на Timer0. Он используется в Arduino функции tone().

Timer3, Timer4, Timer5:
Чипы ATmega1280 и ATmega2560 (установлены в вариантах Arduino Mega) имеют три добавочных таймера. Все они 16 битные и работают аналогично Timer1.

Конфигурация регистров

Для того чтобы использовать эти таймеры в AVR есть регистры настроек. Таймеры содержат множество таких регистров. Два из них — регистры управления таймера/счетчика содержат установочные переменные и называются TCCRxA и TCCRxB, где x — номер таймера (TCCR1A и TCCR1B, и т. п.). Каждый регистр содержит 8 бит и каждый бит хранит конфигурационную переменную. Вот сведения из даташита Atmega328:

Читайте также  Конструкция для модулей из светодиодов
TCCR1A
Бит 7 6 5 4 3 2 1
0x80 COM1A1 COM1A0 COM1B1 COM1B0 WGM11 WGM10
ReadWrite RW RW RW RW R R RW RW
Начальное значение
TCCR1B
Бит 7 6 5 4 3 2 1
0x81 ICNC1 ICES1 WGM13 WGM12 CS12 CS11 CS10
ReadWrite RW RW R RW RW RW RW RW
Начальное значение

Наиболее важными являются три последние бита в TCCR1B: CS12, CS11 и CS10. Они определяют тактовую частоту таймера. Выбирая их в разных комбинациях вы можете приказать таймеру действовать на различных скоростях. Вот таблица из даташита, описывающая действие битов выбора:

CS12 CS11 CS10 Действие
Нет тактового источника (Timer/Counter остановлен)
1 clk_io/1 (нет деления)
1 clk_io/8 (делитель частоты)
1 1 clk_io/64 (делитель частоты)
1 clk_io/256 (делитель частоты)
1 1 clk_io/1024 (делитель частоты)
1 1 Внешний тактовый источник на выводе T1. Тактирование по спаду
1 1 1 Внешний тактовый источник на выводе T1. Тактирование по фронту

По умолчанию все эти биты установлены на ноль.

Допустим вы хотите, чтобы Timer1 работал на тактовой частоте с одним отсчетом на период. Когда он переполнится, вы хотите вызвать подпрограмму прерывания, которая переключает светодиод, подсоединенный к ножке 13, в состояние включено или выключено. Для этого примера запишем Arduino код, но будем использовать процедуры и функции библиотеки avr-libc всегда, когда это не делает вещи слишком сложными. Сторонники чистого AVR могут адаптировать код по своему усмотрению.

Сначала инициализируем таймер:

Регистр TIMSK1 это регистр маски прерываний Таймера/Счетчика1. Он контролирует прерывания, которые таймер может вызвать. Установка бита TOIE1 приказывает таймеру вызвать прерывание когда таймер переполняется. Подробнее об этом позже.

Когда вы устанавливаете бит CS10, таймер начинает считать и, как только возникает прерывание по переполнению, вызывается ISR(TIMER1_OVF_vect). Это происходит всегда когда таймер переполняется.

Дальше определим функцию прерывания ISR:

Сейчас мы можем определить цикл loop() и переключать светодиод независимо от того, что происходит в главной программе. Чтобы выключить таймер, установите TCCR1B=0 в любое время.

Как часто будет мигать светодиод?

Timer1 установлен на прерывание по переполнению и давайте предположим, что вы используете Atmega328 с тактовой частотой 16 МГц. Поскольку таймер 16-битный, он может считать до максимального значения (2^16 – 1), или 65535. При 16 МГц цикл выполняется 1/(16 ∗ 10^6) секунды или 6.25e-8 с. Это означает что 65535 отсчетов произойдут за (65535 ∗ 6.25e-8 с) и ISR будет вызываться примерно через 0,0041 с. И так раз за разом, каждую четырехтысячную секунды. Это слишком быстро, чтобы увидеть мерцание.

Если мы подадим на светодиод очень быстрый ШИМ сигнал с 50% заполнением, то свечение будет казаться непрерывным, но менее ярким чем обычно. Подобный эксперимент показывает удивительную мощь микроконтроллеров — даже недорогой 8-битный чип может обрабатывать информацию намного быстрей чем мы способны обнаружить.

Делитель таймера и режим CTC

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

то частота тактового источника поделится на 1024. Это дает разрешение таймера 1/(16 ∗ 10^6 / 1024) или 6.4e-5 с. Теперь таймер будет переполняться каждые (65535 ∗ 6.4e-5с) или за 4,194с. Это слишком долго.

Но есть и другой режим AVR таймера. Он называется сброс таймера по совпадению или CTC. Вместо счета до переполнения, таймер сравнивает свой счетчик с переменой которая ранее сохранена в регистре. Когда счет совпадет с этой переменной, таймер может либо установить флаг, либо вызвать прерывание, точно так же как и в случае переполнения.

Чтобы использовать режим CTC надо понять, сколько циклов вам нужно, чтобы получить интервал в одну секунду. Предположим, что коэффициент деления по-прежнему равен 1024.

Расчет будет следующий:

Вы должны добавить дополнительную единицу к числу отсчетов потому что в CTC режиме при совпадении счетчика с заданным значением он сбросит сам себя в ноль. Сброс занимает один тактовый период, который надо учесть в расчетах. Во многих случаях ошибка в один период не слишком значима, но в высокоточных задачах она может быть критичной.

Функция настройки setup() будет такая:

Также нужно заменить прерывание по переполнению на прерывание по совпадению:

Сейчас светодиод будет зажигаться и гаснуть ровно на одну секунду. А вы можете делать все что угодно в цикле loop(). Пока вы не измените настройки таймера, программа никак не связана с прерываниями. У вас нет ограничений на использование таймера с разными режимами и настройками делителя.

Вот полный стартовый пример который вы можете использовать как основу для собственных проектов:

Помните, что вы можете использовать встроенные ISR функции для расширения функций таймера. Например вам требуется опрашивать датчик каждые 10 секунд. Но установок таймера, обеспечивающих такой долгий счет без переполнения нет. Однако можно использовать ISR чтобы инкрементировать счетную переменную раз в секунду и затем опрашивать датчик когда переменная достигнет 10. С использованием СТС режима из предыдущего примера прерывание могло бы выглядеть так:

Поскольку переменная будет модифицироваться внутри ISR она должна быть декларирована как volatile. Поэтому, при описании переменных в начале программы вам надо написать:

Послесловие переводчика

В свое время эта статья сэкономила мне немало времени при разработке прототипа измерительного генератора. Надеюсь, что она окажется полезной и другим читателям.

Прерывания Arduino с помощью attachInterrupt

Прерывания – очень важный механизм Arduino, позволяющий внешним устройствам взаимодействовать с контроллером при возникновении разных событий. Установив обработчик аппаратных прерываний в скетче, мы сможем реагировать на включение или выключение кнопки, нажатие клавиатуры, мышки, тики таймера RTC, получение новых данных по UART, I2C или SPI. В этой статье мы узнаем, как работают прерывания на платах Ардуино Uno, Mega или Nano и приведем пример использования функции Arduino attachInterrupt().

Прерывания в Ардуино

Прерывание – это сигнал, который сообщает процессору о наступлении какого-либо события, которое требует незамедлительного внимания. Процессор должен отреагировать на этот сигнал, прервав выполнение текущих инструкций и передав управление обработчику прерывания (ISR, Interrupt Service Routine). Обработчик – это обычная функция, которую мы пишем сами и помещаем туда тот код, который должен отреагировать на событие.

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

Аппаратные и программные прерывания

Прерывания в Ардуино можно разделить на несколько видов:

  • Аппаратные прерывания. Прерывание на уровне микропроцессорной архитектуры. Самое событие может произойти в производительный момент от внешнего устройства – например, нажатие кнопки на клавиатуре, движение компьютерной мыши и т.п.
  • Программные прерывания. Запускаются внутри программы с помощью специальной инструкции. Используются для того, чтобы вызвать обработчик прерываний.
  • Внутренние (синхронные) прерывания. Внутреннее прерывание возникает в результате изменения или нарушения в исполнении программы (например, при обращении к недопустимому адресу, недопустимый код операции и другие).

Зачем нужны аппаратные прерывания

Аппаратные прерывания возникают в ответ на внешнее событие и исходят от внешнего аппаратного устройства. В Ардуино представлены 4 типа аппаратных прерываний. Все они различаются сигналом на контакте прерывания:

  • Контакт притянут к земле. Обработчик прерывания исполняется до тех пор, пока на пине прерывания будет сигнал LOW.
  • Изменение сигнала на контакте. В таком случае Ардуино выполняет обработчик прерывания, когда на пине прерывания происходит изменение сигнала.
  • Изменение сигнала от LOW к HIGH на контакте – при изменении с низкого сигнала на высокий будет исполняться обработчик прерывания.
  • Изменение сигнала от HIGH к LOW на контакте – при изменении с высокого сигнала на низкий будет исполняться обработчик прерывания.

Прерывания полезны в программах Ардуино, так как помогают решать проблемы синхронизации. Например, при работе с UART прерывания позволяют не отслеживать поступление каждого символа. Внешнее аппаратное устройство подает сигнал прерывания, процессор сразу же вызывает обработчик прерывания, который вовремя захватывает символ. Это позволяет экономить процессорное время, которое без прерываний тратилось бы на проверку статуса UART, вместо этого все необходимые действия выполняются обработчиком прерывания, не затрагивая главную программу. Особых возможностей от аппаратного устройства не требуется.

Основными причинами, по которым необходимо вызвать прерывание, являются:

  • Определение изменения состояния вывода;
  • Прерывание по таймеру;
  • Прерывания данных по SPI, I2C, USART;
  • Аналогово-цифровое преобразование;
  • Готовность использовать EEPROM, флеш-память.

Как реализуются прерывания в Ардуино

При поступлении сигнала прерывания работа в цикле loop() приостанавливается. Начинается выполнение функции, которая объявляется на выполнение при прерывании. Объявленная функция не может принимать входные значения и возвращать значения при завершении работы. На сам код в основном цикле программы прерывание не влияет. Для работы с прерываниями в Ардуино используется стандартная функция attachInterrupt().

Отличие реализации прерываний в разных платах Ардуино

В зависимости от аппаратной реализации конкретной модели микроконтроллера есть несколько прерываний. Плата Arduino Uno имеет 2 прерывания на втором и третьем пине, но если требуется более двух выходов, плата поддерживает специальный режим «pin-change». Этот режим работает по изменению входа для всех пинов. Отличие режима прерывания по изменению входа заключается в том, что прерывания могут генерироваться на любом из восьми контактов. Обработка в таком случае будет сложнее и дольше, так как придется отслеживать последнее состояние на каждом из контактов.

На других платах число прерываний выше. Например, плата Ардуино Мега 2560 имеет 6 пинов, которые могут обрабатывать внешние прерывания. Для всех плат Ардуино при работе с функцией attachInterrupt (interrupt, function, mode) аргумент Inerrupt 0 связан с цифровым пином 2.

Прерывания в языке Arduino

Теперь давайте перейдем к практике и поговорим о том, как использовать прерывания в своих проектах.

Функция attachInterrupt используется для работы с прерываниями. Она служит для соединения внешнего прерывания с обработчиком.

Синтаксис вызова: attachInterrupt(interrupt, function, mode)

  • interrupt – номер вызываемого прерывания (стандартно 0 – для 2-го пина, для платы Ардуино Уно 1 – для 3-го пина),
  • function – название вызываемой функции при прерывании(важно – функция не должна ни принимать, ни возвращать какие-либо значения),
  • mode – условие срабатывания прерывания.

Возможна установка следующих вариантов условий срабатывания:

  • LOW – выполняется по низкому уровню сигнала, когда на контакте нулевое значение. Прерывание может циклично повторяться – например, при нажатой кнопке.
  • CHANGE – по фронту, прерывание происходит при изменении сигнала с высокого на низкий или наоборот. Выполняется один раз при любой смене сигнала.
  • RISING – выполнение прерывания один раз при изменении сигнала от LOW к HIGH.
  • FALLING – выполнение прерывания один раз при изменении сигнала от HIGH к LOW.4

Важные замечания

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

  • Функция – обработчик не должна выполняться слишком долго. Все дело в том, что Ардуино не может обрабатывать несколько прерываний одновременно. Пока выполняется ваша функция-обработчик, все остальные прерывания останутся без внимания и вы можете пропустить важные события. Если надо делать что-то большое – просто передавайте обработку событий в основном цикле loop(). В обработчике вы можете лишь устанавливать флаг события, а в loop – проверять флаг и обрабатывать его.
  • Нужно быть очень аккуратными с переменными. Интеллектуальный компилятор C++ может “пере оптимизировать” вашу программу – убрать не нужные, на его взгляд, переменные. Компилятор просто не увидит, что вы устанавливаете какие-то переменные в одной части, а используете – в другой. Для устранения такой вероятности в случае с базовыми типами данных можно использовать ключевое слово volatile, например так: volatile boolean state = 0. Но этот метод не сработает со сложными структурами данных. Так что надо быть всегда на чеку.
  • Не рекомендуется использовать большое количество прерываний (старайтесь не использовать более 6-8). Большое количество разнообразных событий требует серьезного усложнения кода, а, значит, ведет к ошибкам. К тому же надо понимать, что ни о какой временной точности исполнения в системах с большим количеством прерываний речи быть не может – вы никогда точно не поймете, каков промежуток между вызовами важных для вас команд.
  • В обработчиках категорически нельзя использовать delay(). Механизм определения интервала задержки использует таймеры, а они тоже работают на прерываниях, которые заблокирует ваш обработчик. В итоге все будут ждать всех и программа зависнет. По этой же причине нельзя использовать протоколы связи, основанные на прерываниях (например, i2c).

Примеры использования attachInterrupt

Давайте приступим к практике и рассмотрим простейший пример использования прерываний. В примере мы определяем функцию-обработчик, которая при изменении сигнала на 2 пине Arduino Uno переключит состояние пина 13, к которому мы традиционно подключим светодиод.

Давайте рассмотрим несколько примеров более сложных прерываний и их обработчиков: для таймера и кнопок.

Прерывания по нажатию кнопки с антидребезгом

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

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

Этот код позволяет удалить дребезг и не блокирует исполнение программы, как в случае с функцией delay, которая недопустима в прерываниях.

Прерывания по таймеру

Таймером называется счетчик, который производит счет с некоторой частотой, получаемой из процессорных 16 МГц. Можно произвести конфигурацию делителя частоты для получения нужного режима счета. Также можно настроить счетчик для генерации прерываний при достижении заданного значения.

Таймер и прерывание по таймеру позволяет выполнять прерывание один раз в миллисекунду. В Ардуино имеется 3 таймера – Timer0, Timer1 и Timer2. Timer0 используется для генерации прерываний один раз в миллисекунду, при этом происходит обновление счетчика, который передается в функцию millis (). Этот таймер является восьмибитным и считает от 0 до 255. Прерывание генерируется при достижении значения 255. По умолчанию используется тактовый делитель на 65, чтобы получить частоту, близкую к 1 кГц.

Для сравнения состояния на таймере и сохраненных данных используются регистры сравнения. В данном примере код будет генерировать прерывание при достижении значения 0xAF на счетчике.

Требуется определить обработчик прерывания для вектора прерывания по таймеру. Вектором прерывания называется указатель на адрес расположения команды, которая будет выполняться при вызове прерывания. Несколько векторов прерывания объединяются в таблицу векторов прерываний. Таймер в данном случае будет иметь название TIMER0_COMPA_vect. В этом обработчике будут производиться те же действия, что и в loop ().

Подведение итогов

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

Прерывания.

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

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

Что если в эту программу добавить обработку нажатия кнопки?

while (1) < if(PINB.1==0) < PORTD.0=1; >PORTB.0=1; delay_ms(500); PORTB.0=0; delay_ms(500); >

Допустим пользователь нажал кнопку в момент, когда программа выполняет строчку PORTB.0=1, т.е. до того как будет проверено наше условие if(PINB.1==0), должны выполниться две строчки с задержкой по 500ms. Получается пользователь должен держать кнопку больше секунды, чтобы она сработала, это жутко не удобно.

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

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

Например, выполняется ваш цикл мигалки, при этом происходит некоторое событие, программа останавливается, допустим на строчке PORTB.0=0 и перескакивает на строчку if(PINB.1==0). После выполнения этой строчки, программа вернется к основному циклу на строчку delay_ms и будет крутить основной цикл, до тех пор пока снова не будет вызвано следующее прерывание.

Так что же это за такие события, способные остановить выполнения основной программы. Например, переполнение таймера. Допустим, вы настроили таймер, который умеет считать до 255 и у него есть прерывание по переполнению. Это значит, что он досчитает до 255, и вызовет, тот самый важный кусок кода.

interrupt [TIM0_OVF] void timer0_ovf_isr(void)

Еще одним вариантом прерывания таймера, является прерывание по совпадению. Представьте себе, что таймер тикает 1 раз в секунду, мне нужно чтобы он дотикал до 60 и прибавил в переменную минута единичку. Для этого в регистр сравнения таймера нужно запихать 60, дальше он сам по себе будет проверять равно ли текущее количество тиков заданному. Когда это произойдет, программа автоматически прыгнет и выполнит требуемый код.

interrupt [TIM1_COMPA] void timer1_compa_isr(void)

Обратите внимание название функций содержат названия таймеров, в первом случае TIM0 уже понятно что прерывание относится к таймеру0, во втором TIM1 — таймер1. Также из названия функции понятно по какому событию происходит прерывание COMPA — compare — сравнение, OVF — overflow, переполнение.

Кроме того, существуют прерывания, которые не связаны с таймерами, например внешнее прерывание. У микроконтроллеров есть ножки отведенные под внешнее прерывание и при воздействии на эту ножку извне программа выполняет код указанный в прерывании. Можно настроить так, чтобы прерывание возникало при низком уровне на ножке, нарастании/спаду фронта или по любому изменению. Об этом уже говорилось в статье про частотомер.

interrupt [EXT_INT0] void ext_int0_isr(void)

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

Кстати, иногда по тексту программы требуется отключить/включить прерывания.

// Разрешить глобальные прерывания #asm(«sei») // Запретить глобальные прерывания #asm(«cli»)

Как же узнать, какие прерывания есть у используемого микроконтроллера? Либо читать документацию (даташит) на него, либо посмотреть в codewizard список доступных прерываний. В визарде обычно прерывания включается галочкой interrupt. Настоятельно советую разобраться с визардом, он сэкономит кучу времени.

Update. По просьбам трудящихся. Как пользоваться CodeWizard

Запускаем визард.

Смотрим на имеющуюся периферию

Сразу нужно понять, что это относится к самому камню, микроконтроллер не знает что такое кнопка, у него есть ножка и все манипуляции производятся именно с ней. Для манипуляций с ножкой имеется внешнее прерывание. Как им пользоваться описано в 13 уроке AVR.

Для прерываний по таймеру, заходим в раздел Timers/Counter, основное прерывание по таймеру это по совпадению — аналог будильника, т.е. когда выставленное значение совпало с количеством тиков. Про этот режим читаем 5 урок.

Может так оказаться что у таймера нет режима по совпадению, но наверняка есть по переполнению, т.е. если таймер 8 битный, то его максимум 2^8, когда он дотикает до этого значения он обнулится, заодно вызовет прерывание.

22 комментария: Прерывания.

Прерывание возникает в течении нескольких тактов, точно не знаю. Вопрос, если в основном цикле программы выполняется именно функция delay_ms() она до конца отрабатывает или же она тоже «делится на части» ?.

while (1)
<
PORTB.0=1;
delay_ms(500);
PORTB.0=0;
delay_ms(500);
>;
>не работает
ошибка Error: D:AVRknepa.c(3): ‘[‘ expected, but ‘(‘ found
подскажите в чем причина

interrupt это общее название всех прерываний, но нужно понимать что вызвало прерывание — таймер, кнопка, АЦП поэтому там где у вас просто interrupt должно быть interrupt [TIM1_COMPA] void timer1_compa_isr(void) что означает прерывание от таймера1 по совпадению регистра OCRA. Названия прерываний учить не нужно, достаточно пользоваться генератором кода

Извините, я новичок. Подскажите где искать генератор кода? И если не сложно вкратце как им пользоваться?

З.Ы.Мне нужно чтобы кнопка прерывала выполнение программы

На примере мигающего светодиода

Подскажите… Использую функцию прерывания по сравнению timer1.Эта функция должна принимать и передавать int i и unsigned char a[ ]. Как правильно оформить описание функции?

Используйте глобальные переменные, ибо делать функцию внутри прерывания плохая идея, либо в прерывании выставляйте флаги, а в основном цикле делайте функцию.
ЗЫ в конце статьи добавил чуток про прерывания

Здравствуйте. Подскажите пожалуйста как настраивается или откуда берется GIMSK? Я новичек еще, скачал пример кода, а cvavr пишет «undefined symbol ‘GIMSK’».

В даташите он называется General Interrupt Control Register

А он на всех мк работает? Все настройки проекта перекопал и дш, толку ноль. А в дш почти ничего нет по-этому поводу и в таблице тоже его нет, а eimsk есть…Это EIMSK не тоже самое?

настраивайте codewizard ом и не мучайтесь

Добрый Admin у тебя просто великолепный сайт. По твое сайту много научился. Можно опять попросить помощи. Пожалуйста, я вам отправлю архив с моем проектом в нем техническое задание можешь хотя бы меня проконсультировать. Codevision нормально компилирует без ошибок, вот Proteus вытворяет какие то чудеса. Кстати я уже выкладывал уже текст программы и там было ошибка (while).. OK?

можете присылать, но смотреть не обещаю

GIMSK это регистр разрешения прерывания от входов INT0 и INT1. В вашем случае он будет называться GICR.

На счёт протеуса. Удалите его и забудьте как страшный сон. Эта зараза на разных машинах работает как ей захочется. Если эмулировать пример с тупым миганием светодиода, то пойдёт. А если делать что-то серьёзное, то на выходе, то что работает в протеусе никогда не будет работать в живую. А теперь конкретные факты. В протеусе криво реализованы прерывания по таймера. Периоды плавают в зависимости от мощность процессора ПК и его загруженности. При работе с ЖК дисплеями не всегда корректно выводит информацию. Был косяк, графический дисплей 128х64 прекрасно работал в протеусе, а в железе ничего не выводил. Когда добился стабильной работы в железе, перестало работать в протеусе. Ну и если кому интересно изучить все косяки данного девайса, можно почитать на Казусе. Там о нем очень честно отзываются.

Таки нужно понимать, чего ждать от протеуса, вот и весь секрет. Рассмотрите вопрос с т.з. способов отладки, мигать светодиодом? выводить инфу в юарт? выводить инфу на дисплей? А если у вас дофига переменных, юарт занят, а дисплея в проекте нет, да еще и проект большой? Я никогда не отлаживаю в протеусе, то в чем не уверен на 100%, но если мне нужно посмотреть состояние сразу нескольких переменных, то намного быстрее поставить точку останова и посмотреть, альтернативы протеусу по скорости, в этом смысле просто нет. Просто не отлаживайте всякие дисплеи, карты памяти и прочую периферийную лабуду и будет вам счастье.

Как это нет? А куда же делся родной JTAG. Это круче любого протеуса.

у мелких атмег типо 8 увы jtag нету, а таких камней лежит еще целая кучка.

А для таких камней CVAVR может передать файлы AtmelStudio и там провести эмуляцию. По шагово с заходом в функции, при этом можно не только переменные смотреть, но и даже смотреть во все регистра.

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

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