- Для чего нужны внешние прерывания
- Принцип работы внешних прерываний в AVR
- Настройка внешних прерываний в AVR
- Пример использования внешних прерываний в 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 &= ~( (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00) ) //настраиваем на срабатывание INT0 по переднему фронту MCUCR |= (1<<ISC01)|(1<<ISC00); |
Разрешение внешних прерываний в avr atmega
Для того что бы внешние прерывания заработали их надо разрешить, установив в 1 соответствующие биты в регистре GICR.
Бит INT0 отвечает за разрешение/запрещение внешнего прерывания INT0, а бит INT1, соответственно за внешне прерывание INT1.
Так же необходимо что бы был выставлен флаг глобального разрешения прерываний.
Пример кода разрешающего внешнее прерывание INT0 для avr atmega8:
//разрешаем внешнее прерывание INT0 GICR |= (1<<INT0); //выставляем флаг глобального разрешения прерываний sei(); |
Пример использования внешних прерываний в AVR atmega
В качестве примера приведу программу счетчика импульсов. Программа подсчитывает количество импульсов на входе INT0, и раз в секунду выводит результат подсчета в uart.
#include <stdio.h> #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> //переменная счетчик volatile unsigned long int0_cnt = 0; //настройка внешнего прерывния INT0 void int0_init( void ) { //настраиваем на срабатывание INT0 по переднему фронту MCUCR |= (1<<ISC01)|(1<<ISC00); //разрешаем внешнее прерывание INT0 GICR |= (1<<INT0); } //функция обработчик внешнего прерывания INT0 ISR( INT0_vect ) { int0_cnt++; } //настройка UART void uart_init( void ) { //настройка скорости обмена UBRRH = 0; UBRRL = 3; //115200 при кварце 7.3728 МГц //8 бит данных, 1 стоп бит, без контроля четности UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 ); //разрешить прием и передачу данных UCSRB = ( 1 << TXEN ) | ( 1 <<RXEN ); } //передача байта по UART int uart_putc( char c, FILE *file ) { //ждем окончания передачи предыдущего байта while( ( UCSRA & ( 1 << UDRE ) ) == 0 ); UDR = c; return 0; } FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE ); int main( ) { //временная переменная unsigned long tmp; stdout = &uart_stream; int0_init(); uart_init(); sei(); while(1) { //на время копирования значения счетчика запрещаем прерывания cli(); tmp = int0_cnt; //разрешаем прерывания sei(); printf( "int0_cnt = %lu\r\n", tmp ); //пауза 1 секунда _delay_ms( 1000 ); } return 0; } |
Скачать исходники примера работы с внешними прерываниями в avr atmega8 для avr-gcc ( winavr ) можно тут
Спасибо большое за материал, но он бесполезен, если ты ищешь пример кода для Ассемблера.
Я бы не сказал, что он бесполезен. Взяв за основу код на Си, можно написать свой на ассемблере. Или скомпилировать сишный код, и дизассемблировать его :)
Здравствуйте Артём.
Я новичок. Пишу на atmel studio 6 под atmega168pa.
При инициализации внешнего прерывания у меня возникли некоторые трудности.
GICR не определяется. Полез в datasheet нет такого регистра нашел EIMSK
правильной ли будет следующая запись:
EIMSK|=(1<<INT0);
MCUCR|=(1<<ISC01)|(0<<ISC00);
Правильной, на мой взгляд, будет вот такая запись:
EIMSK = ( 1 << INT0 );
EICRA = (1<<ISC01) | (0<<ISC00);
Причём (0<<ISC00), можно не писать, т.к. это выражение равно нулю.
Здравствуйте Артем. Очень полезный материал! Спасибо за урок по прерываниям. Я сейчас пытаюсь приспособить датчик расстояния HC-SR04 своей работе. Очень нужна программа на ассемблере. Если это Вас не затруднит буду очень признателен, ибо в си работал только в DOCе.
День добрый!
А давайте вместе напишем эту программу на ассемблере. Или могу Вам помочь разобраться с Си под AVR, особой разницы с Си под DOS нет.
P.S. Пожалуйста, укажите свой email когда будете оставлять комментарий (его кроме меня никто не увидит), некоторые вопросы по электронной почте быстрее решаются, чем через блог :)
можно ли помочь мне накидать блок схему работы микроконтроллера atmega328 по следующему коду:
Заранее спасибо!
Андерй, пришлите свой вариант, если найду в нём ошибки, сообщу Вам.
Здравствуйте Артем!А не поможете разобрать с программой генератора импульсов,есть исходник написанный в CVAVR,все работает в железе,но есть одно но,периодически пропадает сигнал на несколько секунд,точнее когда вращаешь энкодер меняя частоту.
Здравствуйте. Моя схема почему-то не реагирует на прерывание. Можно с вами пообщаться по почте? Я бы выслал все свои наработки.
Здравствуй) мог бы ты мне помочь сам, ну или статью какую дал бы? До меня не как не доходит как настраивать и работать с таймерами и шим! Много чего прочел, но так и не доходит
Здравствуйте. Хотел узнать, почему в обычном режиме (прерывание при низком уровне на входе INTx) прерывания вызываются шустро в любом режиме и в режимах SLEEP (как и должно быть), а в режиме прерывание «по фронту», «по спаду» или «по изменению уровня» начинает тормозить, а в спящем режиме вообще не реагирует?
Здравствуйте.А каким терминалом вы пользуетесь?
При работе с контроллером?
Доброго времени суток.
Под линуксом использую minicom. Под виндой юзал «Terminal 1.9b».
А возможен такой вариант, кнопки INT0 и INT1 одновременно замыкаются и далее на индикатор выводится заданный параметр и если возможно то как записать условие?