Здравствуйте, на днях решил поэкспериментировать с внешними прерываниями на attiny2313A. Думаю тем кто занимался программированием микроконтроллеров известно, что МК не всегда быстро может реагировать на нажатие кнопки, т.к. проверка PINа стандартно осуществляется в бесконечном цикле и если программа достаточно большая — это может затормозить опрос ножки.
Код ниже написан для WinAVR.

Во втором способе, обычный опрос в цикле будет выглядеть:
если инвертировать, то получиться и для первого способа, но не думаю что это кому-то надо.
В даташите на Мк attiny2313A есть прерывания INT0 (нога PD2), INT1(нога PD3), и PCINT0..7 (весь порт B и хотя ножек у него 8, но прерывание одно на всех, что лично меня не радует )
Прерывания INT0 и INT1 их приоритет выше других прерываний.
Итого мы можем настроить всего три кнопки без использования стандартного опроса.
У меня была мысль, что т.к. я задействую внешние прерывания то регистры DDR и PORT ничего не изменят в поведение МК, но это не так… выход так же надо настраивать как при стандартной обработке.
Сразу оговорюсь, что я пишу про ножки, настроенные на подтягивающие резисторы т.е. DDRxy&=~(1<<y); PORTxy|=(1<<y). иначе смысла нет, срабатывание от прикосновения пальца, как мне кажется, никого не интересует.
Итак даташит страница 58 External Interrupts
MCU Control Register– MCUCR:
Если стоит по дефолту, The low level of INT0 generates an interrupt request, то нажав кнопку мы получим срабатывание вектора, если ее не отпускать, то программа снова и снова будет уходить на прерывание.
Если Any logical change on INT1 generates an interrupt request, то нажав кнопку и не отпуская ее, функция прерывания сработает один раз и дальше программа пойдет по стандартной схеме, но когда мы подождав отпустим кнопку — это опять же будет изменение логики, то вектор опять запуститься.
The falling edge of INT1 generates an interrupt request — тоже самое что и дефолт, во всяком случае по экспериментам, только работает мене стабильно, пока отжимаешь кнопку может сработать.
The rising edge of INT1 generates an interrupt request — кнопка работает только когда ее отжимаешь.
General Interrupt Mask Register – GIMSK:
Глобально разрешает нужные нам прерывания.
External Interrupt Flag Register – EIFR:
Регистр отвечающий за использование прерывания, если логика на ножке изменилась, то в регистре появляется запись, и вектор прерывания начинает обрабатываться.
Pin Change Mask Register – PCMSK:
Разрешает прерывание на той или иной ноге Порта B
Это не настраиваемое внешнее прерывание, в отличие от других и всегда работает по принципу Any logical change что несколько затруднит его использование, хотя и ему применение найдется в разумных руках.
И еще беда одна — наблюдается ложное срабатывание при подключении МК к питанию, уходит один раз на прерывание и больше не сбоит, работая в штатном режиме.
Дополнение от Ocelot( в комментариях):
Нет большой проблемы в том, что прерывание срабатывает по любому изменению уровня (any logical change). В обработчике прерывания всегда можно проверить состояние порта, и определить нужное нам событие. То же самое касается прерывания PCINT, которое одно на все 8 ног. Всегда можно легко узнать, какой именно вход вызвал прерывание.
Attiny2313 PORTD4 — на нем стоит анодом(плюсом) светодиод и моргает когда кнопка на POTRD2 замыкается на землю.
Чтобы использовать другой МК смотрите соответствующий Даташит.
Самому мне помогла статья Dmitry.
UPD 2019: Поправил правописание и опечатки… Убрал недоступные картинки и ссылки на закрывшиеся сайты.
Код ниже написан для WinAVR.

