Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

Если вы уже умеете мигать светодиодом, управлять кнопками и хочется большего, чем просто вывести слово на дисплей, то настало время немного почитать скучной теории и разобраться с прерываниями.

Вспомним самую простую программу из первого урока, светодиод включается, пауза, выключается, пауза и по кругу до бесконечности.

while (1)
{
      PORTB.0=1;
      delay_ms(500);
      PORTB.0=0;
      delay_ms(500);
}

Что если в эту программу добавить обработку нажатия кнопки?

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)
{
}

INTERRUPT0

Еще одним вариантом прерывания таймера, является прерывание по совпадению. Представьте себе, что таймер тикает 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)
{
}

INTERRUPT_ExT

Также, прерывание может возникать, при приеме или передаче данных по UART, для этого достаточно настроить соответствующее прерывание.

INTERRUPT_UART

Кстати, иногда по тексту программы требуется отключить/включить прерывания.

// Разрешить глобальные прерывания
#asm("sei")
// Запретить глобальные прерывания
#asm("cli")

Как же узнать, какие прерывания есть у используемого микроконтроллера? Либо читать документацию (даташит) на него, либо посмотреть в codewizard список доступных прерываний. В визарде обычно прерывания включается галочкой interrupt. Настоятельно советую разобраться с визардом, он сэкономит кучу времени.

Update. По просьбам трудящихся. Как пользоваться CodeWizard

Запускаем визард.
wiz

Смотрим на имеющуюся периферию
wiz1

Сразу нужно понять, что это относится к самому камню, микроконтроллер не знает что такое кнопка, у него есть ножка и все манипуляции производятся именно с ней. Для манипуляций с ножкой имеется внешнее прерывание. Как им пользоваться описано в 13 уроке AVR.
wiz2
Для прерываний по таймеру, заходим в раздел Timers/Counter, основное прерывание по таймеру это по совпадению — аналог будильника, т.е. когда выставленное значение совпало с количеством тиков. Про этот режим читаем 5 урок.

Может так оказаться что у таймера нет режима по совпадению, но наверняка есть по переполнению, т.е. если таймер 8 битный, то его максимум 2^8, когда он дотикает до этого значения он обнулится, заодно вызовет прерывание.

22 комментария: Прерывания.

  • Фарход:

    Прерывание возникает в течении нескольких тактов, точно не знаю. Вопрос, если в основном цикле программы выполняется именно функция delay_ms() она до конца отрабатывает или же она тоже «делится на части» ?.

  • Александр:

    #include
    #include
    interrupt (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);
    };
    }не работает
    ошибка Error: D:\AVR\knepa.c(3): ‘[‘ expected, but ‘(‘ found
    подскажите в чем причина

  • admin:

    interrupt это общее название всех прерываний, но нужно понимать что вызвало прерывание — таймер, кнопка, АЦП поэтому там где у вас просто interrupt должно быть interrupt [TIM1_COMPA] void timer1_compa_isr(void) что означает прерывание от таймера1 по совпадению регистра OCRA. Названия прерываний учить не нужно, достаточно пользоваться генератором кода

  • Александр:

    Извините, я новичок. Подскажите где искать генератор кода? И если не сложно вкратце как им пользоваться?

  • Александр:

    З.Ы.Мне нужно чтобы кнопка прерывала выполнение программы

  • Александр:

    На примере мигающего светодиода

  • Александр:

    Подскажите… Использую функцию прерывания по сравнению timer1.Эта функция должна принимать и передавать int i и unsigned char a[ ]. Как правильно оформить описание функции?

  • admin:

    Используйте глобальные переменные, ибо делать функцию внутри прерывания плохая идея, либо в прерывании выставляйте флаги, а в основном цикле делайте функцию.
    ЗЫ в конце статьи добавил чуток про прерывания

  • Mr.Hublot:

    Здравствуйте. Подскажите пожалуйста как настраивается или откуда берется GIMSK? Я новичек еще, скачал пример кода, а cvavr пишет «undefined symbol ‘GIMSK'».

  • admin:

    В даташите он называется General Interrupt Control Register

  • Mr.Hublot:

    А он на всех мк работает? Все настройки проекта перекопал и дш, толку ноль. А в дш почти ничего нет по-этому поводу и в таблице тоже его нет, а eimsk есть…Это EIMSK не тоже самое?

  • admin:

    настраивайте codewizard ом и не мучайтесь

  • hard_electron:

    Добрый Admin у тебя просто великолепный сайт. По твое сайту много научился. Можно опять попросить помощи. Пожалуйста, я вам отправлю архив с моем проектом в нем техническое задание можешь хотя бы меня проконсультировать. Codevision нормально компилирует без ошибок, вот Proteus вытворяет какие то чудеса. Кстати я уже выкладывал уже текст программы и там было ошибка (while).. OK?

  • admin:

    можете присылать, но смотреть не обещаю

  • Алексей:

    GIMSK это регистр разрешения прерывания от входов INT0 и INT1. В вашем случае он будет называться GICR.

  • Алексей:

    На счёт протеуса. Удалите его и забудьте как страшный сон. Эта зараза на разных машинах работает как ей захочется. Если эмулировать пример с тупым миганием светодиода, то пойдёт. А если делать что-то серьёзное, то на выходе, то что работает в протеусе никогда не будет работать в живую. А теперь конкретные факты. В протеусе криво реализованы прерывания по таймера. Периоды плавают в зависимости от мощность процессора ПК и его загруженности. При работе с ЖК дисплеями не всегда корректно выводит информацию. Был косяк, графический дисплей 128х64 прекрасно работал в протеусе, а в железе ничего не выводил. Когда добился стабильной работы в железе, перестало работать в протеусе. Ну и если кому интересно изучить все косяки данного девайса, можно почитать на Казусе. Там о нем очень честно отзываются.

  • admin:

    Таки нужно понимать, чего ждать от протеуса, вот и весь секрет. Рассмотрите вопрос с т.з. способов отладки, мигать светодиодом? выводить инфу в юарт? выводить инфу на дисплей? А если у вас дофига переменных, юарт занят, а дисплея в проекте нет, да еще и проект большой? Я никогда не отлаживаю в протеусе, то в чем не уверен на 100%, но если мне нужно посмотреть состояние сразу нескольких переменных, то намного быстрее поставить точку останова и посмотреть, альтернативы протеусу по скорости, в этом смысле просто нет. Просто не отлаживайте всякие дисплеи, карты памяти и прочую периферийную лабуду и будет вам счастье.

  • Алексей:

    Как это нет? 😯 А куда же делся родной JTAG. Это круче любого протеуса. 😛

  • admin:

    у мелких атмег типо 8 увы jtag нету, а таких камней лежит еще целая кучка.

  • Алексей:

    А для таких камней CVAVR может передать файлы AtmelStudio и там провести эмуляцию. По шагово с заходом в функции, при этом можно не только переменные смотреть, но и даже смотреть во все регистра.

  • Андрей:

    Извините, я новичок. Подскажите что делать если мне нужно запретить прерывание только по таймеру1, после нажатия кнопки, и разрешить его при нажатии второй кнопки, при этом остальные прерывания должны работать?

  • admin:

    у таймера 1 есть регистр где включаются его прерывания. уберите бит который отвечает за прерывание

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Счетчик
Яндекс.Метрика