Поговорим о прерываниях. Слово прерывание говорит само за себя, происходит остановка какого - то процесса на какое - то время, для того чтобы выполнить дополнительные действия. Прерывания могут быть внешними или внутренними. Приведу простой пример, услышанный из уст моего друга… Собрался он помыть посуду на кухне, взялся с азартом, засучив рукава…но посуда оказалась жирной и он был вынужден прерваться, чтобы найти на одной из полок кухонного гарнитура средство для мытья жирной посуды, после чего снова продолжил свое занятие. Но в какой-то момент зазвонил телефон, и он опять прервался от своей работы, поднял трубку, звонила теща и сказала, что придет в гости, значит надо сходить в магазин купить продукты к ее приходу. Сходил в магазин и только после этого домыл посуду.
На этом примере видно два вида прерываний, первое – связано с выполнением основной работы - поиск средства для жирной посуды -внутреннее прерывание, второе – телефонный звонок – внешнее прерывание.
В микроконтроллере внешние прерывания возникают за счет сигналов поступающих от других источников, внутренние – за счет устройств встроенных в сам микроконтроллер.
Чем же так привлекательны прерывания?
Первое - это то, что мы можем остановить основной процесс для выполнения каких либо других функции, с последующим продолжением этого процесса.
Вторым, и наверное во многих случаях основным считается ускорение процесса выполнения всех функций, за счет внутренних дополнительных устройств. Вернемся к нашему примеру. Допустим, мой друг взялся мыть посуду, когда его жена уже пришла домой. Увидев жирную посуду, он просит ее найти средство для мытья посуды, и пока он моет, она уже принесет ему это средство. Но, вот зазвонил телефон, трубку поднимет жена, поговорит с мамой и сходит в магазин. Совместно все дела сделаны очень быстро!
А еще проще зациклится – т.е. основной программы нет.
Мой друг сидит на диване и ничего не делает, домоработница увидев грязную посуду, говорит ему об этом, и получив разрешение, начинает мыть сама. Когда звонит телефон, он говорит жене, чтобы она подняла трубку, жена разговаривает по телефону, и поле разговора идет в магазин за продуктами… Красота! В таком случае в микроконтроллере одновременно работают несколько устройств ввода-вывода (в современных микроконтроллерах их может быть достаточно много) и общая производительность процессора возрастает во много раз, но прерывания от устройств обрабатываются последовательно одно за другим (не одновременно), в зависимости от приоритета (в нашем примере жена имеет больший приоритет, нежели домоработница).
В микроконтроллерах AVR большим приоритетом пользуется то прерывание, которое прописано ближе к нулевой строке программы в таблице векторов прерываний (см. дальше)
За управление прерываниями отвечают несколько регистров
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 может выглядеть следующим образом:
.cseg
.org 0
rjmp Reset
rjmp INT_0
rjmp INT_1
rjmp Timer1_capt1
rjmp Timer1_comp1
rjmp Timer1_OVF1
rjmp Timer0_OVF0
rjmp UART_RX
rjmp UART_UDRE
rjmp UART_TX
rjmp ANA_COMP
rjmp PCINT
rjmp Timer1_compB
rjmp Timer0_compA
rjmp Timer0_compB
rjmp USI_START
rjmp USI_OVERFLOW
rjmp EE_READY
rjmp WDT_ OVERFLOW
Как видно, вектор прерывания создает относительный переход на метки программ прерываний. Ниже в таблице показаны варианты; 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)
Tiny
и соответствующий ему регистр флагов
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 не синхронизированы с тактовым генератором, т.е. прерывание наступает по факту изменения на любом из выводов.
Mega8

