AVR. Учебный курс

С Таймером 1 и Таймером 2 связаны три вектора прерывания:

Прерывание переполнения таймера - Timer Overflow Interrupt (INT00,2000H);

Прерывание переполнения Таймера 2 - Timer 2 Overflow Interrupt (INT12,2038H);

Прерывание фиксатора Таймера 2 - Timer 2 Capture Interrupt (INT11,2036H).

Регистр IOS1 содержит флажки, которые указывают какое событие вызвало прерывание. Обращение к битам регистра IOS1 по командам JBC или JBS обнуляет биты 0-5. По этой причине, мы рекомендуем чтобы Вы копировали содержимое регистра IOS1 в промежуточный регистр и затем выполняли команды проверки разрядов типа JBC или JBS на промежуточном регистре.

1.3.1. Прерывание переполнения таймера

И Таймер 1 и Таймер 2 могут вызвать прерывание переполнения Таймера (INT00). Установите INT_MASK.0 ,чтобы разрешить это прерывание. Установите или IOC1.2 (Таймер 1) или IOC1.3 (Таймер 2) чтобы выбрать источник прерывания. Когда происходит переполнение, устанавливается флажок состояния в регистре IOS1. Переполнение Таймера 1 устанавливает IOS1.5, а переполнение Таймера 2 устанавливает IOS1.4.

Input/Output Control Register 1

HWindow 0 (Write), HWindow 15 (Read)

Input/Output Status Register 1

Hwindow 0 (Write), HWindow 15 (Read)

1.3.2. Прерывание переполнения Таймера 2

Таймер 2 может генерировать прерывание переполнения Таймера 2 (INT12, адрес вектора - 2038H) вместо стандартного прерывания переполнения Таймера. Это прерывание разрешается установкой INT_MASK1.4. Переполнение Таймера 2 устанавливает IOS1.4. Таймер 2 может генерировать прерывание переполнения Таймера 2 или на границе 7FFFH/8000H или на границе 0FFFFH/0000H. Переполнение может происходить в любом направлении. IOC2.5 выбирает границу переполнения. Когда IOC2.5 установлен, Таймер 2 прерывается на границе 7FFFH/8000H. Иначе, он прерывается на границе 0FFFFH/0000H.

1.3.3. Прерывание фиксатора Таймера 2

Положительный переход на контакте T2CAPTURE (P2.7) заставляет значение Таймера 2 загружаться в регистр T2CAPTURE . Это событие генерирует прерывание фиксатора Таймера 2 (INT11), если установлен INT_MASK1.3 и T2CAPTURE утверждается в течение более двух времен состояний.

Timer 2 Capture Register

Hwindow 15(Read /Write)

1.4. Предосторожности при работе с Таймерами

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

Будьте осторожны при записи в регистры таймера TIMER1 и TIMER2:

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

Конфигурируйте Таймер 2 для функционирования в нормальном режиме (не в быстром режиме приращения):

Так как для полного сканирования CAM, HSO требует восьми времен состояния, Таймер 2, когда он используется как датчик времени для HSO, должен функционировать в нормальном режиме приращения (не в быстром режиме приращения) .

Очистите бит FAST_T2_ENA(IOC2.0) для выбора нормального режима работы таймера.

Конфигурируйте Таймер 2 для счета только в одном направлении.

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

Очистите бит T2UD_ENA(IOC2.1), чтобы сконфигурировать Таймер 2 как суммирующий счетчик.

Используйте предостережение при сбросе Таймера 2.

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

Когда Таймер 2 сконфигурирован для сброса внешним контактом, события программы должны происходить когда Таймер 2 равен единице, а не нулю:

Когда Таймер 2 сконфигурирован, чтобы сбрасываться внешним контактом сброса (IOC0.3 =1), программные события не должны происходить, когда Таймер 2 равен нулю. Если HSI.0 или T2RST (P2.3) сбрасывают Таймер 2, событие может не произойти. Внешние контакты сбрасывают Таймер 2 асинхронно, и Таймер 2 может увеличиться до 1 до того, как HSO может сравнить и распознать CAM запись. Программируйте события так, чтобы они происходили, когда Таймер 2 равен 1, это гарантирует,что HSO имеет достаточное время, чтобы распознать команду, записанную в CAM .

ФРАГМЕНТ ПРОГРАММЫ ИЗУЧЕНИЯ ПРОГРАММИРОВАНИЯ ТАЙМЕРОВ

