Данный урок будет достаточно простым. Надеюсь, вы хорошо усвоили прошлые материалы: урок по таймерам и управление кнопками.
Чтобы понять зачем нужны внешние прерывания, приведу простой пример: допустим у вас в основном цикле программы используются задержки (например, для мигания светодиодом), при этом вам кнопкой нужно перевести работу светодиода в другой режим. Если обработка кнопки находится в основном цикле, то придется ждать пока не отработают все фрагменты кода и очередь не дойдет до обработки кнопки. Иногда это не удобно.
Поэтому в микроконтроллерах придумали такую удобную вещь, как внешнее прерывание. Это значит, что при подаче сигнала на ножку микроконтроллера, основная программа остановится и начнет выполняться такой код, который вы напишите в функции прерывания. После выполнения данной функции, основная программа продолжит выполняться с места, где ее прервали.
Количество ножек, отведенных для внешних прерываний, зависит от типа микроконтроллера, например у 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); }; } |
Прошивка и схема доступны здесь.
Какая макс частота?
Спасибо за урок.
Спасибо за уок.
А как сделать так, чтоб от разной частоты загорались диоды
Например от 1000 — зеленый
2000 — желтый
3000 — красный
Хотябы направте, где смотреть. Зарание спасибо.
отсчитайте за секунду и в зависимости от результата зажигайте, условие if
Спасибо за урок! Вот не могу додуматься как вывести на семисегментный индикатор, не подскажете?
http://avr-start.ru/?p=435
Не могу найти правый индикатор с картинки в Proteus. Подскажите пожалуйста название.
он не среди компонентов, ищите в virtual instrument называется counter timer. Только нужно в его настройках переключиться на частоту
Благодарю.
Сколько минимум вольт нужно подавать на INT0 чтобы частотомер работал?
Зависит от напряжения питания, например в даташите на мегу8 есть таблица Pin Thresholds and Hysteresis, если по ней посмотреть то при питании 5В лог единица около 1,8В.
Есть ли возможность полученную частоту выдавать через пьезоизлучатель? Как это можно сделать(в общих чертах, пожалуйста)?
Если я правильно понял вопрос то так http://avr-start.ru/?p=476
да, правильно. фактически, хотелось бы совместить эти два урока, просто не совсем пойму, как настроить timer2 по аналогии с timer1 в уроке 7.1. подскажите пожалуйста
привет
я сабрал частотамер . Proteus работейит но плату точна не паказивайет .паказивайет другой цифри….
жду ответа
а может без кварца я запустил плату ..но незнаю чем причина
Причин может быть много, начиная от формы сигнала и амплитуды которую вы измеряете.
Здравствуйте, не получается вывести значения на LCD (WH1602a), при использование #asm(«sei») экран перестает выводить данные.
Видимо слишком часто вызываются прерывания, что не успевает выполниться основной цикл.
Объясните строку: sprintf(lcd_buf,»freq=%d»,freq);
Спасибо.Хороший сайт.
Что означает команда sprintf ?
переводит одну строку в другую по определенному формату. т.е. вы можете отображать переменную в двоичном виде, или десятичном, как раз перевод осуществит sprintf
Создал проект на осноове этой статьи павда на меге16 и тактированием от часового кварца в асинхронном режиме, для прерывания использую INT0(по низкому уровню) в протеусе все работает нормально в железе сразу после прошивки в freq= выводит какие то случайные чиста 13569 и тд. но должно быть 0 сигнал не подавал. Подскажите в чем может быть проблема ?
отсутствие подтяжки
Спасибо за ответ 🙂
установил подтяжку 10к резистор частично помогло теперь при включении выводится ноль (подключил к int0 кнопку на gnd чтоб узнать частоту нажатия кнопки) но при первом и дальнейшем нажатии выводятся набор чисел который необнуляется при отсутствии нажатия незнаю может выводится не втом формате
очевидно таймер не правильно настроен, раз не сбрасывается. не забывайте что у кнопки есть дребезг, она может и 100 и 1000 нажатий выдать, вместо одного.
работаетт 😛 паралельно кнопке поставил кондер на 100nF
программа
#include
#include
int i=10;
interrupt[3] void ExtInt0(void) // Обработчик прерываний на int0 (Порт D, вывод 3).
{
i=i+10;
PORTD.3=1;
delay_ms(10);
PORTD.3=0;
delay_ms(10);
}
interrupt[4] void ExtInt1(void) // Обработчик прерываний на int1 (Порт D, вывод 4).
{
i=i-10;
PORTD.3=1;
delay_ms(10);
PORTD.3=0;
delay_ms(10);
}
void main(void)
{
PORTB=0x06;
DDRB=0x3E9;
//настраиваем вывод на вход
DDRD=0b11110011;
//включаем подтягивающий резистор
PORTD=0b11111111;
//разрешаем внешнее прерывание на int0
GICR=0b11000000;
//настраиваем условие прерывания
MCUCR=0b00000000;
#asm(«sei»)
while (1)
{
if(i<10)
{i=10;}
}
PORTB.0=1;
delay_ms(10);
PORTB.0=0;
delay_ms(i);
};
выдается ошибка
Error: undefined symbol 'GICR'
где зарыта собачка ???
attini 2313a
пока ждал ответа решил проблему
заменой GICR на GISM
но остался вопрос почему не работал GICR?
Очевидно потому что у тини нет регистра с таким именем
Добрый день.
Подскажите как с помощью внешнего прерывания сделать одномерное меню.
Принцип следующий: есть 3 кнопки(1-вход в меню,подключена к ножке внешнего прерывания; 2 и 3 +- в каждом пункте меню). Вход в меню работает, переменные увеличиваются и уменьшаются. Как по кнопке 1(при длительном нажатии) сделать выход из прерывания? Длительное нажатие прописал, но после того как отрабатывается длительное нажатие, происходит снова вход в прерывание.
Да еще забыл добавить. Пункты меню листаются той же кнопкой 1. А вот выйти не получается.
Сама идея такой организации плохая. Лучше прерывание для этого не использовать.
Как лучше? В основной программе постоянно проверять состояние? Пробовал и так но если попадаешь на задержку вход в меню получается с запозданием. Задержки есть по 15 сек.
Значит логика программы построена не правильно 🙂 Сделайте так, чтобы программа вообще не использовала задержки в виде пустых тактов. Делайте все по таймеру.
т.е. кроме как перестроить логику, вариантов больше нет?
У Вас в любом случае кнопка будет дребезжать, следовательно на одно нажатие будет вызываться дофигища прерываний. Да и организовывать какую то логику внутри прерываний считается плохим тоном.
Ич, для использования кнопок и работы с меню в программах, сложнее мигания светодиодами 🙂 , лучше использовать опрос кнопок в прерываниях таймера. Возьмите таймер0, настройте его на прерывания каждые 5-30 мс (для антидребезга), а дальше организовывайте опрос кнопок как нужно. Удобно использовать флаги. Т.е., в прерываниях выясняем, какая кнопка нажата, поднимаем соотв. флаг. А в основном цикле смотрим на флаги и делаем, что надо.
Спс. Все вроде бы понятно, но….не будет «плохим тоном» 🙂 если нажатие кнопки я буду проверять в прерывании замера длительности импульса HC-SR04? Проверил, вроде работает, подводных камней каких ждать? 🙂