Funkcje API w obsłudze przerwań
Aby system mikroprocesorowy poprawnie radził sobie z zewnętrznymi zdarzeniami muszą być one obsługiwane za pomocą przerwań. Ma to szczególne znaczenia dla zadań krytycznych, w których nie może być mowy o zbyt dużych opóźnieniach w wykonaniu owego zadania, ani tym bardziej o pominięciu zdarzenia. Często nie zauważa się tego problemu, ponieważ wydaje się, że na przykład cykliczne sprawdzanie w pętli stanu danego wejścia jest wystarczające. Niestety takie podejście prędzej czy później powoduje generowanie błędów w pracy systemu. Jeżeli mamy do czynienia z projektem hobbistycznym, to nie jest to specjalnie dotkliwe, jednakże w przypadku rozwiązań komercyjnych nie można już sobie na takie błędy pozwolić.
W mikrokontrolerach z rdzeniem Cortex-M3 za obsługę przerwań odpowiada sprzętowy kontroler przerwań (NVIC). Dzięki niemu podprogram, który ma się wykonać po nadejściu przerwania jest wywoływany szybciej, a sama implementacja obsługi przerwań jest łatwiejsza, niż miało to miejsce w przypadku rdzeni ARM7 i ARM9.
List. 1. Przykładowy program wykorzystujący przerwanie zewnętrzne
int main(void) { RCC_Conf(); #ifdef VECT_TAB_RAM NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif /* Wybranie grupy priorytetów */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* Konfiguracja NVIC i wlaczenie obslugi przerwania */ NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQChannel; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); GPIO_Conf(); /* Poinformowanie uC o zrodle przerwania */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9); /* Bedzie generowane przerwanie na zboczu opadajacym na EXTI_Line9 */ EXTI_InitStruct.EXTI_Line = EXTI_Line9; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); /* Wygenerowanie przerwania EXTI_Line9 programowo */ EXTI_GenerateSWInterrupt(EXTI_Line9); while (1); } /******* zawartosc pliku stm32f10x.it.c *******/ /* Funkcja oblugi przerwan zewnetrznych od 9 do 5 */ void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line9) != RESET) { /* Przerwanie wywoluje zmiane stanu wyprowadzenia */ GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction) ((1-GPIO_ReadOutputDataBit( GPIOC, GPIO_Pin_6)))); EXTI_ClearITPendingBit(EXTI_Line9); } }
Fragment programu, który działa w oparciu o zewnętrzne przerwanie przedstawiono na list. 1. Najważniejsza jest właściwa konfiguracja MCU, natomiast program, jaki ma być docelowo wywołany umieszcza się w pliku stm32f10x_it.c, który, przy wykorzystaniu pakietów CrossWorks, Keil lub IAR, znajduje się domyślnie w projekcie.
System ustalania priorytetów w mikrokontrolerach wyposażonych w rdzeń Cortex M3 jest rozdzielony na dwie części. Przerwania mają przypisany priorytet główny, tzw. Preemption prioritet, ponadto ustalany jest dodatkowy poziom podpriorytetów (subprioritet).
Rys. 1. Relacje pomiędzy przerwaniami
Relacje pomiędzy nimi przedstawiono na rys. 1. Taki podział ma uzasadnienie w działaniu: gdy obsługiwane jest w danej chwili przerwanie i nadejdzie zgłoszenie od innego przerwania, to NVIC porównuje priorytety główne, i jeżeli nowe przerwanie dysponuje wyższym priorytetem, to następuje jego wywłaszczenie i ono będzie teraz wykonywane. Wartości podpriorytetów mają jedynie znaczenie w momencie, gdy wystąpią dwa przerwania o takim samym priorytecie głównym w tym samym czasie. W takiej sytuacji w pierwszej kolejności zostanie obsłużone przerwanie o wyższym podpriorytecie. Programista wybierając tzw. grupy priorytetów (Priority Group) może ustalać relację pomiędzy liczbą poziomów priorytetów głównych, a liczbą podpriorytetów w zależności od wymagań danej aplikacji. Takich „zestawów” mikrokontrolery STM32 obsługują pięć (od 0 do 4). Jeżeli w docelowym urządzeniu przewidujemy konieczność zastosowania większej liczby zagnieżdżonych przerwań, to w takim wypadku należy wybrać odpowiednio wysoką grupę priorytetów. Zasada jest taka, że im mniejszy numer grupy priorytetów, tym mniej jest do dyspozycji priorytetów głównych, a więcej podpriorytetów (tab. 1).
Tab. 1. Grupy priorytetów
Grupa | Preempiton priority | Subpriority |
NVIC_PriorityGroup_0 | 0 bitów | 4 bity |
NVIC_PriorityGroup_1 | 1 bit | 3 bity |
NVIC_PriorityGroup_2 | 2 bity | 2 bity |
NVIC_PriorityGroup_3 | 3 bity | 1 bit |
NVIC_PriorityGroup_4 | 4 bity | 0 bitów |
Wynika z niej, że w omawianym przypadku z list. 1 jest wybrana opcja zawierająca dwa priorytety główne (preemption priority), każdy dysponuje ośmioma podpriorytetami (subpriority). Sumarycznie otrzymujemy w ten sposób 16 możliwych poziomów priorytetów, czyli tyle, ile obsługują mikrokontrolery z rodziny STM32.
Po wybraniu grupy priorytetów należy odpowiednio ustawić parametry wykorzystywanego przerwania. W tym przykładzie będzie to przerwanie pochodzące od przycisku użytkownika na płycie uruchomieniowej, czyli wyprowadzenie PB9. Ponieważ jest to przerwanie zewnętrzne o numerze 9, to trzeba poinformować NVIC o tym, że będzie ono wykorzystywane. Dokonujemy tego modyfikując pole NVIC_IRQChannel struktury inicjującej. Po tej czynności należy ustalić priorytety dla tego konkretnego przerwania. Zarówno priorytet główny, jak i podpriorytet zostają ustawione na zera, a co za tym idzie, to przerwanie ma pierwszeństwo w obsłużeniu. Gdy wszystkie pola struktury są już wypełnione, jest ona przekazywana, podobnie jak miało to miejsce w przypadku konfiguracji GPIO, przez referencję do funkcji inicjującej. W ten sposób NVIC jest przygotowany na przyjęcie przerwania, należy jeszcze skonfigurować samo przerwanie zgodnie z wymaganiami.
Wyprowadzenie PB9 jest przyporządkowane do zewnętrznego przerwania o numerze 9 (EXTI_Line9), stąd informujemy o tym MCU wypełniając pole EXTI_Line. Kolejne dwie linie kodu definiują zachowanie wyprowadzenia. Będzie ono pracować w trybie przerwania oraz będzie reagować na zbocze opadające. W chwili, gdy takie zdarzenie wystąpi, mikrokontroler przystępuje do wykonywania funkcji EXTI9_5_IRQHandler.