цифровая электроника
вычислительная техника
встраиваемые системы

 

Микроконтроллеры семейства PIC32. Прерывания.

Автор: Mike(admin) от 29-08-2013, 13:18

Говоря простым языком, прерывание это какое-либо внешнее или внутреннее событие, требующее от процессора немедленной реакции на него. При этом выполнение текущей программы на время завершается, процессор сохраняет значения служебных регистров, входит в обработчик прерывания, обрабатывает это прерывание, по выходу восстанавливает служебные регистры и вновь возвращается к месту выполнения основной программы, на котором его прервали. Вообще, в ядре MIPS (а в PIC32 используется именно оно) все прерывания попадают в категорию исключений. К исключениям здесь относится все, что «мешает» нормальной работе основной программы. Например, выполнение процедуры сброса – исключение, ошибка при делении – исключение, и, конечно же, различные прерывания, как внутренние, так и внешние, тоже исключения.


Прерывания


В PIC32 имеются 96 источников прерывания и 64 векторов прерывания. Это значит, что несколько источников могут быть «приписаны» к одному вектору, то есть указателю к функции обработчика прерывания. Нужно учитывать, что механизм прерываний поддерживает одновекторный и мультивекторный режимы. При одновекторном режиме в таблице исключений будет представлен один вектор для прерываний, и, следовательно, будет лишь один обработчик прерываний. Многовекторный режим предоставляет возможность работать с прерываниями в собственных обработчиках, тем самым повышая гибкость и читабельность программы. Давайте начнем разбирать это на примерах и потихоньку вникать во все тонкости прерываний.


Обработчик прерываний может быть представлен в коде следующим образом:



void __attribute__ ((interrupt(ipl3),vector(0)))
InterruptHandler(void)
{
/*Обработка прерывания*/
}

Иличерездирективу #pragma:



#pragma interrupt InterruptHandler ipl5 vector 0
void InterruptHandler(void)
{
/*Обработка прерывания*/
}

Здесь iplx задает приоритет прерывания, x может быть от 0 до 7 (7 самый высокий приоритет). vector 0 – номер вектора прерывания.


Правда есть еще более удобный способ записи функции обработчика с помощью макроса __ISR:



void __ISR(0, ipl1) InterruptHandler (void)
{
/*Обработка прерывания*/
}

Как мы видим, обработчик прерывания похож на простую функцию и может объявляться, где и остальные функции (до main(), после main(), в другом файле, но с обязательной ссылкой на этот файл). Но в отличие от других функций он ничего не принимает и ничего не возвращает (как видно, везде у него void), а также он не может быть вызван из другой функции.


Итак, как же нам организовать прерывание, допустим, от таймера T2? Для начала настроим сам таймер и его прерывание:



OpenTimer2(T2_OFF | T2_SOURCE_INT | T2_PS_1_256, 0xFFFF);
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_3 | T2_INT_SUB_PRIOR_0);

T2_INT_PRIOR_3 – устанавливает приоритет прерывания,


T2_INT_SUB_PRIOR_0 – устанавливает подприоритет прерывания, хотя это сейчас и не нужно, но в случае, если сработали два прерывания с одинаковым приоритетом, то привелегии получит тот, у кого подприоритет будет выше. Подприоритет изменяется от 0 до 3.


Помимо такой записи библиотека plib предлагает следующие макросы:


mT2SetIntPriority(3) – устанавливает приоритет для прерывания от T2.


mT2IntEnable(1) – разрешает прерывание.


mT2ClearIntFlag() – очищает флаг прерывания (в обработчике пригодиться).


И нужный для разрешения одновекторных прерываний макрос INTEnableSystemSingleVectoredInt().


Обработчик прерываний будет выглядеть так:



void __ISR(0, ipl3) InterruptHandler(void)
{
if ((PORTD & 0x0080)==0x0080) {PORTDCLR=0x0080;}
else {PORTDSET=0x0080;}

mT2ClearIntFlag();
}

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


В основной программе с помощью двух кнопок мы либо включаем таймер, либо отключаем:



if (PORTG & 0x40)
{
WriteTimer2(0);//TMR2=0;
T2CONSET = 0x8000;
PORTDSET=0x0040;
}

if (PORTG & 0x80)
{
WriteTimer2(0);//TMR2=0;
T2CONCLR = 0x8000;
}

Весь код:



#include <p32xxxx.h>
#include <plib.h>

// настраиваем частоту
#pragma config FNOSC=XTPLL
#pragma config FPLLIDIV=DIV_2, FPLLMUL=MUL_20, FPLLODIV=DIV_1
#pragma config FWDTEN=OFF // отключаем сторожевой таймер

// Обработчик прерывания
void __ISR(0, ipl3) InterruptHandler(void)
{
if ((PORTD & 0x0080)==0x0080) {PORTDCLR=0x0080;}
else {PORTDSET=0x0080;}

mT2ClearIntFlag();
}

//основная функция
main ()
{

//настройка кэша
mCheConfigure(CHE_CONF_WS2 | CHE_CONF_PF_ALL | CHE_CONF_COH_INVUNL | CHE_CONF_DC_NONE);

CheKseg0CacheOn();
mBMXDisableDRMWaitState();


//Настройка портов

TRISD=0x0000;
PORTD=0x0000;

TRISG=0x03C0;
PORTG=0x0000;

//настройка таймера
OpenTimer2(T2_OFF | T2_SOURCE_INT | T2_PS_1_256, 0xFFFF);
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_3 | T2_INT_SUB_PRIOR_0);

//одновекторный режим
INTEnableSystemSingleVectoredInt();

