Данный урок будет достаточно простым. Надеюсь, вы хорошо усвоили прошлые материалы: урок по таймерам и управление кнопками.
Чтобы понять зачем нужны внешние прерывания, приведу простой пример: допустим у вас в основном цикле программы используются задержки (например, для мигания светодиодом), при этом вам кнопкой нужно перевести работу светодиода в другой режим. Если обработка кнопки находится в основном цикле, то придется ждать пока не отработают все фрагменты кода и очередь не дойдет до обработки кнопки. Иногда это не удобно.
Поэтому в микроконтроллерах придумали такую удобную вещь, как внешнее прерывание. Это значит, что при подаче сигнала на ножку микроконтроллера, основная программа остановится и начнет выполняться такой код, который вы напишите в функции прерывания. После выполнения данной функции, основная программа продолжит выполняться с места, где ее прервали.
Количество ножек, отведенных для внешних прерываний, зависит от типа микроконтроллера, например у atmega8 их 2, у atmega16 их 3. Называются они INT0, INT1 и т.п.
Срабатывать прерывание может по нарастанию сигнала Rise edge, по спаду Falling edge, по любому изменению Any change, Low level по низкому уровню. В визарде выглядит так:
Теперь рассмотрим, как пример, необычное использование внешнего прерывания — частотомер.
Допустим, на ножку настроенную на внешнее прерывание, по нарастанию фронта, подается пульсирующий сигнал. Соответственно каждый период будет срабатывать прерывание, нам остается только подсчитывать их количество за одну секунду.
Для этого настроим таймер 1 на срабатывание 1 раз в секунду, как в 5 уроке. При срабатывании прерывания таймера, обнуляем счетчик и выводим результат на дисплей.
#include <mega8.h> // Alphanumeric LCD Module functions #asm .equ __lcd_port=0x18 ;PORTB #endasm #include <lcd.h> #include <stdio.h> unsigned long i = 0, freq=0; char lcd_buf[33]; interrupt [EXT_INT0] void ext_int0_isr(void) { i++; } interrupt [TIM1_COMPA] void timer1_compa_isr(void) { freq=i; i=0; TCNT1H=0x00; TCNT1L=0x00; } void main(void) { // Declare your local variables here // Input/Output Ports initialization // Port B initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTB=0x00; DDRB=0x00; PORTD=0xFF; DDRD=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 7,813 kHz // Mode: Normal top=FFFFh // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: On // Compare B Match Interrupt: Off TCCR1A=0x00; TCCR1B=0x05; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x1E; OCR1AL=0x85; OCR1BH=0x00; OCR1BL=0x00; // External Interrupt(s) initialization // INT0: On // INT0 Mode: Rising Edge // INT1: Off GICR|=0x40; MCUCR=0x03; GIFR=0x40; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x10; // Global enable interrupts #asm("sei") lcd_init(8); while (1) { sprintf(lcd_buf,"freq=%d",freq); lcd_gotoxy(0,0); lcd_puts(lcd_buf); }; } |
Прошивка и схема доступны здесь.
*Сигнал Trig формируется каждые 50мс.
ether описал решение, которое будет работать и в маленьком проекте, и в большом. При этом, все действия выполняются в основном цикле, по флагам. Естественно, в основном цикле не должно быть никаких задержек, он должен выполняться максимально быстро. Вы можете или сразу привыкать делать все правильно, в этом случае, если в будущем проект разрастется, то это не потребует больших трудозатрат; либо делайте так как понятно, в этом случае будет работать сразу, но возможно с косяками. Все равно в будущем вы будете набивать шишки и рано или поздно задумаетесь о том, как делать правильно. Как ни странно 95% людей выбирают 2 путь.
Ну, особо подводных камней как-то не нашлось 🙂
Но вот один нюанс все-таки всплыл. Если состояние кнопок проверяется в прерывании замера длительности (т.е. — в обработчике внешнего прерывания с ножки, к которой подключен выход датчика Echo. Если не прав — поправьте 🙂 ) — то в случае, если что-то стряслось (с датчиком, или ножка отпаялась или еще что-нибудь), то в обработку кнопок не попадется никогда….А это ж ведь не хорошо? 🙂
К стати, Ич, а чем у вас формируеться сигнал Триг?
Снова здравствуйте снова проблема не работает внешнее прерывание выкладываю свой код. Посмотрите пожалуйста не вызывает обработчик прерывание. Код
#define xtal 1000000L
#include
#asm
.equ __w1_port=0x12 ;PORTD 😀
.equ __w1_bit=0
#endasm
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_DEVICES 8
int set; // переменная которая подсчитавать импульсы и постоянно сравнивает с показанием переменной z
char ps = 0; // флаг нажатой кнопки
unsigned char str[4]; //массив температуры строковой форматы
unsigned char rom_codes[MAX_DEVICES][9]; // масссив иденфекации датчиков
unsigned char devices; // переменная количество датчиков
int z; // переменная результат вычисление температуры
char lcd[33];// масссив строк вывода на дисплей
char lcd_buf[17]; // масссив строк вывода на дисплей
// внешное прерывание по спадающему сигналу
interrupt [EXT_INT0] void ext_int0_isr(void)// функция обработчика внешого прерыравание по ниспадающему уровню сигнала
//по нажатие кнопки PD2 вызывается обработчик прерывания
{
while (1) // цикл обработки внешного прерыравание
{
if((PIND.3==0) && (ps == 0)) // условия при которой переменная set увеличивается n+1(когда PD3 низкий логический уровень)
{
set++; //инкремент
delay_ms(50); // задержка от дребезга контактов
sprintf(lcd_buf,»ustanovka\n%d»,set );// функцию преобразования вывода на жк дисплей
lcd_clear();// очистка жк дисплея
lcd_puts(lcd_buf); // функция вывода на жк дисплей
ps = 1; // флаг нажатой кнопки
}
if(PIND.3==1 && (ps ==1))// условия протиположное первому блока обработчика прерывание
{
ps = 0; // флаг нажатой кнопки
}
};
}
// конец внешого прерывание
void main(void) // главная функция
{
#asm(«sei»);// разрещить глобальные прерывание
PORTD=0x06;//нстроиваем pазряды подключаем внутренные нагрузочные резисторы PD0- датчик DS18b20,- PD2 кнопка настройка вверхнего предела температуры
// PD3 кнопка увеличивает переменную Set;
DDRD=0x01;// настройка порта к 1-wire
DDRC=0x01; // настройка для порта для управление выходного транзистора P
PORTC=0x01;// задание начального значение для микроконтроллера
//настроиваем регистры, отвечающие за внешнее прерырование по INT0 по нисподающими уровню сигнала
GICR=0x40; // установливаем нужные биты единицу в регистр отвечающий за прерывание GIGR|= (INT1>0)|INT0>1)
MCUCR=0x02;// установливаем флаги в регистр статусный регистр первый и нулевой биты установливаем единицы MCUCR|=(ISC01>1)|(ISC00>0)
GIFR=0x40;// регистр отвечающий за обработка внещного прерыравние установим на шестой разряд в единицу
devices=w1_search(0xf0,rom_codes);//функция опроса всех подключенных датчиков
lcd_init(16); // инилиазация режима дисплея
lcd_puts(«Logometr LR 64-03″); // функция вывода текста
delay_ms(1000); // звдержка
while(1){ //основной цикл
delay_ms(900);// задержка преобразование окончание темперетуры
z=ds18b20_temperature(&rom_codes[0][0]);// получение в пеоеменую z температуру заданного датчика
itoa(z, str); //преобразование значение температуры символьный вид
if(z>=40){ //условие для управление выходными транзисторами
PORTC=0x00;// управление выходами
sprintf(lcd,»t=%sC\nVisokaya»,str);// функция подгодовки строк для вывода на дисплей
}
else{
PORTC=0x03;
sprintf(lcd,»t=%sC\nNormalnaya»,str);
}
lcd_clear(); // функции очистки дисплея
lcd_puts(lcd);// функция вывода информации на дисплей
}
}
// конец программмы
у вас внутри прерывания бесконечный цикл, это 5 баллов
Здраствуйте, Admin, прерыванияим можно любые процессы остановить ненадолго например? И это прерывания
interrupt
[EXT_INT0] void ext_int0_isr(void)
{
\\какое-то действие
}
можно
Подскажите пожалуйста, как сделать так чтобы таймер мерял импульсы за 30 секунд, потом выводил на экран результат? Допустим в верхней строчке шло текущее измерение, а в нижней хранилось значение прошлого измерения? И еще вопрос я хочу кол-во подсчитанных импульсов за 30 сек умножить на 120 и разделить на 333, и потом уже вывести на экран как результат измерения, как это сделать?
делаете 2 переменных, и выводите их по очереди, куда угодно. второй вопрос не понятен result = (x*120)/333?
Да, так точно!
По Low level какое минимальное напряжение сигнала может быть?
от -0.5 до 0.2VCC
if(flag == 1)
{
delay_us(hz); // НЕ ПОЛУЧАЕТСЯ А ЕСЛИ вот так delay_ms(hz); работает но управление симистором большой добавляет время. Как можна делать по ступенични управлять
PORTD.0 = 1; // УПРАВЛЕНИЕ СИМИСТОР
delay_us(500);
PORTD.0 = 0;
delay_us(500);
flag = 0;
};
if(PINB.0 == 0)
{
hz++; //увиличения
}
только написать какую то свою функцию, или использовать таймер
// управление симистор
#include
#include
int flag;
unsigned long int hz = 0;
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
flag++; // вход детектор нуль сеть
if(flag == 1)
{
delay_ms(hz); // как создать можна увиличение delay_us(hz);это не получайетса а если switch(hz){ case 1:delay_(100);break}а это болшой код
PORTD.0 = 1; // выход симистор
delay_us(500);
PORTD.0 = 0; // выход симистор
delay_us(500);
flag = 0;
};
}
// Declare your global variables here
void main(void)
{
// Declare your local variables here
PORTB=0x00;
DDRB=0x08;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0xFF;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD =0b11111110;
DDRD = 0b00000001; // 0b PB8 PB7 PB6 PB5 PB4 PD3 PB2 PB1
// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
GICR|=0x40;
MCUCR=0x02;
GIFR=0x40;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;
// Global enable interrupts
#asm(«sei»)
while (1)
{
if(PINC.0 == 0 )
{
hz++;
delay_ms(10);
};
};
}
или как можно исполизоват timer на входной прерывания ну чтобы управлять симистор…
внутри внешнего прерывания, включаете таймер, обнуляете его, выключаете ногу мк. внутри прерывания по совпадению, выключаете таймер, включаете ногу. в основном цикле меняете величину OCR прерывания по совпадению.
как я получу OCR в PORTB.1
направления дайте пажалувста
как я получу OCR в PORTD.0
направления дайте пажалувста
flag++; // вход детектор нуль сеть
if(flag == 1)
{
delay_ms(hz); // как создать можна увиличение delay_us(hz);это не получайетса а если switch(hz){ case 1:delay_(100);break}а это болшой код
PORTD.0 = 1; // выход симистор
delay_us(500);
PORTD.0 = 0; // выход симистор
delay_us(500);
flag = 0;
};
спасибо большое нашел свой код
// Îáðàáîòêà ïðåðûâàíèé
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
PORTD.0 = 1;
delay_us(50);
PORTD.0 = 0;
delay_us(50);
TCCR1A=0x00;//ÂÛÊËÞ×ÀÅÒÅ ÒÀÉÌÈÐ
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
}
Думаю на выходных сделаю пример с симистром
Выложу свой проект велокомпьютера, прерывания на колесе — магнит + геркон, отображает скорость дистанцию и время.
Здравствуйте, подскажите пожалуйста можно ли сделать внешние прерывания по условию, то есть, если некоторое условие выполняется, то считаем импульсы, а если нет, то нет?
можно внутри обработчика внешнего прерывания написать условие
Либо разрешать/запрещать прерывания, когда надо, управляя нужным флагом в регистре TIMSK.
Помогите пожалуйста, не знаю как правило записать программу с внешним прерыванием.
static char count=0;
static ovf=0;
ISR(INTO_vect)
{ if count ==0)
запуск таймера ,если count =0;
count++;
else if (count==48)
{ остановка таймера
ovf-время 3x оборотов
время переполнения таймера
(есть схема в протеусе )
Здравствуйте.Подскажите,от чего зависит максимально возможная частота внешних прерываний,кроме времени исполнения прерывания.?
от частоты на которой работает проект
Добрый вечер. 🙂 Спасибо за уроки благодаря вам я учусь программировать .Есть готовые проекты .Хотел бы у вас поинтересоваться вот вы говорили в каком-то посте, чтобы зажечь светодиод при определённой частоте надо использовать if — условие .Сделал проект на оптрон идут 3 частоты 25,45,65гц.с оптрона в контроллер там 3 светодиода .При 25гц. всё понятно.светодиод горит,а при 45 горит и первый светодиод и второй .КАК ОТДЕЛИТЬ 45 ОТ 25 И ТЕМ БОЛЕЕ ОТ 65гц. ❓
несколько раз читал вопрос, но так его и не понял
Добрый день. Я отсчитал за секунду 25импульсов и зажог светодиод 1,дальше итсчитываю 50импульсов и зажигаю светодиод 2 но при этом горит и светодиод 1 так как в 50 импульсов входит и 25.Как отделить????????
Считаете за секунду и проверяете
Здравствуйте ув. Админ. Хороший сайт, подскажите пожалуйста два момента. Хочу сделать измеритель влажности и поступил следующим образом: дисплей подключил к порту D, в качестве прерываний использовал 5 ножку (INT1) ,она как раз свободна,- так можно делать?
В программу вдобавок впилил формулу пересчета :
interrupt [EXT_INT0] void ext_int0_isr(void)
{
i++;
}
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
freq=565,1-(0,0765*i); ///формула пересчета
i=0;
И вывожу значение freq на дисплей.
Вроде как все работает, но дисплей мигает часто и вдобавок пробиваются какие то кракозябры. Дисплей исправен. Дело в том, что входная частота составляет 7Кгц — это много?
Уважаемый Админ! Растолкуйте, пожалуйста: команда asm(«cli») останавливает внешнее прерывание? Какие прерывания будут работать после этой команды (например RESET)?
Дисплей может мигать если его часто очищать. Попробуйте затирать символы не очищая.
Cli не останавливает прерывание. Она их запрещает. Т.е. после этой команды программа не будет заходить в прерывания.
Здравствуйте! Пытаюсь разобраться с прерыванием. Вроде все работает, даже через чур.
Не могу понять почему при включении прерывания (GIMSK: INT=1) если до этого было воздействие на ногу, то программа сразу же уходит в обработку прерывания. Может я чего-то не учел?
Мой пробный код
#include tiny13.h>
#include delay.h>
interrupt [2] void External_Interrupt(void)
{
PORTB ^=0b00001000;;
}
void main(void)
{
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
PORTB|=0b00000111;;
DDRB =0b00011000;
MCUCR=0b00000010;
GIMSK=0b00000000;
#asm(«sei»)
while (1)
{
if (PINB.0 == 0)
{
delay_ms(500);
GIFR &=0b10111111;
GIMSK |=0b01000000;
}
if (PINB.2 == 0)
{
delay_ms(500);
GIMSK &=0b10111111;
}
}
}
прерывание происходит когда If the I-bit in SREG and the INT0 bit in GIMSK are set (one), the MCU will jump to the corresponding Interrupt Vector. Следовательно сбрасывайте эти биты, чтобы не уходить в прерывание
Разобрался. Протеус7 гонит. 👿 👿 А вот восьмой работает ровно. 😛 Завтра на железе проверю.
А насколько затратно считать импульсы по внешнему прерыванию?
Вот например в частотомере, при возникновении внешнего прерывания происходит простой инкремент переменной переменная эта 32разрядная сколько понадобится на это тактов? Тоесть какая макс. измеряемая частота будет при 20МГц тактовой.
Я так думаю что на инкремент 32 битной переменной нужно 4 такта.