BMW 3-series Coupe (Бмв ) 2006-2009: описание, характеристики, фото, обзоры и тесты
Длительный тест Range Rover Sport: часть вторая
Audi E-tron (Ауди ) 2010: описание, характеристики, фото, обзоры и тесты
Принципы ухода за АКБ зимой
SEAT Toledo (Сиат Толедо) 1998-2004: описание, характеристики, фото, обзоры и тесты
Skoda Octavia (Шкода Октавия) 1996-1999: описание, характеристики, фото, обзоры и тесты
Chrysler PT Cruiser (Крайслер Пт крузер) 1999-2010: описание, характеристики, фото, обзоры и тесты
Примеряем Audi A6 Allroad и A8 Hybrid к нашим дорогам
Toyota Tundra Crew Max (Тойота Тундра Crew Max) 2006-2009: описание, характеристики, фото, обзоры и тесты
Прерывания.
Опубликовано: 05.09.2018
Если вы уже умеете мигать светодиодом, управлять кнопками и хочется большего, чем просто вывести слово на дисплей, то настало время немного почитать скучной теории и разобраться с прерываниями.
Вспомним самую простую программу из первого урока, светодиод включается, пауза, выключается, пауза и по кругу до бесконечности.
while ( 1 ) { PORTB.0 = 1 ; delay_ms ( 500 ) ; PORTB.0 = 0 ; delay_ms ( 500 ) ; } |
Что если в эту программу добавить обработку нажатия кнопки?
Лекция 8: Прерывания
while ( 1 ) { if ( PINB.1 == 0 ) { PORTD.0 = 1 ; } PORTB.0 = 1 ; delay_ms ( 500 ) ; PORTB.0 = 0 ; delay_ms ( 500 ) ; } |
Допустим пользователь нажал кнопку в момент, когда программа выполняет строчку PORTB.0=1, т.е. до того как будет проверено наше условие if(PINB.1==0), должны выполниться две строчки с задержкой по 500ms. Получается пользователь должен держать кнопку больше секунды, чтобы она сработала, это жутко не удобно.
Или, как например, в динамической индикации, индикаторы быстро загораются по очереди через определенные промежутки времени. Если этот код крутить в основном цикле и плюс к этому обрабатывать допустим измерения, то в лучшем случае индикаторы будут мерцать, а в худшем гореть будет только один.
Думаю идея понятна, подобных ситуаций может быть множество. Решение подобных проблем, когда нужно совместить выполнение основной программы, с некоторой более неотложной задачей, возлагается на прерывание. Прерывание это и есть кусок вашего важного кода.
Например, выполняется ваш цикл мигалки, при этом происходит некоторое событие, программа останавливается, допустим на строчке PORTB.0=0 и перескакивает на строчку if(PINB.1==0). После выполнения этой строчки, программа вернется к основному циклу на строчку delay_ms и будет крутить основной цикл, до тех пор пока снова не будет вызвано следующее прерывание.
interrupt [ номер вектора ] void имя функции обработчика прерывания ( void ) { if ( PINB.1 == 0 ) { PORTD.0 = 1 ; } } void main ( void ) { while ( 1 ) { PORTB.0 = 1 ; delay_ms ( 500 ) ; PORTB.0 = 0 ; delay_ms ( 500 ) ; } ; } |
Так что же это за такие события, способные остановить выполнения основной программы. Например, переполнение таймера. Допустим, вы настроили таймер, который умеет считать до 255 и у него есть прерывание по переполнению. Это значит, что он досчитает до 255, и вызовет, тот самый важный кусок кода.
interrupt [ TIM0_OVF ] void timer0_ovf_isr ( void ) { } |
Еще одним вариантом прерывания таймера, является прерывание по совпадению. Представьте себе, что таймер тикает 1 раз в секунду, мне нужно чтобы он дотикал до 60 и прибавил в переменную минута единичку. Для этого в регистр сравнения таймера нужно запихать 60, дальше он сам по себе будет проверять равно ли текущее количество тиков заданному. Когда это произойдет, программа автоматически прыгнет и выполнит требуемый код.
interrupt [ TIM1_COMPA ] void timer1_compa_isr ( void ) { min ++; } |
Обратите внимание название функций содержат названия таймеров, в первом случае TIM0 уже понятно что прерывание относится к таймеру0, во втором TIM1 — таймер1. Также из названия функции понятно по какому событию происходит прерывание COMPA — compare — сравнение, OVF — overflow, переполнение.
Кроме того, существуют прерывания, которые не связаны с таймерами, например внешнее прерывание. У микроконтроллеров есть ножки отведенные под внешнее прерывание и при воздействии на эту ножку извне программа выполняет код указанный в прерывании. Можно настроить так, чтобы прерывание возникало при низком уровне на ножке, нарастании/спаду фронта или по любому изменению. Об этом уже говорилось в статье про частотомер.
interrupt [ EXT_INT0 ] void ext_int0_isr ( void ) { } |
Также, прерывание может возникать, при приеме или передаче данных по UART, для этого достаточно настроить соответствующее прерывание.
Кстати, иногда по тексту программы требуется отключить/включить прерывания.
// Разрешить глобальные прерывания #asm("sei") // Запретить глобальные прерывания #asm("cli") |
Как же узнать, какие прерывания есть у используемого микроконтроллера? Либо читать документацию (даташит) на него, либо посмотреть в codewizard список доступных прерываний. В визарде обычно прерывания включается галочкой interrupt. Настоятельно советую разобраться с визардом, он сэкономит кучу времени.
Update. По просьбам трудящихся. Как пользоваться CodeWizard
Запускаем визард.
Смотрим на имеющуюся периферию
Сразу нужно понять, что это относится к самому камню, микроконтроллер не знает что такое кнопка, у него есть ножка и все манипуляции производятся именно с ней. Для манипуляций с ножкой имеется внешнее прерывание. Как им пользоваться описано в 13 уроке AVR.
Для прерываний по таймеру, заходим в раздел Timers/Counter, основное прерывание по таймеру это по совпадению — аналог будильника, т.е. когда выставленное значение совпало с количеством тиков. Про этот режим читаем 5 урок.Может так оказаться что у таймера нет режима по совпадению, но наверняка есть по переполнению, т.е. если таймер 8 битный, то его максимум 2^8, когда он дотикает до этого значения он обнулится, заодно вызовет прерывание.