Контроллер
прерываний (VIC) микроконтроллеров серий 23XX и 24XX содержит в своём составе 16
так называемых слотов прерываний. В случае возникновения прерываний от
нескольких слотов одновременно, VIC обработает их по очереди в соответствии с
уровнем приоритетности – первым прерывание от слота 1 и далее в порядке
возрастания их порядковых номеров. К
каждому из 16 слотов можно подключить один из 32 источников прерываний,
назначив, таким образом, приоритет их
обработки.
Названия
и порядковые номера источников прерываний приведены:
- на рисунке >> Interrupt_sources.jpg;
-
в заголовочном файле >> irq.h;
- LPC23XX UserManual (раздел 7.5 "Interrupt sources”) [1];
- LPC24XX UserManual (раздел
7.5 "Interrupt sources”) [2].
Подключение
источника прерывания к VIC, назначение
приоритета и адреса функции-обработчика
Для
подключения источника прерывания N к слоту M, достаточно в регистр приоритета
источника VICVectPriorityN записать номер слота – M. Фактически это означает
– назначить прерыванию N приоритет M. В
примерах для Keil на серию LPC23XX
назначение приоритета происходит в функции install_irq () передачей параметров
IntNumber – номер источника прерывания (N) и Priority – приоритет прерывания
(M):
DWORD install_irq ( DWORD IntNumber, void
*HandlerAddr, DWORD Priority );
Адрес
функции-обработчика прерывания от источника N, на который отправится программа для обработки прерывания от назначенного ему слота, должен быть записан в
регистр VICVectAddrN. В функцию
install_irq этот адрес передаётся параметром *HandlerAddr, где HandlerAddr –
имя функции-обработчика.
Функция
install_irq вместо имён регистров VICVectAddr и VICVectPriority использует их косвенную
адресацию относительно базового адреса VIC:
DWORD install_irq( DWORD IntNumber, void *HandlerAddr,
DWORD Priority )
{
DWORD *vect_addr; // Указатель на VICVectAddr (адрес обработчика данного
прерывания)
DWORD *vect_cntl; // Указатель на VICVectPriority (приоритет данного прерывания)
VICIntEnClr = (1<<IntNumber); // Запретить прерывание с номером IntNumber
if ( IntNumber
>= VIC_SIZE ) {return ( FALSE );}
else
{
/*
Устанавливаем указатель на адрес регистра VICVectAddr для прерывания IntNumber */
vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + IntNumber*4);
/* Устанавливаем указатель на адрес регистра
VICVectPriority для прерывания IntNumber */
vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_CNTL_INDEX + IntNumber*4);
/* Записываем в VICVectAddr адрес обработчика
прерывания IntNumber */
*vect_addr = (DWORD)HandlerAddr; /*
set interrupt vector */
/* Записываем в VICVectPriority приоритет
для прерывания IntNumber */
*vect_cntl
= Priority;
VICIntEnable
= 1 << IntNumber; // Разрешить
прерывание с номером IntNumber
return(
TRUE );
}
}
Благодаря этому инициализацию VIC для
любого прерывания можно провести простым изменением передаваемых функции
параметров.
После возникновения прерывания от слота адрес обработчика
назначенного ему источника из регистра VICVectAddrN копируется в общий регистр
адреса прерывания VIC – VICAddress, содержимое которого для перехода на
обработчик загружается в программный счётчик PC микроконтроллера.
Разрешение и запрет
прерываний
Для разрешения и
запрета прерывания от любого из 32 источников, необходимо записать единичный
бит в соответствующую позицию регистров VICIntEnable и VICIntEnClr. Запись
нулевых битов в указанные регистры не оказывает никакого действия, благодаря этому
вместо операции чтение-модификация-записть можно использовать простую запись в регистры:
VICIntEnable = 1 << UART0_INT; // Разрешить прерывание от UAT0.
VICIntEnClr = 1 << UART0_INT; // Запретить прерывание от UAT0.
Рассмотрим
конкретный пример – прерывания от TIMER0 с интервалом в 1 секунду.
Проинициализируем
таймер 0, регистр сравнения T0MR на 1 секунду, VIC на прерывания от T0 с
наивысшим приоритетом 1:
void
T0Init (void)
{
T0MCR |= (1<<0)|(1<<1); //
При совпадении генерировать прерывания и сбрасывать счётчик.
T0TCR
|= (1<<0); //
Разрешить работу таймера.
// При частоте ядра 57.6 МГц и
делителе шины 1/2, для частоты 1Гц регистр сравнения:
T0MR0=57600000/2;
// Настроить прерывание TIMER0_INT с приоритетом 1:
install_irq(TIMER0_INT,
(void *)TIMER0_Handler, 1 );
}
Функция обработчика прерывания TIMER0:
void TIMER0_Handler (void) __irq
{ T0IR |=(1<<0); //
Сбросить флаг прерывания. FIO1PIN ^= (1<<26); //
Сменить состояние светодиода на противоположное. VICVectAddr = 0; // Acknowledge Interrupt.
}
Фон программы:
int main (void)
{
SCS |= 0x01; // Включение быстрого доступа
к портам 0 и 1. FIO1DIR |= (1<<26); // Вывод светодиода
на выход.
FIO1SET |= (1<<26); // Погасить светодиод. T0Init (); // Инициализировать таймер0. while (1) {}
} Программа
в виде готового проекта для LPC2368 и µVision3
>> Timer0_1sec.rar
Приложения: 1. LPC23XX_UserManual.pdf
2. LPC24XX_UserManual.pdf
|