Учебный курс AVR. Использование внешних прерываний в AVR
14/09/2011 - 17:38
Pavel Bobkov
Микроконтроллер общается с внешним миром посредством портов ввода/вывода. Порт представляет собой совокупность выводов микроконтроллера объединенных в группу. Каждый вывод порта можно независимо от других выводов конфигурировать на вход или на выход. Также многие выводы микроконтроллеров AVR имеют альтернативные функции. Одна из таких функций – это прерывание по изменению состояния вывода, так называемое внешнее прерывание. Об этом прерывании мы сегодня и поговорим.
Внешние прерывания
Для примера будем рассматривать микроконтроллер ATmega16.
У него три внешних прерывания - INT0, INT1 и INT2. Эти прерывания жестко «привязаны» к выводам PD2, PD3 и PB2 и переназначить их на другие выводы нельзя.
Когда используются внешние прерывания, выводы PD2, PD3 и PB2 конфигурируются на вход. Однако если они настроены на выход, внешние прерывания тоже будут генерироваться при изменении их состояния, что позволяет реализовать программные прерывания.
Для разрешения или запрещения внешних прерываний предназначен управляющий регистр GICR (General Interrupt Control Register).
Установка битов INT1, INT0 или INT2 разрешает прерывания при возникновении события на соответствующем выводе микроконтроллера AVR, а сброс — запрещает.
Естественно нужно установить еще и флаг глобального разрешения прерываний - I, который расположен в регистре SREG. Без него вообще ни одно прерывание вызываться не будет.
Внешнее прерывание может происходить по одному из условий:
– по низкому уровню на выводах INT0, INT1,
– по любому изменению логического уровня на выводах INT0, INT1
– по спадающему фронту сигнала на выводах INT0, INT1, INT2,
– по нарастающему фронту на выводах INT0, INT1, INT2.
Небольшая иллюстрация, чтобы уяснить разницу между фронтом и уровнем сигнала.
Условия генерации прерываний устанавливаются с помощью конфигурационных регистров. Для INT0, INT1 – это регистр MCUCR (MCU Control Register). Для INT2 – MCUCSR (MCU Control and Status Register)
В таблице ниже приведены возможные значения разрядов ISC01, ISC00 и соответствующие им условия генерации внешнего прерывания.
Для прерывания INT1 таблица выглядит аналогично, только управляющие разряды другие — это ISC11 и ISC10.
Прерывание INT2 может происходить только по фронтам сигнала, поэтому для установки условий используется всего один бит - это бит ISC2 регистра MCUCSR.
Кстати, при смене значения бита ISC2 может быть сгенерировано прерывание INT2. Чтобы этого не происходило, нужно производить модификацию бита ISC2 так: запретить внешнее прерывание, поменять бит ISC2, сбросить флаг прерывания — INTF2 (смотри ниже) и опять разрешить прерывание INT2.
Обнаружение фронтов сигналов на выводах INT0/INT1 осуществляется синхронно, то есть по сигналу тактового генератора. Минимальная длительность входного импульса, гарантирующая генерацию прерывания, составляет один период тактового сигнала микроконтроллера AVR.
Внешние прерывания INT0/INT1 сконфигурированные на срабатывание по низкому уровню обрабатываются асинхронно. Для генерации прерывания, уровень должен удерживаться до окончания выполнения текущей команды. Если после обработки прерывания уровень еще удерживается, прерывание будет вызвано снова.
Обнаружение перепадов сигнала на выводе INT2 тоже осуществляется асинхронно. Минимальная длительность импульса, гарантирующая генерацию прерывания, составляет 50 нс.
Внешние прерывания, обнаруживаемые асинхронно, могут быть использованы для пробуждения микроконтроллера, находящегося в любом из шести режимов пониженного энергопотребления. Об этом будет отдельный материал.
Последний регистр, имеющий отношение к внешним прерываниям, - это статусный регистр GIFR (General Interrupt Flag Register). В нем содержатся флаги, устанавливаемые в случае формирования запроса на внешнее прерывание.
Флаги сбрасываются аппаратно, когда вызывается обработчики прерываний. Также их можно сбросить программно, записав в регистр единицы. Причем сброс нужно производить перезаписью регистра GIFR, на не операцией побитового ИЛИ.
Неправильно: GIFR |= (1<<INTF1) Правильно: GIFR = (1<<INTF1). Почему? Читаем в статье про таймеры AVR.
Неправильно: GIFR |= (1<<INTF1) Правильно: GIFR = (1<<INTF1). Почему? Читаем в статье про таймеры AVR.
Пример использования внешнего прерывания
Рассмотрим простой пример использования внешнего прерывания — опрос тактовой кнопки.
//***************************************************************************
// Author(s)...: Pashgan http://ChipEnable.Ru
// Target(s)...: mega16
// Compiler....: IAR EWA 5.11A
// Description.: Использование внешнего прерывания.
// Data........: 14.09.2011
//***************************************************************************
#include <ioavr.h>
#include <intrinsics.h>
#include "lcd_lib.h"
#define PIN_INT0 PD2
volatile unsigned char flagInt0 = 0;
int main( void )
{
LCD_Init();
//настраиваем вывод на вход
DDRD &= ~(1<<PIN_INT0);
//включаем подтягивающий резистор
PORTD |= (1<<PIN_INT0);
//разрешаем внешнее прерывание на int0
GICR |= (1<<INT0);
//настраиваем условие прерывания
MCUCR |= (1<<ISC01)|(0<<ISC00);
__enable_interrupt();
while(1){
if (flagInt0){
LCD_Goto(0,0);
LCD_SendString("on ");
flagInt0 = 0;
__delay_cycles(16000000);
LCD_Goto(0,0);
LCD_SendString("off");
}
}
return 0;
}
//внешнее прерывание. обработчик.
#pragma vector=INT0_vect
__interrupt void int0(void)
{
unsigned char i = 0;
unsigned char count = 0;
while(i < 16){
if ((PIND & PIN_INT0) == 0) count++;
i++;
}
if (count > 10) flagInt0 = 1;
}
В начале функции main мы инициализируем дисплей, настраиваем вывод PD2 на вход и включаем подтягивающий резистор. Разрешаем внешнее прерывание INT0 и задаем условие его генерации – по спадающему фронту. Далее устанавливается флаг глобального разрешения прерываний, и программа бесконечно выполняет цикл while, в теле которого происходит опрос программного флага.
Если флаг установлен, то на дисплей выводится строка “On” и флаг сбрасывается. А после секундной задержки на дисплей выводится строка “Off”. Если флаг не установлен, то ничего не происходит.
Вывод PD2 подтянут с помощью внутреннего резистора к плюсу питания. Пока кнопка не нажата на выводе присутствует напряжение логической единицы. Когда кнопка нажимается, потенциал вывода PD2 становится равен напряжению логического нуля.
Обнаружив перепад сигнала, микроконтроллер вызывает обработчик внешнего прерывания, в теле которого происходит многократный опрос вывода PD2. Если в течение этой процедуры уровень сигнала все еще остается низким, о чем свидетельствует значение счетчика, - взводится флаг. Далее обработчик прерывания заканчивает свою работу, и программа возвращается в основной цикл.
Файлы
Tagged under
Comments
struct
{
...
} _PORTA @ 0x21;
Выдаёт ошибку Error[e46]: Undefined external
Code:
__io struct{
...
}_PORTA @ 0x21;
static volatile __io struct
{
BYTE _P0:1, _P1:1, _P2:1, _P3:1, _P4:1, _P5:1, _P6:1, _P7:1;
} _DDRA @ 0x1A;
, а линковщик IAR 5.51 ругается на такие определения, говорит Error[e46]: Undefined external "_MCUCR" referred in TCore
Тут же запускаю версию IAR 4.20 и компиляция работает без проблем, всё линкуется
microsmaster собакаяндексточ кару
Если так ничего не придумаете то ничего, в принципе на версии 4.20 работает, а IAR в параллель работает нормально.
1) if (flagInt0) - почему именно flagint0?
2) Почему в цикле есть flagInt0 = 0; итак же ясно что флагинт0 = 0, ещё в самом верху.
3) flagInt0 = 1; в конце. т.е. в 1 вопросе флагинт0 = 1 ?
2. В начале программы выполняется инициализация flagInt0.Если этого не сделать в переменной может быть что угодно.
флаг сигнализирует о том что была нажата кнопка, после обработки нажатия (вывод строки) он сбрасывается.
3. не понял вопроса
Цитата - "Флаги сбрасываются аппаратно, когда вызывается обработчики прерываний. Также их можно сбросить программно, записав в регистр нули."
В AVR флаги сбрасываются записью в них логической единицы, исправьте.
И очень интересно увидеть как с компьютера можно управлять МК, помигать светиком и т.п.))
http://chipenable.ru/index.php/programming-c/43-avr-usart-uart.html
2. Наверно надо указать, что при прерывании по уровню его флаг всегда == 0.
Опишите, что на Си нельзя применять для программного сброса флага прерывания операцию:
GIFR |= (1
Code:
GIFR |= (1 << INTF0);
Надо только так:
Code:
GIFR = (1 << INTF0);
__enable_interrupt();
и каков ее аналог в CVAVR?
Я понимаю что это связано как то с разрешением или обьявлением прерываний но разаобратья не могу.
А вообще в конце статьи есть проект для CodeVision AVR.
Кроме того от длительного нажатия вы защитились,зада в прерывание по спадающему фронту, но так нельзя будет обрабатывать события по долгому нажатию (например короткое нажатие - одно событие, долгое - другое).
Получается что кнопки на внешние прерывания лучше вообще не вешать, а опрашивать их состояние периодически?
Linker error: D:\Ci\Book_C\Ex ternalInterrupt -CVAVR\External Interrupt\lcd_l ib.h(49): function 'LCD_Init' declared, but never defined
RSS feed for comments to this post