Прерывания в PIC16F628A
Прерывания – это очень полезная вещь в микроконтроллерах, она позволяет отвлечь контроллер от выполнения основной программы, на подпрограмму при срабатывании одного из прерываний. Прерывания могу быть как внешними: изменения уровня сигнал на одном из выводов или приход сообщения в USART … , так и внутренними: при переполнении таймера или совершения операции работы с внутренней EPROM памятью …
Немного теории
За прерывания в микроконтроллерах среднего семейства PIC отвечает регистр INTCON. Он содержит биты разрешения прерываний и флаги, что прерывание произошло.
Для начала работы с прерываниями в регистре INTCON нужно разрешить прерывания вообще, для этого бит GIE устанавливаем в единицу. Также нужно разрешить прерывания от нужных источников.
Несколько источников прерываний содержится уже в регистре INTCON, другая часть содержится в регистрах PIE/PIR, для их использования нужно в регистре INTCON разрешить прерывания от периферийных модулей – бит PEIE. Все возможные источники прерываний среднего семейства PIC микроконтроллеров представлены на следующем рисунке:
Для PIC16F628A регистр PIE выглядит следующим образом:
После срабатывание прерывания текущая выполняемая задача (функция) в микроконтроллере приостанавливается, и управление передаётся специальной функции — interrupt isr, которая отвечает за обработку прерываний, после обработки прерывания выполнение приостановленной функции продолжается. В этой функции можно опросив флаги возможных прерываний – определить, какое из них произошло. Обработчик прерываний должен содержать как можно меньше операций, что бы как можно быстрее вернуть управление главной функции, если конечно там что-то важное может обрабатываться.
Практика
Рассмотрим 2 варианта прерываний: по изменению уровня сигнала на RB7 и по приходу байта в модуль USART.
Соберём небольшую схему, которая будет содержать: кнопку, приёмник USART и 2 светодиода индикации.
Задача: загорание на некоторое время первого светодиода по пришествии байта в USART и загорание ненадолго второго светодиода по нажатию кнопки.
Для начала нужно эти источники прерываний инициализировать:
INTCON=0b11001000; // Разрешены прерывания, разрешены прерывания от периферии, разрешить прерывания от PORTB< 4:7> RCIE=1; // Разрешить прерывания от приёмника USART
Ниже приведена функция обработки прерываний — interrupt isr . В ней нужно опросить флаги возможных источников прерываний, выполнить нужные действие и в заключение сбросить флаги произошедших прерываний. Если эти флаги не сбросить то по прекращению обработки этой функции управление вернётся не к приостановленной функции, а обратно к обработчику прерываний.
void interrupt isr(void) { if(RBIF) // Обрабатываем прерывания на входах PORTB< 4:7> { if(RB7==0) { LP_ON; LP_time=1000; } RBIF=0; // сбрасываем флаг } if(RCIF) // Обрабатываем прерывания от приёмника USART { getch_Usart(); LU_ON; LU_time=1000; } }
В свою очередь в функции проверяем, какое прерывание произошло, включаем соответствующий светодиод и задаём время, которое он будет гореть, далее сбрасываем флаги произошедших прерываний.
т.к. обрабатываем только приход байта в USART, то заодно в обработчике прерываний очищаем входной буфер этого модуля, так же имея 4 входа в обработчике RBIF, проверяем только нужный.
В главной функции в бесконечном цикле расположены счётчики, которые отсчитывают, сколько времени горят светодиоды, а после гасят их.
Скачать исходники, прошивку, проект в proteus
Статья обновлена 02.07.2011
Alex_EXE | 04.02.2011 | Микроконтроллеры |
Роман пишет 27.06.2011 в 20:26 #
Небольшое замечание ради справедливости ) . Флаг прерывания RCIF программно сбросить нельзя , он сбрасывается сам после чтения байта из буфера . В данном случае после getch_Usart(); он уже сброшен . И строка : RCIF=0; не нужна , тем более бесполезна см.выше .
Alex_EXE пишет 02.07.2011 в 04:12 #
Для надёжности сбрасывал 🙂 . Убрал, спасибо.
ИЛЬЯ пишет 14.08.2013 в 19:57 #
Скажите а как включить внешний кварц,или как изменять биты регистра управления?
Виктор пишет 26.09.2016 в 17:27 #
Может быть я ошибаюсь , но :
Разве так, как указано в примере RBIF возможно сбросить?
Мне казалось, что сперва необходимо программе прочитать PORT B, а затем сбрасывать флаг.
if(RBIF)
{
if(RB7==0)
{
LP_ON;
LP_time=1000;
}
PORT B ; // читаем порт
RBIF=0; // сбрасываем флаг
}
Medved пишет 18.01.2022 в 08:51 #
Смею заметить, что не все компиляторы С самостоятельно выполняют сохранение регистров при уходе в обработку прерывания. Т.е. в программе обработки прерывания перво-наперво мы должны выполнить сохранение важных регистров (цитата из даташита):
——————————————————
— сохранить регистр W;
— сохранить регистр STATUS в банке памяти 0;
— выполнить код обработки прерываний;
— восстановить регистр STATUS (с восстановлением текущего банка);
— восстановить регистр W.
……………..
— В конкретных приложениях может потребоваться сохранять и другие специальные регистры (например, FSR,
PCLATH).
————————————————————-
У вас же об этом не упомянуто даже вскользь. Т.е. надо читать доки на сам компилятор, умеет ли он по умолчанию в сохранение регистров или нет.
Проясню ситуацию — я не гуру, я просто самостоятельно разбираюсь в программировании МК PIC для решения нужной мне задачи, и на эти грабли с регистрами я только что звонко наступил.
Alex_EXE пишет 21.01.2022 в 07:51 #
Ассемблер конечно хорошо, но тут приподнялись на уровень повыше, на C, что бы облегчить себе работу и уйти от прямой работы с регистрами контроллера.
В программировании PIC контроллеров я применял компилятор HITECH, какой уже не вспомню. Компилятор, который идёт в комплекте с MPLAB X IDE должен это поддерживать.
В статье приведенное замечание не упоминается, т.к. видимо об него, когда самостоятельно изучал в свой время PIC контроллеры не споткнулся и не подозревал о нём.