и соответствующий ему регистр флагов
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.
Биты управления регистра GICR:
Бит 7 - INT1: External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов GIFR
Бит 6 – INT0: External Interrupt Request 0 Enable - бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено.. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов GIFR
Mega16
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.
Биты управления регистра GICR:
Бит 7 – INT1: 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 по возрастающему фронту сигнала:
ldi r16,0x80 ; запишем в r16 число 0b10000000
ldi r17,0x0C ; запишем в r17 число 0b00001100
out MCUCR,r17 ; прерывание сформируется по нарастающему фронту ISC11=1, ISC10=1
out GMSK,r16 ; выставим маску INT0
sei
Кстати на tiny2313 можно сформировать прерывание на любых выводах PCINT0…7, на Mega до 48 серии эти возможности отсутствуют…
Есть такие операции, при выполнении которых, возникшие прерывания могут вызвать сбой программы. В таких случаях перед началом выполнения операции пишем CLI, а после SEI. Называются такие операции –
атомарными.
Желательно, чтобы программы прерываний были компактными и выполнялись с максимальной скоростью, потому, что целью любых прерываний является фиксация события. Если по разным причинам программа выполняется медленно, то достаточно зафиксировать событие и обработать его чуть позже.
Если векторы используемых в программе прерываний следуют в таблице не подряд, то для расположения нужного вектора в нужном месте памяти программ следует использовать директивы .org, например программа с прерываниями по INT0 и INT1 для ATtyny2313 будет следующей:
; =================================================== Установки
.def temp=r16 ; директива .def назначает регистру r16 имя temp
;==================================================== Начало программы
.cseg ; директива .cseg определяет начало сегмента кодов
.org 0 ; начало первой строки программы
; =================================================== Векторы прерывания
rjmp Reset ; Reset - Переход на начало программы
.org INT0addr
rjmp INT_0
.org INT1addr
rjmp INT_1
; =================================================== Инициализация портов
Reset:
ser temp ; устанавливаем все биты регистра temp в 1
out DDRB,temp ; переводим все биты порта B на вывод
clr temp ; обнуляем регистр temp
out DDRD,temp ; переводим все биты порта D на ввод
out PortB,temp ; отключаем подтягивающие резисторы портов B
ldi temp,0b00001100 ; записываем в temp число 0b00001100
out PortD,temp ; отключаем подтягивающие резисторы портов D кроме 2 и 3 бита
; =================================================== Инициализация прерываний
ldi temp,(1<<INT1)|(1<<INT0) ; запись единиц в биты соответствующие INT1 и INT0, регистра temp (0b11000000)
out GIMSK,temp ; инициализация внешних входов INT1 и INT0
ldi temp,(1<<ISC11)|(1<<ISC01) ; запись единиц в биты соответствующие ISC11 и ISC01, регистра temp (0b00001010)
out MCUCR,temp ; инициализация прерываний по спадающему фронту входного сигнала
SEI ; разрешение прерываний
; =================================================== Программа
main:
rjmp main ; зацикливаемся
; ==================================================== Подпрограммы прерываний
INT_0: ; Условия прерывания по INT0
clr temp ; Обнуляем все выходы
out PORTB,temp ; порота PORTB
ldi temp,1 ; Устанавливаем 0 бит
out PORTB,temp ; порота PORTB и выходим из прерывания
reti
INT_1: ; Условия прерывания по INT1
clr temp ; Обнуляем все выходы
out PORTB,temp ; порота PORTB
ldi temp,2 ; Устанавливаем 1 бит
out PORTB,temp ; порота PORTB и выходим из прерывания
reti
Названия векторов прерываний можно посмотреть в файле tn2313def.inc под заголовком: INTERRUPT VECTORS. Этот файл можно найти в папках установленной программы AVR Studio 5.0 или Atmel Studio
для ATtiny2313 будет выглядеть так:
; ***** INTERRUPT VECTORS ************************************************
.equ INT0addr = 0x0001 ; External Interrupt Request 0
.equ INT1addr = 0x0002 ; External Interrupt Request 1
.equ ICP1addr = 0x0003 ; Timer/Counter1 Capture Event
.equ OC1Aaddr = 0x0004 ; Timer/Counter1 Compare Match A
.equ OC1addr = 0x0004 ; For compatibility
.equ OVF1addr = 0x0005 ; Timer/Counter1 Overflow
.equ OVF0addr = 0x0006 ; Timer/Counter0 Overflow
.equ URXCaddr = 0x0007 ; USART, Rx Complete
.equ URXC0addr = 0x0007 ; For compatibility
.equ UDREaddr = 0x0008 ; USART Data Register Empty
.equ UDRE0addr = 0x0008 ; For compatibility
.equ UTXCaddr = 0x0009 ; USART, Tx Complete
.equ UTXC0addr = 0x0009 ; For compatibility
.equ ACIaddr = 0x000a ; Analog Comparator
.equ PCIaddr = 0x000b ;
.equ OC1Baddr = 0x000c ;
.equ OC0Aaddr = 0x000d ;
.equ OC0Baddr = 0x000e ;
.equ USI_STARTaddr = 0x000f ; USI Start Condition
.equ USI_OVFaddr = 0x0010 ; USI Overflow
.equ ERDYaddr = 0x0011 ;
.equ WDTaddr = 0x0012 ; Watchdog Timer Overflow
.equ INT_VECTORS_SIZE = 19 ; size in words
а эта программа для микроконтроллера ATmega64 где задействованы 7 внешних прерываний и прерывание по совпадению таймера для формирования аппаратной задержки:
/*
ATMega64
1000000Hz
programm led contol interrupt
*/
.include "m64def.inc"
; =================================================== Установки
.def temp = r16 ; Определение основного рабочего регистра temp
.def temp1 = r17 ; Определение дополнительного рабочего регистра
.def temp2 = r18 ; Определение дополнительного рабочего регистра
.def temp3 = r19 ; Определение дополнительного рабочего регистра
.def temp4 = r20 ; Определение дополнительного рабочего регистра
.equ time = 3024 ; Присвоение перменой time значения 3024
; =================================================== Начало программного кода
.cseg ; Выбор сегмента программного кода
.org 0 ; Установка текущего адреса
; =================================================== Векторы прерываний
rjmp RESET ;Обработка сброса
.org INT0addr ; Установка текущего адреса
rjmp EXT_INT0 ;Обработка внешнего прерывания INT0
.org INT1addr ; Установка текущего адреса
rjmp EXT_INT1 ;Обработка внешнего прерывания INT1
.org INT2addr ; Установка текущего адреса
rjmp EXT_INT2 ;Обработка внешнего прерывания INT2
.org INT3addr ; Установка текущего адреса
rjmp EXT_INT3 ;Обработка внешнего прерывания INT3
.org INT4addr ; Установка текущего адреса
rjmp EXT_INT4 ;Обработка внешнего прерывания INT4
.org INT5addr ; Установка текущего адреса
rjmp EXT_INT5 ;Обработка внешнего прерывания INT5
.org INT6addr ; Установка текущего адреса
rjmp EXT_INT6 ;Обработка внешнего прерывания INT6
.org INT7addr ; Установка текущего адреса
rjmp EXT_INT7 ;Обработка внешнего прерывания INT7
.org OC1Aaddr ; Установка текущего адреса
rjmp OC1_A ;Обработка совпадения A таймера 1
; =================================================== Начало программы
RESET:
; =================================================== Инициализация стека
ldi temp,low(RAMEND) ; Устанавливаем младший бит из значения RAMEND прописанного по
out SPL,temp ; умолчанию в файле m64def.inc
.if (RAMEND)>=0x0100 ; Если значение опреративной памяти превышает число 256 то,
ldi temp,high(RAMEND) ; Устанавливаем старший бит из значения RAMEND прописанного по
out SPH,temp ; умолчанию в файле m64def.inc
.endif ; Конец условия
; =================================================== Инициализация портов
ser temp ; устанавливает все биты регистра temp в 1
clr temp1 ; обнуляет регистр temp1 - все биты регистра temp1 в 0
out DDRB, temp ; переводит все биты порта B на вывод
out DDRC,temp
out DDRD, temp1 ; перевод битов PD,PE(0-7) на ввод
out DDRE, temp1
out PortB, temp1 ; отключает подтягивающие резисторы портов B
out PortC,temp1
ldi temp2,(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3) ; запись битов PD0, PD1, PD2, PD3
out PortD, temp2 ; подключение подтягивающих резисторов на PD0, PD1, PD2, PD3
ldi temp2, (1<<PE4)|(1<<PE5)|(1<<PE6)|(1<<PE7) ; запись битов
out PortE, temp2 ; подключение подтягивающих резисторов
; =================================================== Инициализация прерываний
ser temp ; устанавливает все биты регистра temp в 1
;ldi temp,(1<<INT0)|(1<<INT1)|(1<<INT2) ; запись битов INT0, INT1 и INT2
out EIMSK,temp ; инициализация внешних входов INT0, INT1 и INT2
ldi temp,(1<<ISC01)|(1<<ISC11)|(1<<ISC21)|(1<<ISC31) ; запись битов ISC01, ISC11, ISC21, ISC31
sts EICRA,temp ; инициализация прерываний по спадающему фронту входного сигнала
; =================================================== Инициализация таймера T1
CLI ; Запрет прерываний
clr temp
out TCCR1A, temp
sts TCCR1C, temp
ldi temp,(1<<WGM12)|(1<<CS12)|(1<<CS10) ; Выбор режима таймера (WGM1(0,1,3)-0, WGM12-1)-CTC, (CS11-0, CS1(0,2)-1)-1024
out TCCR1B, temp
ldi temp, high(time) ; Старший полубайт кода совпадения
out OCR1AH, temp ; Запись в регистр совпадения старш.
ldi temp, low(time) ; Младший полубайт кода совпадения
out OCR1AL, temp ; Запись в регистр совпадения младш.
; =================================================== Запись в регистр маски прерываний
ldi temp,(1<<OCIE1A) ; Разрешаем прерывание от таймера
out TIMSK, temp
SEI ; разрешение прерываний
; =================================================== программа
ldi temp4, 0b10101010 ; Начальная установка значения регистра temp4
Loop:
rjmp Loop
; =================================================== Подпрограммы
; =================================================== запись состояния кнопок по прерыванию
EXT_INT0:
ldi temp3,1 ; Запись состояния кнопки PD0
out PortB,temp3
reti
EXT_INT1:
ldi temp3,2 ; Запись состояния конпки PD1
out PortB,temp3
reti
EXT_INT2:
ldi temp3,4 ; Запись состояния конпки PD2
out PortB,temp3
reti
EXT_INT3:
ldi temp3,8 ; Запись состояния кнопки PD3
out PortB,temp3
reti
EXT_INT4:
ldi temp3,16 ; Запись состояния конпки PD4
out PortB,temp3
reti
EXT_INT5:
ldi temp3,32 ; Запись состояния конпки PE5
out PortB,temp3
reti
EXT_INT6:
ldi temp3,64 ; Запись состояния конпки PE6
out PortB,temp3
reti
EXT_INT7:
ldi temp3,128 ; Запись состояния конпки PE7
out PortB,temp3
reti
OC1_A:
com temp4 ; Инверсия регистра temp4
out PortC,temp4
reti
; =================================================== Конец
Как видно, сразу после внешних прерываний следует прерывание по совпадению таймера Т1 (режим СТС). В микроконтроллерах старших серий, таких как ATmega или других, нужно обращаться к строке прерывания через директиву .org, т.к. регистры прерываний уже 32 битные, а иногда могут чередоваться как 16 так и 32 разрядные, и точно угадать номер строки с нужным прерыванием бывает очень затруднительно!
Строки типа: EXT_INT0 или OC1Aaddr, прописаны в файле m64def.inc, который можно найти в папках программы-компилятора. Обычно название файла инициализации микроконтроллера прописывается в самом начале программы как .include "m64def.inc" или подобной ему.
Теперь посмотрим аналогичную программу с прерываниями для ATtiny2313 написанную на СИ
#define F_CPU 1000000UL // установка частоты микроконтроллера
#include <avr/io.h> // стандартная библиотека ввода-вывода
#include <avr/interrupt.h> // библиотека прерываний
ISR (INT0_vect) // определение вектора прерываний INT0
{
PORTB &=~(1<<PB1); // обнулить PB1
PORTB |=(1<<PB0); // установить PB0
}
ISR (INT1_vect) // определение вектора прерываний INT1
{
PORTB &=~(1<<PB0); // обнулить PB0
PORTB |=(1<<PB1); // установить PB1
}
int main(void)
{
DDRB = 0xFF; // Порт PB на вывод
DDRD = 0; // Порт PD на вход
PORTD=(1<<PD2)|(1<<PD3); // Подтягивающий резистор на PD2 и PD3
GIMSK=(1<<INT0)|(1<<INT1); // Установка внешних прерываний INT0 и INT1
MCUSR=(1<<ISC11)|(1<<ISC01); // Установка прерываний по спадающему фронту входного сигнала
asm ("sei");
//---------------------------------------- Бесконечный цикл
while(1)
{
}
return 0; //конец программы
}
Если в программе на Си есть функции прерываний, то их необходимо определить до функции main. Любое прерывание определяется функцией ISR (вектор прерывания) { программа прерывания }
названия веторов прерывания можно посмотреть в файле iotn2313.h под заголовком /* Interrupt vectors: */
В СИ для выполнения прерываний необходимо подключить библиотеку прерываний #include <avr/interrupt.h> и разрешить выполнение глобальных прерываний командой asm ("sei"); т.к.
непосредственное обращение к регистру SREG в СИ ограничено.
Следует так же запомнить, что прерывания работают как с портами установленными на ввод, так и с портами установленными на вывод!
Дальше, подробно рассмотрим внутренние прерывания на примере встроенных таймеров.
Сообщение отредактировал galrad: 07 Ноябрь 2015 - 15:23