Стандартный опрос:
- DDRxy&=~(1<<y); PORTxy&=~(1<<y);
с одной стороны кнопка подключена к плюсу (VCC), а с другой стороны к ножке.
В таком случае провод который подключен к ножке выступает в роли антенны и любое возмущение электромагнитного поля вокруг проводка вызывает срабатывание кнопки, что неприемлемо.
- DDRxy&=~(1<<y); PORTxy|=(1<<y);
С одной стороны кнопка подключена к минусу (GND), а с другой стороны к ножке.
Это наиболее приемлемый вариант, наводок не возникает и кнопка срабатывает стабильно.
Во втором способе, обычный опрос в цикле будет выглядеть:
if (!(PINB & (1<<PINB6))){ // если нажата кнопка на ноге PORTB6, то выполнить:
моргнуть;
}
if (PINB & (1<<PINB7)){ // если не нажата кнопка на ноге PORTB7, то выполнить:
моргнуть;
}
если инвертировать, то получиться и для первого способа, но не думаю что это кому-то надо.
Итак сами внешние прерывания
В даташите на Мк attiny2313A есть прерывания INT0 (нога PD2), INT1(нога PD3), и PCINT0..7 (весь порт B и хотя ножек у него 8, но прерывание одно на всех, что лично меня не радует )
Прерывания INT0 и INT1 их приоритет выше других прерываний.
Итого мы можем настроить всего три кнопки без использования стандартного опроса.
У меня была мысль, что т.к. я задействую внешние прерывания то регистры DDR и PORT ничего не изменят в поведение МК, но это не так… выход так же надо настраивать как при стандартной обработке.
Сразу оговорюсь, что я пишу про ножки, настроенные на подтягивающие резисторы т.е. DDRxy&=~(1<<y); PORTxy|=(1<<y). иначе смысла нет, срабатывание от прикосновения пальца, как мне кажется, никого не интересует.
Регистры управления
Итак даташит страница 58 External Interrupts
MCU Control Register– MCUCR:
Если стоит по дефолту, The low level of INT0 generates an interrupt request, то нажав кнопку мы получим срабатывание вектора, если ее не отпускать, то программа снова и снова будет уходить на прерывание.
Если Any logical change on INT1 generates an interrupt request, то нажав кнопку и не отпуская ее, функция прерывания сработает один раз и дальше программа пойдет по стандартной схеме, но когда мы подождав отпустим кнопку — это опять же будет изменение логики, то вектор опять запуститься.
The falling edge of INT1 generates an interrupt request — тоже самое что и дефолт, во всяком случае по экспериментам, только работает мене стабильно, пока отжимаешь кнопку может сработать.
The rising edge of INT1 generates an interrupt request — кнопка работает только когда ее отжимаешь.
General Interrupt Mask Register – GIMSK:
Глобально разрешает нужные нам прерывания.
External Interrupt Flag Register – EIFR:
Регистр отвечающий за использование прерывания, если логика на ножке изменилась, то в регистре появляется запись, и вектор прерывания начинает обрабатываться.
Pin Change Mask Register – PCMSK:
Разрешает прерывание на той или иной ноге Порта B
Отдельно про PCINT
Это не настраиваемое внешнее прерывание, в отличие от других и всегда работает по принципу Any logical change что несколько затруднит его использование, хотя и ему применение найдется в разумных руках.
И еще беда одна — наблюдается ложное срабатывание при подключении МК к питанию, уходит один раз на прерывание и больше не сбоит, работая в штатном режиме.
Дополнение от Ocelot( в комментариях):
Нет большой проблемы в том, что прерывание срабатывает по любому изменению уровня (any logical change). В обработчике прерывания всегда можно проверить состояние порта, и определить нужное нам событие. То же самое касается прерывания PCINT, которое одно на все 8 ног. Всегда можно легко узнать, какой именно вход вызвал прерывание.
Вектора прерываний затрагивающие нашу тему
void preriv() //функция инициализации прерываний
{
GIMSK=(1<<PCIE)|(1<<INT0);
PCMSK=(1<<PCINT0);
MCUCR=(0<<ISC00)|(0<<ISC01);
}
preriv();// вызов функции инициализации в теле цикла
ISR(INT0_vect){ // прерывание по вектору INT0
PORTD|=(1<<PORTD4);
_delay_ms(1000);
}
ISR(PCINT_vect){ // прерывание по вектору PCINT
PORTD|=(1<<PORTD4);
_delay_ms(1000);
}
Attiny2313 PORTD4 — на нем стоит анодом(плюсом) светодиод и моргает когда кнопка на POTRD2 замыкается на землю.
Чтобы использовать другой МК смотрите соответствующий Даташит.
Самому мне помогла статья Dmitry.
UPD 2019: Поправил правописание и опечатки… Убрал недоступные картинки и ссылки на закрывшиеся сайты.