чем отличаются прерывания int0 по фронту и по уровню
AVR: Обработка внешних прерываний
Для чего нужны внешние прерывания
Прерывание — это событие по которому прерывается исполнение основного кода программы ( например функции main) и управление передаётся функции обработчику прерывания. Соответственно внешние прерывания — это некие внешние события прерывающие исполнение основного кода программы.
Внешние прерывания позволяют получить быструю, гарантированную реакцию на внешние события. По этому наиболее частое применение внешних прерываний это реализация счетчиков импульсов, измерение частоты или длительности импульсов, программная реализация uart, one-wire, i2с, spi, а так-же обработка сигналов от внешних периферийных устройств.
Принцип работы внешних прерываний в AVR
Для того что бы микроконтроллер узнал о внешних событиях используются дискретные входы INT0 INT1 и т.д. Дискретные означает что они работают с логическими уровнями: 0 и 1.
0 — это отсутствие напряжения на входе
1 — наличие на входе напряжения, которое равно напряжению питания микроконтроллера.
Внешние прерывания можно разделить на два типа:
Внешние прерывания по уровню
Срабатывание внешнего прерывания может быть настроено на низкий или высокий логический уровень. Например, если прерывание настроено на низкий логический уровень, то оно возникает когда на входе INT напряжение равно нулю. Если же прерывание настроено на высокий уровень, то оно возникает когда на входе логическая 1.
При работе с прерываниями по уровню надо помнить, что пока на входе INT соответствующий уровень, прерывание будет возникать постоянно. Т.е. если возникло прерывание, например по низкому уровню и программа его обработала, но если при выходе из обработчика прерывания на входе остается низкий уровень, то прерывание сработает еще раз, и опять будет вызван обработчик прерывания, и так будет продолжаться до тех пор пока на входе не появится высокий уровень. Что бы этого не происходило нужно в обработчике запрещать данный вид прерываний, или перенастраивать его на другой уровень.
Внешние прерывание по фронту
Прерывание по переднему фронту или, как иногда говорят, нарастанию сигнала, возникает когда происходит изменение уровня сигнала на входе INT с 0 на 1. Прерывание по заднему фронту ( спаду сигнала ), возникает при изменении уровня сигнала на входе INT с 1 на 0.
Так же возможно настроить прерывание что бы оно реагировало на любое изменение на входе INT т.е. оно будет возникать и по переднему и по заднему фронту.
Настройка внешних прерываний в AVR
Внешние прерывания в avr atmega8 настраиваются при помощи бит ISCxx регистра MCUCR.
Зависимость условия срабатывания внешнего прерывания INT0 от бит ISC0x регистра MCUCR в avr atmega8
ISC01 | ISC00 | Условие срабатывания |
---|---|---|
0 | 0 | Низкий уровень на INT0 |
0 | 1 | Любое изменение на INT0 |
1 | 0 | Задний фронт на INT0 |
1 | 1 | Передний фронт на INT0 |
Для внешнего прерывания INT1, настройка производиться так же, только используются биты ISC11 ISC10.
Пример настройки внешнего прерывания для avr atmega8:
//сбрасываем все биты ISCxx MCUCR &=
Бит INT0 отвечает за разрешение/запрещение внешнего прерывания INT0, а бит INT1, соответственно за внешне прерывание INT1.
Так же необходимо что бы был выставлен флаг глобального разрешения прерываний.
Пример кода разрешающего внешнее прерывание INT0 для avr atmega8:
AVR Урок 42. EXINT или внешние прерывания
Урок 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(), а также не забываем включить глобальные прерывания
int_ini ();
sei ();
Давайте ещё на всякий случай настроим данную ножку на вход и подтянем резистор к питанию. В данной ситуации лучше резистор подтянуть, так как может возникнуть неопределённое состояние. Для этого добавим соответствующий код в функцию port_ini
//Включим ножку INT0 (PD2) на вход
//Подтянем резистор на ножке INT0 (PD2) к питанию
PORTD |= 0b00000100;
Также ещё нам необходимо добавить сам обработчик прерывания, в котором мы по возникновению настроенного события на ножке порта будем зажигать наш светодиод
ISR ( INT0_vect )
PORTB |= 0b00100000;
Соберём код, прошьём контроллер и попробуем нажать кнопку
Как мы видим, светодиод у нас загорелся.
Теперь давайте попробуем также испытать и другую ножку – INT1.
Для этого сначала настроим данную ножку в port_ini()
//Включим ножки INT0 и INT1 (PD2 и PD3) на вход
(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 )
Теперь мы можем подключить ещё кнопку на ножку PD3 (D3), воторым проводм также подключив её к общему проводу, собрать код, прошить контроллер и понажимать кнопки и отследить результаты изменения уровней по светодиоду
Теперь, если мы соберём код и прошьём контроллер, то первая кнопка будет у нас светодиод зажигать, а вторая тушить.
Казалось бы, что тут такого? А то, что происходит это уже независимо от хода самой программы, не в бесконечном цикле, когда во время там написанного обработчика по нажатию кнопки мы можем вообще не находиться и будет уже не ясно, обработается ли наше событие. Поэтому, изучив внешние прерывания, мы сделали большой шаг вперёд к независимости обработки тех или иных событий на ножках портов микроконтроллера. Конечно, учитывая наш уже теперь очень богатый опыт, нам было это проделать не очень тяжело.
Приобрести плату Arduino UNO R3 можно здесь.
Смотреть ВИДЕОУРОК (нажмите на картинку)
Програмирование в AVR Studio 5 с самого начала. Часть 7
Поговорим о прерываниях. Слово прерывание говорит само за себя, происходит остановка какого — то процесса на какое — то время, для того чтобы выполнить дополнительные действия. Прерывания могут быть внешними или внутренними. Приведу простой пример, услышанный из уст моего друга…
Собрался он помыть посуду на кухне, взялся с азартом, засучив рукава…но посуда оказалась жирной и он был вынужден прерваться, чтобы найти на одной из полок кухонного гарнитура средство для мытья жирной посуды, после чего снова продолжил свое занятие. Но в какой-то момент зазвонил телефон, и он опять прервался от своей работы, поднял трубку, звонила теща и сказала, что придет в гости, значит надо сходить в магазин купить продукты к ее приходу. Сходил в магазин и только после этого домыл посуду.
Чем же так привлекательны прерывания?
Первое — это то, что мы можем остановить основной процесс для выполнения каких либо других функции, с последующим продолжением этого процесса.
Вторым, и наверное во многих случаях основным считается ускорение процесса выполнения всех функций, за счет внутренних дополнительных устройств. Вернемся к нашему примеру. Допустим, мой друг взялся мыть посуду, когда его жена уже пришла домой. Увидев жирную посуду, он просит ее найти средство для мытья посуды, и пока он моет, она уже принесет ему это средство. Но, вот зазвонил телефон, трубку поднимет жена, поговорит с мамой и сходит в магазин. Совместно все дела сделаны очень быстро!
А еще проще зациклится – т.е. основной программы нет.
Мой друг сидит на диване и ничего не делает, домоработница увидев грязную посуду, говорит ему об этом, и получив разрешение, начинает мыть сама. Когда звонит телефон, он говорит жене, чтобы она подняла трубку, жена разговаривает по телефону, и поле разговора идет в магазин за продуктами… Красота! В таком случае в микроконтроллере одновременно работают несколько устройств ввода-вывода (в современных микроконтроллерах их может быть достаточно много) и общая производительность процессора возрастает во много раз, но прерывания от устройств обрабатываются последовательно одно за другим (не одновременно), в зависимости от приоритета (в нашем примере жена имеет больший приоритет, нежели домоработница).
За управление прерываниями отвечают несколько регистров
SREG –регистр статуса (состояния). Смотрим таблицу устройств ввода-вывода. Седьмой бит регистра SREG –флаг I (interrupt), который называется флагом глобального разрешения прерываний. Если флаг опущен (седьмой бит равен нулю), то все прерывания запрещены. Если флаг поднять (установить I в 1), мы разрешим прерывания.
Устанавливается и сбрасывается флаг I командами:
SEI — разрешить прерывания
CLI — запретить прерывания
Какие из прерываний будут работать, задается с помощью регистров называемых – масками прерываний.
Обозначаются маски прерываний следующим образом:
TIMSK. – управление прерываниями от таймеров и других встроенных устройств.
GIMSK (GIKR в семействе Mega) — управление всеми внешними прерываниями.
Маски прерываний в свою очередь зависят от флагов прерываний:
TIFR и GIFR соответственно (не путайте с флагом глобального разрешения прерываний).
Последовательность выполнения прерываний:
При включении микроконтроллера все флаги прерываний сброшены в 0. Для включения прерываний программа должна установить флаг I регистра SREG в 1. После этого прописать регистры маски с установленными локальными прерываниями (прерывания, которые нам нужны).
Когда приходит (сигнал) запрос на прерывание, то он поднимает флаг прерывания (даже в том случае если прерывание запрещено, для организации вложенных прерываний и приоритета между разными прерываниями). Если нет запрета прерываний, то контроллер обратится к соответствующему (Interrupt Vectors) — вектору прерываний, приостанавливая текущую программу.
Вектор прерывания – это фиксированная строка программной области, куда переходит программа в случае возникновения прерывания.
Весь список векторов прерывания – называется таблицей векторов прерывания, который располагается в начале программного кода.
Итак, в момент обращения к вектору прерывания, флаг I регистра SREG и флаг вызвавший прерывание сбрасывается в 0, запрещая другие прерывания. Если в процессе выполнения прерывания, возникли другие запросы прерываний, флаги этих прерываний остаются поднятыми. По окончании выполнения текущего прерывания флаг I регистра SREG поднимается, разрешая выполнение следующего. Если пришли несколько запросов, и их флаги окажутся поднятыми то первым будет выполнено прерывание, чей вектор меньше по адресу в таблице, ближе к началу памяти. За ним второй, и так далее. Кроме этого программист может организовать так называемое вложенное прерывание, когда в процессе выполнения программы прерывания возникает еще одно прерывание. Тогда прекращается выполнение текущего прерывания и выполняется новое, после завершения которого, возобновляется выполнение остановленного прерывания.
В качестве примера приведена таблица векторов прерывания для ATtiny2313
Таблица векторов прерывания для Атмега16 выглядит следующим образом:
При сравнении, таблицы совершенно не совпадают.
В семействе ATtiny строка вектора прерывания занимает 16 бит, а в семействе Mega занимают 32 бита (обратите внимание на адреса векторов прерывания, напомню, что адресная строка в программной области представлена 16 битным словом).
Программный код для ATtiny2313 может выглядеть следующим образом:
Как видно, вектор прерывания создает относительный переход на метки программ прерываний. Ниже в таблице показаны варианты; 1. Когда нет прерываний; 2, 3. с внешним прерыванием по входу INT_1.
Если метки «пустые” (под меткой нет программы), то ничего не происходит, и программа последовательно «пробежавшись” по оставшимся меткам благополучно доходит до команды RETI- Interrupt return — выход из обработчика прерывания как показано в первом столбце таблицы.
Чтобы выполнить программу прерывания, например по входу INT_1, нужно метку INT_1: вынести из списка. Это схематично показано во втором столбце таблицы.
Но, программисту неудобно каждый раз прописывать все прерывания и отдельно метки к ним, особенно в последних моделях, где таблица достаточно большая, проще в строке вектора прерывания сразу написать команду RETI, если прерывание не используется. Тогда программа будет выглядеть, как показано в третьем столбце таблицы.
В AVR-контроллерах в зависимости от модели может быть от 1 до 8 входов внешних прерываний.
Рассмотрим систему управления внешними прерываниями. Для этого предусмотрены следующие комбинации I/O-регистров в зависимости от модели (см. соответствующий DataSheet):
— GIMSK, EIFR, PCMSK, MCUCR;
— GIKR, GIFR, MCUCR;
— EIMSK, EICR, EIFR;
GIMSK, GIKR, EIMSK — маски прерываний,
EIFR, PCMSK, GIFR, EIFR – флаги прерываний
Для разрешения или запрещения внешних прерываний предназначены управляющие регистры: GIMSK-(General Interrupt Mask Register)(Tiny), GICR- (General Interrupt Control Register)(Mega), MCUCR – (MCU Control Register)
EIFR- External Interrupt Flag Register: 1- разрешено, 0 – запрещено. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.
Биты управления регистра GIMSK:
Бит 7 – INT1: External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов EIFR. Вывод INT1 синхронизирован с тактовым генератором.
Бит 6 – INT0: External Interrupt Request 0 Enable — бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов EIFR. Вывод INT10 синхронизирован с тактовым генератором.
Бит 5 – PCIE: Pin Change Interrupt Enable – бит разрешения прерывания на выводах PCINT0…7: 1- разрешено, 0 – запрещено. Любое изменение на любом из выводов PCINT0…7 будет формировать прерывание. Выводы PCINT0…7 настраиваются на прерывание индивидуально, битами в регистре флагов PCMSK.
PCMSK — Pin Change Mask Regiser — регистр флагов PCMSK: 1- разрешено, 0 – запрещено. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний. Выводы PCINT0…7 не синхронизированы с тактовым генератором, т.е. прерывание наступает по факту изменения на любом из выводов.
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.
Биты управления регистра GICR:
Бит 7 – : External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов GIFR
Бит 6 – INT0: External Interrupt Request 0 Enable — бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов GIFR
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.
Биты управления регистра GICR:
Бит 7 – : External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов GIFR
Бит 6 – INT0: External Interrupt Request 0 Enable — бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов GIFR
Бит 5 – INT2: External Interrupt Request 2 Enable — бит разрешения прерывания INT2: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT2 настроен как выход. Бит INT2 настраиваются на прерывание в регистре флагов GIFR
Функциями входов INT0 и INT1во всех контроллерах управляют младшие биты регистра MCUCR
MCUCR– MCU Control Register
Биты управления:
Биты 1, 0 – ISC01, ISC00 (Interrupt Sense Control 0 Bit 1 and Bit 0) – состояние данных битов определяет событие на выводе INT0, при котором формируется прерывание INT0:
ISC01=0, ISC00=0 – уровень логического нуля;
ISC01=0, ISC00=1 – любая смена логического состояния;
ISC01=1, ISC00=0 – по спадающему фронту;
ISC01=1, ISC00=1 – по нарастающему фронту.
Биты 3, 2 – ISC11, ISC10 (Interrupt Sense Control 1 Bit 1 and Bit 0) – состояние данных битов определяет уровень сигнала на выводе INT1, по которому формируется прерывание INT1:
ISC11=0, ISC10=0 – уровень логического нуля;
ISC11=0, ISC10=1 – любая смена логического состояния;
ISC11=1, ISC10=0 – по спадающему фронту;
ISC11=1, ISC10=1 – по нарастающему фронту.
Ну вот, вроде как с минимумом о внешних прерываниях поговорили.
Понятно, что для того, чтобы прерывания работали, нужно соответственно их прописывать.
Допишем начатую для tiny, инициализацию прерывания на INT1 по возрастающему фронту сигнала:
Кстати на tiny2313 можно сформировать прерывание на любых выводах PCINT0…7, на Mega до 48 серии эти возможности отсутствуют…
Есть такие операции, при выполнении которых, возникшие прерывания могут вызвать сбой программы. В таких случаях перед началом выполнения операции пишем CLI, а после SEI. Называются такие операции – атомарными.
Желательно, чтобы программы прерываний были компактными и выполнялись с максимальной скоростью, потому, что целью любых прерываний является фиксация события. Если по разным причинам программа выполняется медленно, то достаточно зафиксировать событие и обработать его чуть позже.
Чтобы не загромождать лишней информацией изложенный материал, рекомендую читателям пользоваться даташитами, а если не все понятно, то чаще задавать вопросы на форумах.
Дальше, подробно рассмотрим внутренние прерывания на основе встроенных таймеров.
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.