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

 

Микроконтроллеры семейства 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, прерывания




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

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

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