Прерывание по таймеру arduino. Прерывания Arduino с помощью attachInterrupt. В этом уроке используется

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

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

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

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

Решить поставленные задачи помогают именно таймеры. Но таймеры микроконтроллеров 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

С учетом всего сказанного напишем программу, переключающую светодиод. В данном случае она будет это делать по событию переполнения таймера‑счетчика Timer 1 (вектор у нас обозначен: TIM1_OVF). Так как счетчик 16‑разрядный, то событие переполнения будет возникать при каждом 65 536‑м импульсе входной частоты. Если мы зададим коэффициент деления тактовой частоты на входе Timer 1 равным 64, то при 4 МГц частоты генератора мы получим примерно 1 Гц: 4 000 000/64/65 536 = 0,953674 Гц.

Это не совсем то, что нам требуется, и к тому же частота неточно равна одному герцу. Для того чтобы светодиод переключался точно раз в полсекунды (т. е. период его был равен секунде), программу придется немного усложнить, загружая каждый раз в счетные регистры определенное значение, которое рассчитывается просто: если период одного тактового импульса таймера равен 16 мкс (частота 4 000 000/64), то для получения 500 000 микросекунд надо отсчитать таких импульсов 31 250. Так как счетчик суммирующий, а прерывание возникает при достижении числа 65 536, то нужно предварительно загружать в него необходимое число 65 536 – 31250 = 34 286.

Это не единственный способ, но наиболее универсальный, годящийся для всех таймеров. Кстати, именно таким способом реализован отсчет времени в Arduino (см. главу 21 ). Иной способ – использовать прерывание по достижению определенного числа, загруженного в регистр сравнения А или В . Как это делается, мы увидим далее в этой главе. Для того чтобы осуществить само переключение из красного в зеленый, нам придется поступить как раньше, т. е. по каждому событию переполнения перебрасывать два бита в регистре PortD .

Полностью программа тогда будет выглядеть так:

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

Заметки на полях

Кстати, а как остановить запущенный таймер, если это потребуется? Очень просто: если обнулить регистр TCCR1B (тот, в котором задается коэффициент деления тактовой частоты), то таймер остановится. Чтобы запустить его опять с коэффициентом 1/64, нужно снова записать в этот регистр значение 0b00000011.

Обратите внимание, что оператор reti (окончание обработки прерывания) при обработке прерывания таймера встречается дважды – это вполне нормальный прием, когда подпрограмма разветвляется. Можно, конечно, и пометить последний оператор reti меткой, и тогда текст процедуры стал бы неотличим от первого варианта, но так будет корректнее.

Обратите также внимание на форму записи ldi temp, (1 << TOIE1) . Поскольку бит, обозначаемый как TOIE1, в регистре TIMSK имеет номер 7, то эта запись эквивалентна записи ldi temp,0b10000000 – можно писать и так, и так, и еще кучей разных способов. Например, для запуска таймера с коэффициентом 1/64 требуется, как видно из текста программы, установить младшие два бита регистра TCCR1B. Здесь мы устанавливаем их в temp напрямую, но поскольку эти биты называются CS11 и CS10, то можно записать так:

ldi temp, (1 << CS11) I (1 << CS10)

или даже так:

ldi temp, (3 << CS10)

Подробно этот способ записи приведен в описании AVR‑ассемблера на сайте Atmel .

Подробности

В этой программе есть один тонкий момент, связанный с загрузкой счетных регистров таймера. При чтении и записи 16‑разрядных регистров Timer 1 их содержимое может измениться в промежутке между чтением или записью отдельных 8‑разрядных «половинок» (ведь, например, в данном случае таймер продолжает считать, пока идет обработка прерывания). Потому в 16‑разрядных таймерах AVR предусмотрен специальный механизм чтения и записи таких регистров. При записи первым загружается значение старшего байта, которое автоматически помещается в некий (недоступный для программиста) буферный регистр. Затем, когда поступает команда на запись младшего байта, оба значения объединяются, и запись производится одновременно в обе «половинки» 16‑разрядного регистра. Наоборот, при чтении первым должен быть прочитан младший байт, при этом значение старшего автоматически фиксируется помещением в тот же буферный регистр, и при следующей операции чтения старшего байта его значение извлекается оттуда. Таким образом и при чтении значения оба байта соответствуют одному и тому же моменту времени.

С Таймером 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

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

В этом уроке мы поговорим о прерываниях. Как понятно из названия, прерывание это событие, которое приостанавливает выполнение текущих задач и передает управление обработчику прерывания. Обработчик прерывания — это функция. Например: если вы написали скетч по управлению мотором или просто плавно зажигаете и гасите светодиод в цикле, то нажатие на кнопку может не обрабатываться, так как 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 )


Top