Урок 42
EXINT или внешние прерывания
Вот наконец-то и настало время нам попробовать поработать с внешними прерываниям. Данный урок, во-первых, был очень востребован, хотя он и кажется на первый взгляд несложным. Очень много было просьб и я не мог не откликнуться. Во-вторых, мы сейчас работаем с модулем LAN ENC28J60, у которого имеется выход, по которому мы можем получить прерывания по окончании определённых операций, которые я и хотел обработать. Также, если у меня получится, я хотел этим поделиться и с вами, но без первоначального представления о внешних прерываниях в контроллере AVR это понять, я считаю, будет, мягко говоря, нелегко. Вот поэтому и созрел данный урок.
Так как я случайно залочил свой контроллер ATMega328, расположенный на недорогой плате, то работать мы будем с платой Arduino UNO.
Нет, не пугайтесь, мы не будем работать со средой программирования Arduino IDE, мы также будем работать с тем же самым Atmel Studio, только Arduino мы будем использовать в качестве отладочной платы и будем его программировать через разъём ISP.
Так что вы можете использовать по-прежнему любые отладочные платы. Но если вдруг кто-то решит использовать так же, как и я, Arduino UNO или любую другую Arduino, то предупреждаю заранее — после этого уже работать с ней Вы не сможете через стандартную среду программирования — Arduino IDE, так как загрузчик мы в случае прошивания через ISP затираем. Но это не смертельно, так как его можно будет впоследствии восстановить. Правда это занятие не из лёгких, но если есть интерес к восстановлению загрузчиков Arduino после их затирания программированием через ISP, то я могу по этому поводу написать статью и дать видеоурок.
Вообщем, пока не приедут платы более мелкие, я буду свои уроки писать с применением Arduino UNO, также и с модулем LAN я также буду работать с той же платой.
Также чем оказался хорош Arduino, что к нему не требуется переходник TTL-USB, так как он там уже прекрасно аппаратно реализован. Причём, мало того, мы через штатный USB-разъём Arduino не только будем пользоваться USART-ом, но и питать наш Arduino будем через него же.
В качестве программатора мы также будем использовать дешёвый USBASP. Так как он у меня с переключателем питания 3,3В/5В, то убрав с него совсем перемычку, мы вообще отключим питание с программатора на плату и будем через ISP исключительно только программировать нашу плату.
Так как на плате Arduino находится 6-контактный разъём ISP, то будем использовать стандартный ISP-переходник
После подключения программатора к плате мы получим вот такую картину
Подключим разъём USB к компьютеру — теперь у нас есть готовый выход USART на ПК, а также и питание
Вернёмся к нашим прерываниям. Внешние прерывания — это такие прерывания, которые обрабатываются вследствие возникновения некоторых событий на определённой ножке порта микроконтроллера. Таких событий существует несколько. Прерывания у контроллера AVR могут срабатывать как по уровню, так и по фронту. По уровню они срабатывают, когда будет замечен определённый логический уровень. То есть, если будет настроено срабатывание по уровню логической единицы, то если данный уровень будет оставаться на ножке внешнего прерывания, то прерывание будет срабатывать циклично, пока не установится другой логический уровень. А по фронту — когда будет замечен переход из одного логического состояния в другое. Фронт, как известно бывает восходящий или нисходящий. Говорят ещё вместо восходящего просто фронт, а вместо нисходящего — спад. Также ещё называют передний и задний фронт. Вообщем — это кому как удобно.
Ножек для отслеживания внешних прерываний у контроллера существует две — INT0 и INT1. Первая ножка совпадает с ножкой порта PD2, а вторая — с PD3
Теперь немного по аппаратной организации внешних прерываний.
Первый регистр — это регистр управления типом обрабатываемого события — EICRA
Пара битов ICS01 и ICS00 управляет типом событий на ножке INT0, а вторая — ICS11 и ICS10 — INT1
Вот зависимость типов событий от включения битов ICS01 и ICS00
А это для ICS11 и ICS10
Соответственно получим мы следующую зависимость:
00 — Низкий уровень на ножке,
01 — Любое изменение на ножке,
10 — нисходящий фронт на ножке,
11 — восходящий фронт на ножке.
Второй регистр — это регистр включения ножки прерываний EIMSK
В данном регистре существует всего два бита, разрешающих прерывания на соответствующих ножках — INT1 и INT0. Биты так и называются, поэтому не перепутаем.
Третий регистр — регистр флагов EIFR, устанавливаемых и сбрасываемых при определённых событиях
Напрямую мы к данному регистру не обращаемся, так как работать будем с функцией-обработчиком.
Теперь вроде что-то прояснилось по вопросу внешних прерываний.
Осталось дело за малым. Создать и прошить проект, который будет как-то наглядно демонстрировать работу внешних прерываний.
Запустим Atmel Studio 7, создадим проект с именем EXTI01, выберем контроллер ATmega328P, добавим в main.c функцию инициализации портов port_ini, в которой включим ножку светодиода на выход. Светодиод на плате располагается на ножке B6
//———————————————
void port_ini(void)
{
//Включим ножку светодиода на выход
DDRB |= 0b00100000;
}
//———————————————
Вызовем данную функцию в main()
//---------------------------------------------
int main(void)
{
port_ini();
while(1)
{
}
}
//---------------------------------------------
Подключим кнопку между ножкой PD2 (D2) и общим проводом
Вернёмся в проект и напишем функцию инициализации внешних прерываний. Настроим нисходящий фронт, так как резистор для исключения неопределённого состояния на ножке мы можем подтянуть программно только к питанию и у нас в обычном состоянии на ножке будет логический 1
//---------------------------------------------
void int_ini(void)
{
//включим прерывания INT0 по нисходящему фронту
EICRA |= (1<<ISC01);
//разрешим внешние прерывания INT0
EIMSK |= (1<<INT0);
}
//---------------------------------------------
Вызовем данную функцию в main(), а также не забываем включить глобальные прерывания
port_ini();
int_ini();
sei();
Давайте ещё на всякий случай настроим данную ножку на вход и подтянем резистор к питанию. В данной ситуации лучше резистор подтянуть, так как может возникнуть неопределённое состояние. Для этого добавим соответствующий код в функцию port_ini
DDRB |= 0b00100000;
//Включим ножку INT0 (PD2) на вход
DDRD &= ~(0b00000100);
//Подтянем резистор на ножке INT0 (PD2) к питанию
PORTD |= 0b00000100;
}
//---------------------------------------------
Также ещё нам необходимо добавить сам обработчик прерывания, в котором мы по возникновению настроенного события на ножке порта будем зажигать наш светодиод
//---------------------------------------------
ISR(INT0_vect)
{
PORTB |= 0b00100000;
}
//---------------------------------------------
Соберём код, прошьём контроллер и попробуем нажать кнопку
Как мы видим, светодиод у нас загорелся.
Теперь давайте попробуем также испытать и другую ножку — INT1.
Для этого сначала настроим данную ножку в port_ini()
//Включим ножки INT0 и INT1 (PD2 и PD3) на вход
DDRD &= ~(0b00001100);
//Подтянем резисторы на ножках INT0 и INT1 (PD2 и PD3) к питанию
PORTD |= 0b00001100;
Затем перенастроим прерывания с учётом данной ножки и в функции int_ini()
//включим прерывания INT0 и INT1 по нисходящему фронту
EICRA |= (1<<ISC11)|(1<<ISC01);
//разрешим внешнее прерывание INT0 и INT1
EIMSK |= (1<<INT1)|(1<<INT0);
А вот обработчик у данной ножки свой отдельный, поэтому добавим его и светодиод в нём будем гасить
//---------------------------------------------
ISR(INT1_vect)
{
PORTB &= ~(0b00100000);
}
//---------------------------------------------
Теперь мы можем подключить ещё кнопку на ножку PD3 (D3), вторым проводом также подключив её к общему проводу, собрать код, прошить контроллер и понажимать кнопки и отследить результаты изменения уровней по светодиоду
Теперь, если мы соберём код и прошьём контроллер, то первая кнопка будет у нас светодиод зажигать, а вторая тушить.
Казалось бы, что тут такого? А то, что происходит это уже независимо от хода самой программы, не в бесконечном цикле, когда во время там написанного обработчика по нажатию кнопки мы можем вообще не находиться и будет уже не ясно, обработается ли наше событие. Поэтому, изучив внешние прерывания, мы сделали большой шаг вперёд к независимости обработки тех или иных событий на ножках портов микроконтроллера. Конечно, учитывая наш уже теперь очень богатый опыт, нам было это проделать не очень тяжело.
Предыдущий урок Программирование МК AVR Следующий урок
Приобрести плату Arduino UNO R3 можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Смотреть ВИДЕОУРОК (нажмите на картинку)
Не переназначаются ножки прерываний INT0, INT1. Они привязаны с своим пинам
Здравствуйте! Вы можете написать статью про восстановление оригинального загрузчика Arduino?
Знаю, что можно восстанавливать прошивку если на руках имеется еще одна плата со штатной прошивкой. Можно ли восстановить прошивку имея только программатор?
Здравствуйте. Можно. Как-нибудь обязательно расскажу, как это делается.