;**Исследование пpoгpаммных задеpжек с использованием Timer1*

ldb wsr,#15 ; Пеpеключиться в HWindow 15

ld timer1,#0c000h ; Загpузить значение счетчика

ldb wsr,#0 ; Пеpеключиться в HWindow 0

jbc ios1, 5, $ ; Ожидание пеpеполнения Timer

;***********************************************************

Timer1

Данная библиотека представляет собой набор функций для настройки аппаратного 16-битного таймера Timer1 в ATMega168/328. В микроконтроллере доступно 3 аппаратных таймера, которые могут быть настроены различными способами для получения различных функциональных возможностей. Начало разработки данной библиотеки было вызвано необходимостью быстро и легко установить период или частоту ШИМ сигнала, но позже она разраслась, включив в себя обработку прерываний по переполнению таймера и другие функции. Она может быть легко расширена или портирована для работы с другими таймерами.

Точность таймера зависит от тактовой частоты процессора. Тактовая частота таймера Timer1 определяется установкой предварительного делителя частоты. Этот делитель может быть установлен в значения 1, 8, 64, 256 или 1024.

  • Максимальный период = (Делитель / Частота) × 2 17
  • Длительность одного отсчета = (Делитель / Частота)

Для установки просто распакуйте и поместите файлы в каталог Arduino/hardware/libraries/Timer1/ .

Timer3

Обратите внимание, что библиотека Timer1 может использоваться на Arduino Mega, но она не поддерживает все три выходных вывода OCR1A , OCR1B и OCR1C . Поддерживаются только A и B . OCR1A подключен к выводу 11 на Mega, а OCR1B - к выводу 12. С помощью одного из трех вызовов, которые задают вывод, значение 1 задаст вывод 11 на Mega, а 2 - задаст вывод 12. Библиотека Timer3 была протестирована только на Mega.

Библиотеку для таймера Timer3 можно здесь ()

Для установки просто распакуйте и поместите файлы в каталог Arduino/hardware/libraries/Timer3/ .

Методы библиотек TimerOne и TimerThree

Настройка

void initialize(long microseconds=1000000); Вы должны вызвать этот метод первым, перед использованием любых других методов библиотеки. При желании можно задать период таймера (в микросекундах), по умолчанию период устанавливается равным 1 секунде. Обратите внимание, что это нарушает работу analogWrite() на цифровых выводах 9 и 10 на Arduino. void setPeriod(long microseconds); Устанавливает период в микросекундах. Минимальный период и максимальная частота, поддерживаемые данной библиотекой, равны 1 микросекунде и 1 МГц, соответственно. Максимальный период равен 8388480 микросекунд, или примерно 8,3 секунды. Обратите внимание, что установка периода изменит частоту срабатывания прикрепленного прерывания и частоту, и коэффициент заполнения на обоих ШИМ выходах.

Управление запуском

void start(); Запускает таймер, начиная новый период. void stop(); Останавливает таймер. void restart(); Перезапускает таймер, обнуляя счетчик и начиная новый период.

Управление выходным ШИМ сигналом

void pwm(char pin, int duty, long microseconds=-1); Генерирует ШИМ сигнал на заданном выводе pin . Выходными выводами таймера Timer1 являются выводы PORTB 1 и 2, поэтому вы должны выбрать один из них, всё остальное игнорируется. На Arduino это цифровые выводы 9 и 10, эти псевдонимы также работают. Выходными выводами таймера Timer3 являются выводы PORTE , соответствующие выводам 2, 3 и 5 на Arduino Mega. Коэффициент заполнения duty задается, как 10-битное значение в диапазоне от 0 до 1023 (0 соответствует постоянному логическому нулю на выходе, а 1023 - постоянной логической единице). Обратите внимание, что при необходимости в этой функции можно установить и период, добавив значение в микросекундах в качестве последнего аргумента. void setPwmDuty(char pin, int duty); Быстрый способ для настройки коэффициента заполнения ШИМ сигнала, если вы уже настроили его, вызвав ранее метод pwm() . Этот метод позволяет избежать лишних действий по включению режима ШИМ для вывода, изменению состояния регистра, управляющего направлением движения данных, проверки необязательного значения периода и прочих действий, которые являются обязательными при вызове pwm() . void disablePwm(char pin); Выключает ШИМ на заданном выводе, после чего вы можете использовать этот вывод для чего-либо другого.

Прерывания

