Wstęp
Przerwanie jest to pewien sygnał, który zostaje zgłoszony do mikrokontrolera w momencie wyłapania przerwania. Na czas obsługi przerwania układ zawiesza wykonywanie pozostałych operacji, po czym przechodzi do wykonania operacji w przerwaniu. Po jej wykonaniu mikrokontroler wraca do normalnej pracy. W momencie wywołania sygnału układ zapisuje swój aktualny stan, po to by po powrocie do normalnej pracy zacząć działanie od przerwanej czynności.
Źródłami przerwania mogą być takie układu jak np. SPI, DMA, UART, TIMER, lub pewne układy zewnętrzne jakie zostały podłączone do mikrokontrolera.
Główną zaletą przerwań jest to, że pozwalają na wykonywanie określonych operacji w momencie kiedy jest to konieczne, bądź gdy chce tego użytkownik. Nie trzeba czekać aż dany stan na linii zostanie sprawdzony w czasie normalnej pracy.
NVIC
NVIC (ang. Nested Vectored Interrupt Controller) jest to tzw. kontroler przerwań sprzętowych. Mikrokontroler zastosowany w opisywanej wersji Nucleo pozwala na obsługę 43 maskowalnych kanałów przerwań. Możliwe jest ustawienie 16-stu wartości priorytetów przerwań. Najważniejszym jest wartość 0. Czyli jeśli zostaną zgłoszone dwa przerwania, gdzie każde będzie miało inną wartość, wtedy zostanie wykonane to z niższym (bliższym zero) priorytetem. Oznacza to, że nawet jeśli jest już wykonywane przerwanie o wyższym numerze, to zostanie przerwane jego działanie na rzecz przerwania o numerze bliższym 0.
Jeśli ważność przerwań jest taka sama, to kolejność zostaje określona przez podpriorytet.
Występują też przerwania bardzo ważne, których priorytetu nie da się zmienić, i zawsze będą miały pierwszeństwo w wykonywaniu. Do nich należą:
- Reset - zresetowanie układu mikrokontrolera (Podpriorytet -3);
- NMI - przerwanie niemaskowalne (Podpriorytet -2);
- HardFault - wystąpienie błędu lub przerwania, którego nie da się obsłużyć (Podpriorytet -1);
W trakcie programowania są do wyboru następujące opcje ustawiania, które są zawarte w strukturze NVIC_InitTypeDef:
- NVIC_IRQChannel - określa źródło przerwania (kanał);
- NVIC_IRQChannelPreemptionPriority - określa priorytet grupowy;
- NVIC_IRQChannelSubPriority - określa podpriorytet;
- NVIC_IRQChannelCmd - ustawia włączenie danego przerwania;
EXTI
EXTI (ang. External Interrupt) są to zewnętrzne źródła przerwania. Każda z dostępnych linii GPIO może zostać skonfigurowana tak aby zgłaszała przerwanie.
Dostępne są następujące możliwości powiązania pinów z funkcjami obsługi przerwania:
- Pin 0 - EXTI0_IRQHandler();
- Pin 1 - EXTI1_IRQHandler();
- Pin 2 - EXTI2_IRQHandler();
- Pin 3 - EXTI3_IRQHandler();
- Pin 4 - EXTI4_IRQHandler();
- Piny od 5 do 9 - EXTI9_5_IRQHandler();
- Piny od 10 do 15 - EXTI10_15_IRQHandler();
Struktura EXTI składa się z następujących parametrów:
- EXTI_Line - deklaracja linii, która zostanie wykorzystana do obsługi przerwania;
- EXTI_Mode - wybranie trybu obsługi danego zgłoszenia. Można wybrać zewnętrzne bądź wewnętrzne;
- EXTI_Trigger - jakie zbocze sygnału będzie wywoływało przerwanie rosnące, opadające czy oba;
- EXTI_LineCmd - włącza dany kanał przypisany do odpowiedniego przerwania zewnętrznego;
Program
Program będzie dosyć prosty, zaprezentuje w nim sposób inicjalizacji przerwania dla przycisku wbudowanego. Zostanie zapalona dioda bądź zgaszona dioda wbudowana. Jedno wciśnięcie przycisku powoduje zapalenie, drugie natomiast powoduje zgaszenie diody.
Poniżej przedstawiam całość programu wraz z komentarzem.
- #include "stm32f10x.h"
- #define PinDioda GPIO_Pin_5
- #define PinPrzycisk GPIO_Pin_13
- #define LineDioda GPIOA
- #define LinePrzycisk GPIOC
- #define ClockGPIOA RCC_APB2Periph_GPIOA
- #define ClockGPIOC RCC_APB2Periph_GPIOC
- void NVIC_EXTIInit(void);
- void GPIOInit(void);
- void EXTI15_10_IRQHandler(void);
- volatile int zapal = 0;
- int main(void)
- {
- GPIOInit();
- NVIC_EXTIInit();
- while (1) {
- }
- }
- void NVIC_EXTIInit(void)
- {
- EXTI_InitTypeDef EXTIInit;
- NVIC_InitTypeDef NVICInit;
- //Wybór konfigurowanego przerwania
- NVICInit.NVIC_IRQChannel = EXTI15_10_IRQn;
- //Priorytet grupowy
- NVICInit.NVIC_IRQChannelPreemptionPriority = 0x00;
- //Podpriorytet
- NVICInit.NVIC_IRQChannelSubPriority = 0x00;
- //Włączenie obsługi
- NVICInit.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVICInit);
- //Ustawienie źródła przerwania
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13);
- //EXTI_StructInit(&EXTIInit);
- //Wybór linii 13
- EXTIInit.EXTI_Line = EXTI_Line13;
- //Ustawienie generowanego przerwania, możliwe jest wybranie zdarzenia
- EXTIInit.EXTI_Mode = EXTI_Mode_Interrupt;
- //Wyzwolenie zboczem opadającym, bo przycisk zwarty do masy
- EXTIInit.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
- //Włączenie przerwania
- EXTIInit.EXTI_LineCmd = ENABLE;
- EXTI_Init(&EXTIInit);
- }
- //Obsługa przerwania
- void EXTI15_10_IRQHandler()
- {
- //Zidentyfikowanie źródła przerwania
- if (EXTI_GetITStatus(EXTI_Line13) != RESET)
- {
- //Podanie głównego kodu wykonywanego w procedurze obsługi przerwania
- //Jeśli przycisk został wciśnięty
- if ((GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0) && (zapal == 0))
- {
- GPIO_SetBits(GPIOA, GPIO_Pin_5);
- zapal = 1;
- }
- else if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0)
- {
- GPIO_ResetBits(GPIOA, GPIO_Pin_5);
- zapal = 0;
- }
- //Koniec wpisywania głównego kodu wykonywanego w przerwaniu
- //Wyzerowanie flagi obsługi przerwania
- EXTI_ClearITPendingBit(EXTI_Line13);
- }
- }
- //Inicjalizacja pinów dla przycisku i diody
- void GPIOInit(void)
- {
- GPIO_InitTypeDef GpioInit;
- RCC_APB2PeriphClockCmd(ClockGPIOA, ENABLE);
- RCC_APB2PeriphClockCmd(ClockGPIOC, ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
- GPIO_StructInit(&GpioInit);
- GpioInit.GPIO_Pin = PinDioda;
- GpioInit.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_Init(LineDioda, &GpioInit);
- GpioInit.GPIO_Pin = PinPrzycisk;
- GpioInit.GPIO_Mode = GPIO_Mode_IPU;
- GPIO_Init(LinePrzycisk, &GpioInit);
- }