Печать
Категория: PIC
Просмотров: 25221
Допустим, есть у нас МК. Программа на нем исправно работает, но тут происходит некое событие, например, сработал датчик, и это событие нам нужно как то обработать. После чего опять продолжить исполнение программы. Так как исполнение программы прерывается, то и событие, вызвавшее это прерывание, называют прерыванием.
Есть два основных варианта описания обработки прерывания в программе: с постоянным слежением в функциях с помощью отдельных команд (использовали в прошлых статьях при слежении за состояниями кнопок) и с помощью специальной функции обработки прерываний.

Недостатком первого способа является необходимость прописывать в каждой функции программы алгоритм отслеживания прерываний, например:

void pause()
{
     int i;                           // Вводим целое i для счета пробега по циклу
     for ( i=0;i<=t;i++)      //цикл будет выполнятся пока i<t t-время паузы
         {
         Delay_ms(1);          //Пауза в 1 мс
         if (knopka1 == 0 || knopka2 == 0 ) break;   //Проверка переключателей
         }                           //если кнопки нажимались, пауза прерывается
}

Кроме того, пока программа просматривает состояние прерывания (есть оно или нет) дальнейший код программы не исполняется, так как МК занят.

Рассмотрим второй способ. В mikroc для обработки прерываний есть специальная функция interrupt(). Обращение к данной функции происходит лишь в том случае, когда появляется прерывание, причем если прерывания нет, то выполняется основной код программы без траты времени на отслеживание состояния прерываний как в первом случае.

При использовании функции interrupt() МК будет исполнять программу следующим образом:
Исполняется программа. Если появляется прерывание, исполнение программы останавливается и исполняется код описанный в функции прерывания interrupt(). После того как прерывание обработано, основная программа продолжает исполняться с того места, где остановилась в момент возникновения прерывания.

Рассмотрим подробнее функцию interrupt(). Так как она не возвращает никакого значения, то инициализировать ее нужно следующим образом:

void interrupt(){

//код для обработки прерывания

}

Перед использованием функции обработки прерываний, нужно разрешить сами прерывания и прописать в программе, когда она будет вызываться. Для этого служит регистр (команда) INTCON. Данная команда содержит 8 бит, в которые входят биты разрешения и флаги.
Бит разрешения указывает на конкретный вид прерываний. Флаг же говорит о состоянии прерывания (если он равен 1, то прерывание есть, если 0, то прерывания нет).

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

Вырезка из мануала на семейство PIC16F

Конфигурирование прерываний начинается с бита GIE, если на его месте стоит 0, значит, все прерывания запрещены, не зависимо от того, что будет стоять дальше. Следовательно, ставим 1, разрешая прерывания. Затем устанавливаем нужные нам прерывания. Пока лезть в дебри не будем, и рассмотрим пример на основе того, что уже знаем: установим единицу в ячейку соответствующую RBIE, тем самым разрешаем прерывания по изменению сигнала на выводах RB4, RB5, RB6 и RB7. К данному биту относится флаг RBIF (бит 0). На его месте ставим 0 – прерывания в момент инициализации нет (когда оно будет значение RBIF станет 1). Таким образом, получаем следующее:

INTCON=0b10001000;

Ну а теперь практика.

int i;                                     // i типа integer
void interrupt ()                    // Функция обработки прерываний
{
 if(INTCON.RBIF){                 // Если прперывание произошло на выводах порта B, а именно
  if(PORTB.F4==1)                // если прерывание произошло на 4 выводе порта В, то
    {
      PORTB=0b00000111;      // на RB0, RB1 и RB2 ставим 1
      Delay_ms(1000);            // Пауза
      PORTB=~PORTB;            // Инвертируем выводы порта В
    }
  INTCON.RBIF = 0;               // Обнуляем флаг (завершаем прерывание)
 }
}

void main() {                       // Главная функция
TRISB=0b11111000;            // RB0, RB1 и RB2 на выход, остальные на вход
PORTB=0b00000000;           // Устанавливаем на выходах нули
INTCON=0b10001000;

  while(1){                           // Бесконечный цикл
    PORTB=0b00000001;                 // На RB0 1 на остальных нули
    for(i=0;i<500;i++)asm{NOP};   // Пауза
    PORTB=0b00000010;                 // На RB1 1 на остальных нули
    for(i=0;i<500;i++)asm{NOP};   // Пауза
    PORTB=0b00000100;                 // На RB2 1 на остальных нули
    for(i=0;i<500;i++)asm{NOP};   // Пауза
  }
}

Сначала работает бегущий огонь. При возникновении прерывания на RB4, работа огня приостанавливается и выполняется функция interrupt() – загораются все три светодиода на 1000 мс.

Скрин из протеуса при симулировании:

 

Как видно из работы программы в протеусе (видео ниже), прерывание возникает при изменении низкого сигнала на высокий (с 0 на 1). Таким образом было составлено две схемы.

В первой прерывание возникает сразу при нажатии кнопки, так как на RB4 низкий уровень (0 – синий квадратик), а стал высокий(1), т.е. прерывание возникает при замыкании контактов кнопки.

Во втором же случае, при не нажатой кнопке на выводе RB4 уже установлена единица и при нажатии на кнопку будет переход с высокого уровня на низкий, но ничего не произойдет, так как прерывание будет только в обратном случае, т.е. при отпускании кнопки. Следовательно, во второй схеме прерывание будет появляться при размыкании контактов кнопки или, например, разрыве провода, который можно включить вместо кнопки.

Симуляция работы в протеусе:

Так же любые вопросы можно обсудить на форуме.

Авторизоваться, чтобы комментировать