void attachInterrupt(void (*isr)(), long microseconds=-1); Вызывает функцию через заданный в микросекундах интервал. Будьте осторожны при попытке выполнить слишком сложный обработчик прерывания при слишком большой тактовой частоте, так как CPU может никогда не вернуться в основной цикл программы, и ваша программа будет «заперта». Обратите внимание, что при необходимости в этой функции можно установить и период, добавив значение в микросекундах в качестве последнего аргумента. void detachInterrupt(); Отключает прикрепленное прерывание.

Остальные

unsigned long read(); Считывает время с момента последнего переполнения в микросекундах.

Пример 1

В примере ШИМ сигнал с коэффициентом заполнения 50% подается на вывод 9, а прикрепленный обработчик прерывания переключает состояние цифрового вывода 10 каждые полсекунды.

#include "TimerOne.h" void setup() { pinMode(10, OUTPUT); Timer1.initialize(500000); // инициализировать timer1, и установить период 1/2 сек. Timer1.pwm(9, 512); // задать шим сигнал на выводе 9, коэффициент заполнения 50% Timer1.attachInterrupt(callback); // прикрепить callback(), как обработчик прерывания по переполнению таймера } void callback() { digitalWrite(10, digitalRead(10) ^ 1); } void loop() { // ваша программа... }

Модифицированные библиотеки от Paul Stoffregen

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

Плата ШИМ выводы TimerOne ШИМ выводы TimerThree
Teensy 3.1 3, 4 25, 32
Teensy 3.0 3, 4
Teensy 2.0 4, 14, 15 9
Teensy++ 2.0 25, 26, 27 14, 15, 16
Arduino Uno 9, 10
Arduino Leonardo 9, 10, 11 5
Arduino Mega 11, 12, 13 2, 3, 5
Wiring-S 4, 5
Sanguino 12, 13

Методы модифицированных библиотек аналогичны описанным выше, но добавлен еще один метод управления запуском таймера:

Void resume(); Возобновляет работу остановленного таймера. Новый период не начинается.

Пример 2

#include // Данный пример использует прерывание таймера, чтобы // помигать светодиодом, а также продемонстрировать, как // делить переменную между обработчиком прерывания и // основной программой. const int led = LED_BUILTIN; // вывод со светодиодом void setup(void) { pinMode(led, OUTPUT); Timer1.initialize(150000); Timer1.attachInterrupt(blinkLED); // вызывать blinkLED каждые 0.15 сек. Serial.begin(9600); } // Обработчик прерывания будет мигать светодиодом и // сохранять данные о том, сколько раз мигнул. int ledState = LOW; volatile unsigned long blinkCount = 0; // используйте volatile для общих переменных void blinkLED(void) { if (ledState == LOW) { ledState = HIGH; blinkCount = blinkCount + 1; // увеличить значение при включении светодиода } else { ledState = LOW; } digitalWrite(led, ledState); } // Основная программа будет печатать счетчик миганий // в Arduino Serial Monitor void loop(void) { unsigned long blinkCopy; // хранит копию blinkCount // чтобы прочитать переменную, которая записана в обработчике // прерывания, мы должны временно отключить прерывания, чтобы // быть уверенными, что она не изменится, пока мы считываем ее. // Чтобы минимизировать время, когда прерывания отключены, // просто быстро копируем, а затем используем копию, позволяя // прерываниям продолжать работу. noInterrupts(); blinkCopy = blinkCount; interrupts(); Serial.print("blinkCount = "); Serial.println(blinkCopy); delay(100); }

Проблемы с контекстом прерываний

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

В этом уроке мы поговорим о таймерах.

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

Итак, зачем нам таймер?

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

Решить поставленные задачи помогают именно таймеры. Но таймеры микроконтроллеров AVR не знают что такое секунда, минута, час. Однако они прекрасно знают, что такое такт! Работают они именно благодаря наличию тактирования контроллера. То есть, таймер считает количество тактов контроллера, отмеряя тем самым промежутки времени. Допустим, контроллер работает при тактовой частоте 8МГц, то есть когда таймер досчитает до 8 000 000, пройдет одна секунда, досчитав до 16 000 000, пройдет 2 секунды и так далее.