//------------------------------------------------
//бесконечный цикл
do
{

if (PORTG & 0x40)
{
WriteTimer2(0);//TMR2=0;
T2CONSET = 0x8000;
PORTDSET=0x0040;
}

if (PORTG & 0x80)
{
WriteTimer2(0);//TMR2=0;
T2CONCLR = 0x8000;
PORTDCLR=0x0040;
}
}
while(1); // закрытие бесконечного цикла

} // закрытие функции main

В общем, все довольно просто … с одним прерыванием. В одновекторном режиме можно обрабатывать и несколько прерываний, но тогда в обработчике нужно будет вручную организовать вложенность прерываний: каждый раз при заходе в обработчик проверять, установлен ли флаг определенного прерывания, и если да, то обрабатывать его. В случае большого количества источников прерываний это становится крайне неудобно, к тому же возрастает нагрузка на процессор. Все проблемы решает многовекторный режим прерываний!


В этом режиме обязательно нужно учитывать номер вектора прерывания (список прерываний и соответствующие им номера векторов приведены в даташите на контроллер, стр. 112-114). Теперь функция обработчика будет не одна, их будет столько, сколько векторов прерываний мы ожидаем увидеть в нашей программе. Итак, добавим в программу таймер T3 и соответствующий обработчик.



OpenTimer3(T3_OFF | T3_SOURCE_INT | T3_PS_1_256, 0x1FFF);
ConfigIntTimer3(T3_INT_ON | T3_INT_PRIOR_5 | T3_INT_SUB_PRIOR_0);

void __ISR( _TIMER_3_VECTOR, ipl5) T3InterruptHandler( void)
{
if ((PORTD & 0x0040)==0x0040) {PORTDCLR=0x0040;}
else {PORTDSET=0x0040;}

mT3ClearIntFlag();
}

Обработчик таймера T2 будет выглядеть следующим образом:



void __ISR( _TIMER_2_VECTOR, ipl3) T2InterruptHandler(void)
{
if ((PORTD & 0x0080)==0x0080) {PORTDCLR=0x0080;}
else {PORTDSET=0x0080;}

mT2ClearIntFlag();
}

Незабываем включить мультивекторный режим:



INTEnableSystemMultiVectoredInt();

По первой кнопке будем включать T2, по второй – T3, по третьей отключать оба таймера:



if (PORTG & 0x40)
{
WriteTimer2(0);//TMR2=0;
T2CONSET = 0x8000;

}

if (PORTG & 0x80)
{
WriteTimer3(0);//TMR3=0;
T3CONSET = 0x8000;
}

if (PORTG & 0x0100)
{
WriteTimer2(0);//TMR2=0;
WriteTimer3(0);//TMR3=0;
T2CONCLR = 0x8000;
T3CONCLR = 0x8000;
}

Весь код выглядит так:



#include <p32xxxx.h>
#include <plib.h>

// настраиваем частоту
#pragma config FNOSC=XTPLL
#pragma config FPLLIDIV=DIV_2, FPLLMUL=MUL_20, FPLLODIV=DIV_1
#pragma config FWDTEN=OFF // отключаем сторожевой таймер

// Обработчики прерываний

void __ISR( _TIMER_2_VECTOR, ipl3) T2InterruptHandler(void)
{
if ((PORTD & 0x0080)==0x0080) {PORTDCLR=0x0080;}
else {PORTDSET=0x0080;}

mT2ClearIntFlag();
}

void __ISR( _TIMER_3_VECTOR, ipl5) T3InterruptHandler( void)
{
if ((PORTD & 0x0040)==0x0040) {PORTDCLR=0x0040;}
else {PORTDSET=0x0040;}

mT3ClearIntFlag();
}

//основная функция
main ()
{

//настройка кэша
mCheConfigure(CHE_CONF_WS2 | CHE_CONF_PF_ALL | CHE_CONF_COH_INVUNL | CHE_CONF_DC_NONE);

CheKseg0CacheOn();
mBMXDisableDRMWaitState();


//Настройка портов

TRISD=0x0000;
PORTD=0x0000;

TRISG=0x03C0;
PORTG=0x0000;

//настройка таймеров
OpenTimer2(T2_OFF | T2_SOURCE_INT | T2_PS_1_256, 0xFFFF);
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_3 | T2_INT_SUB_PRIOR_0);

OpenTimer3(T3_OFF | T3_SOURCE_INT | T3_PS_1_256, 0x1FFF);
ConfigIntTimer3(T3_INT_ON | T3_INT_PRIOR_5 | T3_INT_SUB_PRIOR_0);

//многовекторный режим
INTEnableSystemMultiVectoredInt();

//------------------------------------------------
//бесконечный цикл
do
{

if (PORTG & 0x40)
{
WriteTimer2(0);//TMR2=0;
T2CONSET = 0x8000;

}

if (PORTG & 0x80)
{
WriteTimer3(0);//TMR3=0;
T3CONSET = 0x8000;
}

if (PORTG & 0x0100)
{
WriteTimer2(0);//TMR2=0;
WriteTimer3(0);//TMR3=0;
T2CONCLR = 0x8000;
T3CONCLR = 0x8000;
}
}
while(1); // закрытие бесконечного цикла

} // закрытие функции main


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


© digitrode.ru


Теги: PIC32, прерывания



   Благодарим Вас за интерес к информационному проекту digitrode.ru.
   Если Вы хотите, чтобы интересные и полезные материалы выходили чаще, и было меньше рекламы,
   Вы можее поддержать наш проект, пожертвовав любую сумму на его развитие.


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

Комментарии:

Оставить комментарий