Однако, тут возникает первое препятствие. Регистры то у нас 8 битные, то есть досчитать мы можем максимум до 255, а взяв 16 битный таймер, мы, досчитаем максимум до 65535. То есть за одну секунду мы должны обнулить таймер огромное количество раз! Конечно, можно заняться этим, если больше заняться нечем. Но ведь просто измерять время, используя мощный микроконтроллер совсем не интересно, хочется сделать нечто большее. Тут нам на помощь приходит предделитель. В общем виде это промежуточное звено между таймером и тактовой частотой контроллера. Предделитель облегчает нашу задачу позволяя поделить тактовую частоту на определенное число, перед подачей её на таймер. То есть установив предделитель на 8, за 1 секунду наш таймер досчитает до 1 000 000, вместо 8 000 000 (Разумеется, при частоте тактирования контроллера 8МГц). Уже интереснее, не так ли? А поделить мы можем и не только на 8, но и на 64 и даже на 1024.

Теперь настало время собрать схему, настроить наш таймер, предделитель, и сделать уже хоть что-то полезное!

А делать мы сегодня будем “бегущие огни” из светодиодов. То есть поочередно будем зажигать 3 светодиода, с периодом 0.75 секунды (То есть время работы одного светодиода 0.25 секунды). Соберем следующую схему:

Номиналы резисторов R 1-R 3 рассчитайте самостоятельно.

Далее, рассмотрим регистры отвечающие за работу таймеров. Всего AtMega 8 имеет в своем составе 3 таймера.Два 8 битных(Timer 0,Timer 2) и один 16 битный(Timer 1).Рассматривать будем на примере 16 битного таймера 1.

Пара регистров 8 битных регистров TCNT 1H и TCNT 1L , вместе образуют 16 битный регистр TCNT 1. Данный регистр открыт как для записи, так и для чтения. При работе таймера 1, значение данного регистра при каждом счете изменяется на единицу. То есть в регистре TCNT 1 записано число тактов, которые сосчитал таймер. Так же мы можем записать сюда любое число в диапазоне от 0 до 2 в 16 степени. В таком случае отсчет тактов будет вестись не с 0, а с записанного нами числа.

Регистр TIMSK отвечает за прерывания, генерируемые при работе таймеров микроконтроллера. Прерывание – обработчик специального сигнала, поступающего при изменении чего либо . Любое прерывания микроконтроллера может быть разрешено или запрещено. При возникновении разрешенного прерывания, ход основной программы прерывается, и происходит обработка данного сигнала. При возникновении запрещенного прерывания, ход программы не прерывается, а прерывание игнорируется. За разрешение прерывания переполнения счетного регистра TCNT 1 таймера 1 отвечает бит TOIE 1(Timer 1 Overflow Interrupt Enable ).При записи 1 в данный бит прерывание разрешено, а при записи 0 – запрещено. Данное прерывание генерируется таймером 1 при достижении максимального значения регистра TCNT 1. Подробнее о прерываниях поговорим в следующем уроке.

Регистр TCCR 1B отвечает за конфигурацию таймера 1. В данном случае битами CS 10-CS 12 мы задаем значение предделителя согласно следующей таблицы.

Остальные биты пока нас не интересуют.

Так же существует регистр TCCR 1A , который позволяет настроить другие режимы работы таймера, например ШИМ, но о них в отдельной статье.

А теперь код на C :

#define F_CPU 16000000UL #include #include uint8_t num=0; ISR(TIMER1_OVF_vect) { PORTD=(1<2) { num=0; } TCNT1=61630;//Начальное значение таймера } int main(void) { DDRD|=(1<

#define F_CPU 16000000UL

#include

#include

uint8_t num = ;

ISR (TIMER1_OVF_vect )

PORTD = (1 << num ) ;

num ++ ;

if (num > 2 )

num = ;

TCNT1 = 61630 ; //Начальное значение таймера

int main (void )

DDRD |= (1 << PD0 ) | (1 << PD1 ) | (1 << PD2 ) ;

TCCR1B |= (1 << CS12 ) | (1 << CS10 ) ; //Предделитель = 1024

TIMSK |= (1 << TOIE1 ) ; //Разрешить прерывание по переполнению таймера 1

TCNT1 = 61630 ; //Начальное значение таймера

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

while (1 )

//Основной цикл программы, он пуст, так как вся работа в прерывании

Код на ASM :

Assembly (x86)

Include "m8def.inc" rjmp start .org OVF1addr rjmp TIM1_OVF start: ldi R16,LOW(RamEnd) out SPL,R16 ldi R16,HIGH(RamEnd) out SPH,R16 ldi R16,1 ldi R17,0b00000111 out DDRD,R17 ldi R17,0b00000101 out TCCR1B,R17 ldi R17,0b11110000 out TCNT1H,R17 ldi R17,0b10111110 out TCNT1l,R17 ldi R17,0b00000100 out TIMSK,R17 sei main_loop: nop rjmp main_loop TIM1_OVF: out PORTD,R16 lsl R16 cpi R16,8 brlo label_1 ldi R16,1 label_1: ldi R17,0b10111110 out TCNT1L,R17 ldi R17,0b11110000 out TCNT1H,R17 reti

Include "m8def.inc"

Rjmp start

Org OVF 1addr

Rjmp TIM 1_ OVF

start :

Ldi R 16, LOW (RamEnd )

Out SPL , R 16

Ldi R 16, HIGH (RamEnd )

Out SPH , R 16

Ldi R 16, 1

Ldi R 17, 0b00000111

Out DDRD , R 17

Ldi R 17, 0b00000101

Out TCCR 1B , R 17

Ldi R 17, 0b11110000

Out TCNT 1H , R 17

Ldi R 17, 0b10111110

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

В данном разделе будет описано на использование программного таймера 2 для периодических прерываний. Исходная идея состояла в использовании этого таймера для генерации частоты биений в звуковых проектах Arduino. Чтобы выводить тон или частоту нам нужно переключать порт ввода-вывода на согласованной частоте. Это можно делать с использованием циклов задержки. Это просто, но означает, что наш процессор будет занят, ничего не выполняя, но ожидая точного времени переключения вывода. С использованием прерывания по таймеру мы можем заняться другими делами, а вывод пусть переключает ISR, когда таймер подаст сигнал, что время пришло.

Необходимо только установить таймер, чтобы подавал сигнал с прерыванием в нужное время. Вместо прокрутки бесполезного цикла для задержки по времени, главная программа может делать что-то другое, например, контролировать датчик движения или управлять электроприводом. Что бы ни требовалось проекту, больше нам не нужно процессорное время для получения задержек. Далее будет описано ISR в общем только то, что касается прерываний таймера 2. Более подробно об использовании прерываний в процессорах AVR можно прочитать в руководстве пользователя avr-libc(англ). На данном этапе не требуется полного понимания, но, в конечном счете, вы можете захотеть получить возможность ускорить использование прерываний, раз это важный инструмент для приложений на микроконтроллерах.

Таймеры на Arduino

Arduino пользуется всеми тремя таймерами ATMega168:

1) Таймер 0 (Системное время, ШИМ 5 and 6)

Используется для хранения счетчика времени работы программы. Функция millis() возвращает число миллисекунд с момента запуск программы, используя ISR глобального приращения таймера 0. Таймер 0 также используется для реализации ШИМ на выводах 5 и 6.

2) Таймер 1 (ШИМ 9 и 10)

Используется для реализации ШИМ для цифровых выводах 9 и 10.

3)Таймер 2 (ШИМ 3 и 11)

Используется для управления выходами ШИМ для цифровых выводов 3 и 11.

Хотя все таймеры используются, только Таймер 0 имеет назначенную таймеру ISR. Это означает, что можно захватить Таймер 1 и/или Таймер2 под свои нужды. Однако в результате нельзя будет использовать ШИМ на некоторых портах ввода-вывода. Если планируется использовать ШИМ, необходимо помнить об этом.

Загрузка микроконтроллера прерываниями

Чтобы дать представление об эффекте, предположим, что таймер ISR запускался бы каждые 20 мкс. Процессор, работающий на 16 МГц, может выполнить около 1 машинной команды каждые 63 мс или около 320 машинных команд для каждого цикла прерывания (20 мкс). Предположим также, что исполнение каждой строки программы на С может занять много машинных команд. Каждая инструкция, используемая в ISR, отнимает время, доступное для исполнения любой другой программы. Если бы ISR использовала около 150 машинных циклов, было бы потрачено половина доступного процессорного времени. При активных прерываниях главная программа откладывалась бы около? времени, занимаемого ей в других случаях. 150 машинных команд - не очень большая программа на С, поэтому необходимо быть внимательны.

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

Измерение загрузки прерываниями

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

Таймер не был установлен в режим, когда он перезагружается автоматически. Это значит, что ISR должна перезагрузить таймер для следующего интервала счета. Было бы точнее иметь автоматически перезагружаемый таймер, но, используя этот режим, можно измерить время, проводимое в ISR, и соответственно исправить время, загружаемое в таймер. Ключ в том, что при помощи этой коррекции при разумной точности, также получаем и число, показывающее, сколько времени проводим в ISR.

Метод заключается в том, что таймер хранит время, даже если он переполнен и прерван. В конце ISR можно захватить текущее значение счетчика таймера. Это значение представляет то время, которое он отнял у разработчика до следующей точки программы. Это суммарное время, затраченное на переход в процедуру прерывания и выполнение программы в ISR. Небольшая ошибка будет оттого, что не подсчитывается время, затраченное на команду перезагрузки таймера, но эту ошибку можно исправить эмпирически. Фактически именно поэтому используется в формуле подсчета загружаемого значения 257 вместо 256. Было обнаружено опытным путем, что это дает лучший результат. Лишний такт компенсирует команду перезагрузки таймера.

Использование прерываний в Arduino

Часто при работе с проектами на микроконтроллерах требуется запускать фоновую функцию через равные промежутки времени. Это часто реализуется установкой аппаратного таймера для выработки прерывания. Это прерывание запускает программу обработки прерываний (Interrupt Service Routine, ISR) для управления периодическим прерыванием. В настоящей статье я описываю установку 8-битного таймера 2 для выработки прерываний на микроконтроллере ATMega168 Arduino. Я пройдусь по этапам, требуемым для установки программы обработки прерываний и внутри нее самой. Arduino подразумевает процессор ATMega168. Этот микроконтроллер имеет несколько систем ввода-вывода, которые доступны каждому пользователю Arduino, поскольку библиотека Arduino облегчает их использование. К примеру, цифровой ввод-вывод, ШИМ, аналого-цифровые входы и последовательный порт. ATMega168 также имеет три внутренних аппаратных таймера. Хотя библиотека Arduino позволяет использовать некоторые свойства таймеров, нельзя напрямую использовать таймер для выработки периодических прерываний.

таймер память цоколевка stepper

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

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

В этом уроке используется:

Аппаратные прерывания

В Arduino имеется 4 вида аппаратных прерываний. Отличаются они сигналом на контакте прерывания.

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

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

В Arduino Uno есть два пина, поддерживающие прерывания. Это цифровые пины 2 (int 0) и 3 (int 1). Один из них мы и будем использовать в нашей схеме.

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


Схема кажется сложной и запутанной, но это не так. Мы подключаем кнопку прерывания к пину Arduino D2, используя аппаратное подавление дребезга контактов. К аналоговому пину A0 мы подключаем инфракрасный дальномер. И к пинам D9, D10 и D11 мы подключаем светодиоды через резисторы на 150 Ом. Мы выбрали именно эти контакты для светодиодов, потому что они могут выдавать ШИМ сигнал.Теперь рассмотрим скетч:

// Назначение прерывания int buttonInt = 0; // Переменные с пинами светодиодов int yellowLed = 11; int redLed = 10; int greenLed = 9; int nullLed = 6; volatile int selectedLed = greenLed; // Инфракрасный дальномер int distPin = 0; void setup () { // Устанавливаем режимы пинов pinMode(redLed, OUTPUT); pinMode(greenLed, OUTPUT); pinMode(yellowLed, OUTPUT); pinMode(nullLed, OUTPUT); // Устанавливаем прерывание attachInterrupt(buttonInt, swap, RISING); } // Обработчик прерывания void swap() { if(selectedLed == greenLed) selectedLed = redLed; else if(selectedLed == redLed) selectedLed = yellowLed; else if(selectedLed == yellowLed) selectedLed = nullLed; else selectedLed = greenLed; } void loop () { // Получаем данные с дальномера int dist = analogRead(distPin); int brightness = map(dist, 0, 1023, 0, 255); // Управляем яркостью analogWrite(selectedLed, brightness); }

// Назначение прерывания

int buttonInt = 0 ;

// Переменные с пинами светодиодов

int yellowLed = 11 ;

int redLed = 10 ;

int greenLed = 9 ;

int nullLed = 6 ;

volatile int selectedLed = greenLed ;

// Инфракрасный дальномер

int distPin = 0 ;

void setup () {

// Устанавливаем режимы пинов

pinMode (redLed , OUTPUT ) ;

pinMode (greenLed , OUTPUT ) ;

pinMode (yellowLed , OUTPUT ) ;

pinMode (nullLed , OUTPUT ) ;

// Устанавливаем прерывание

attachInterrupt (buttonInt , swap , RISING ) ;

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

void swap () {

if (selectedLed == greenLed )

selectedLed = redLed ;

else if (selectedLed == redLed )

selectedLed = yellowLed ;

else if (selectedLed == yellowLed )