Kurs podstawowy
Przerwania

Przerwania są jednym z podstawowych zagadnień związanych z mikrokontrolerami. Pozwalają wykonywać operacje tylko wtedy, gdy jest to konieczne. Czym one są i w jaki sposób ich używać? W tym artykule w najbardziej przystępny sposób omówię system przerwań.

 

 

 

Mikrokontrolery w swej strukturze posiadają wbudowane moduły wykonujące określone zadania. Pomimo, że są one częścią wewnętrznej struktury mikrokontrolera, traktować je należy jako moduły zewnętrzne. Mikrokontrolery sterują także zewnętrznymi urządzeniami, których stan przekazywany jest za pośrednictwem czujników. Moduły wewnętrzne, jak i urządzenia zewnętrzne wymagają obsługi w zależności od ich stanu. Ciągłe sprawdzanie poszczególnych ich stanu w programie głównym mocno spowolniłoby pracę całego układu. W tym celu powstał system przerwań umożliwiający komunikowanie się urządzeń zewnętrznych z mikrokontrolerem i umożliwiający mu selektywną obsługę każdego z nich. Zasada działania systemu przerwań opiera się na zgłaszaniu przez moduł, czy urządzenie żądania obsługi. Mikrokontroler przerywa wtedy bieżące zadanie i przystępuje do wykonania podprogramu odpowiedniego dla danego źródła przerwania. Mikrokontrolery AVR mają domyślnie zablokowaną obsługę przerwań po inicjalizacji. Aby ją aktywować należy dołączyć do programu bibliotekę interrupt.h oraz ustawić bit I w rejestrze SREG.

 

 

Rejestr SREG (Status Register - Rejestr statusów)

NR bitu 7 6 5 4 3 2 1 0
Nazwa bitu I T H S V N Z C
zapis/odczyt Z/O Z/O Z/O Z/O Z/O Z/O Z/O Z/O
Wartość domyślna 0 0 0 0 0 0 0 0

 

Modyfikację bitu I wykonuje się za pomocą instrukcji sei() oraz cli(). Instrukcja sei() ustawia bit I zapisując do niego wartość 1, natomiast instrukcja cli() zeruje go, wpisując do niego wartość 0. Ustawiony bit I oznacza globalne zezwolenie na przyjmowanie i obsługę przerwań, a wyzerowany, globalne zablokowanie przerwań.

Jak już wspomniałem istnieje wiele autonomicznych źródeł przerwań, które mogą zgłaszać żądania obsługi. Istnieje jednak możliwość, że w jednym cyklu zegarowym zgłoszone zostanie więcej niż jedno żądanie przerwania. W tym celu wprowadzono odgórnie priorytety przerwań.

 

Nr wektora Adres w pamięci programu Nazwa wektora Źródło Rodzaj przerwania
1 0x000   RESET Sygnał zewnętrzny, reset po włączeniu, reset Brown-out, reset Watch-dog, Reset JTAG AVR
2 0x002 INT0_vect INT0 Zewnętrzne przerwanie 0
3 0x004 INT1_vect INT1 Zewnętrzne przerwanie 1
4 0x006 INT2_vect INT2 Zewnętrzne przerwanie 2
5 0x008 TIMER2_COMP_vect TIMER2 COMP Porównanie w TIMER2
6 0x00A TIMER2_OVF_vect TIMER2 OVF Przepełnienie w TIMER2
7 0x00C TIMER1_CAPT_vect TIMER1 CAPT Przechwycenie w TIMER1
8 0x00E TIMER1_COMPA_vect TIMER1 COMPA Porównanie A w TIMER1
9 0x010 TIMER1_COMPB_vect TIMER1 COMPB Porównanie B w TIMER1
10 0x012 TIMER1_OVF_vect TIMER1 OVF Przepełnienie TIMER1
11 0x014 TIMER0_COMP_vect TIMER0 COMP Porównanie TIMER0
12 0x016 TIMER0_OVF_vect TIMER0 OVF Przepełnienie TIMER0
13 0x018 SPI_STC_vect SPI, STC Zakończenie transmisji przez SPI
14 0x01A USART_RXC_vect USART, RXC Zakończenie odbierania przez USART
15 0x01C USART_UDRE_vect USART, UDRE Pusty rejestr danych USART
16 0x01E USART_TXC_vect USART, TXC Zakończenie wysyłania przez USART
17 0x020 ADC_vect ADC Zakończenie przetwarzania przez ADC
18 0x022 EE_RDY_vect EE_RDY Stan gotowości EEPROM
19 0x024 ANA_COMP_vect ANA_COMP Komparator analogowy
20 0x026 TWI_vect TWI Interfejs I2C
21 0x028 SPM_RDY_vect SPM_RDY Gotowość na umieszczenie programu

Tab. 1. Wektory przerwań mikrokontrolera ATmega32.

 

Tabela 1 przedstawia wektory przerwań mikrokontrolera ATmega32. Każde przerwanie ma swój numer i odpowiadający mu priorytet. Im wyższy numer, tym niższy priorytet przerwania. Wyjaśnię jeszcze co to jest wektor przerwania. Jest to adres w pamięci programu, gdzie znajduje się podprogram obsługi przerwania. Dziwnym może się wydawać to stwierdzenie, gdyż adres wektora ma długość tylko 2 bajtów. Jest to zbyt mało na umieszczenie tam jakiegokolwiek podprogramu. Ma to związek z organizacją pamięci programu. W początkowym obszarze pamięci znajduje się tabela wektorów przerwań. Mikrokontroler po sygnale zerowania, a takim jest także włączenie zasilania rozpoczyna pracę od wykonania instrukcji umieszczoną pod adresem 0x000. Jest to adres wektora inicjalizacyjnego, który posiada najwyższy priorytet. Jego cechą charakterystyczną jest to, że nie jest objęty globalnym zezwoleniem na przerwania. Wykonany będzie zawsze, nawet, gdy przerwania będą zablokowane. Za obszarem przeznaczonym na tabelę wektorów przerwań umieszczony jest podprogram inicjalizujący, na którego końcu umieszczona jest instrukcja skoku do adresu pamięci gdzie umieszczony jest program główny. Na tej samej zasadzie funkcjonują inne wektory przerwań. Gdy pojawi się żądanie przerwania, mikrokontroler przechodzi do adresu wektora przyporządkowanego temu przerwaniu i wykonuje umieszczoną tam instrukcję skoku do podprogramu. W niewykorzystanych wektorach znajdują się instrukcje skoku do tego samego adresu pamięci, gdzie znajduje się instrukcja skoku do adresu 0x000, gdyż przyjęcie przerwania pomimo jego nieużywania jest przesłanką o błędnym działaniu mikrokontrolera, co wymaga jego wyzerowania.

Przerwania można podzielić na zewnętrzne, czyli pochodzące od urządzeń znajdujących się poza mikrokontrolerem, i wewnętrznych, pochodzących od modułów wewnętrznych mikrokontrolera. Przerwania zewnętrzne wyzwalane mogą być poziomem niskim, zmianą stanu na przeciwny, zboczem opadającym oraz zboczem narastającym. Przerwania wewnętrzne wyzwalane są na skutek zdarzeń, które zaszły w modułach wewnętrznych mikrokontrolera.

Działanie systemu przerwań oparte jest o flagi przyporządkowane do każdego ich źródła. Flagi te znajdują się w odpowiednich rejestrach, gdzie w momencie pojawienia się sygnału wyzwalającego przerwanie ustawiany jest właściwy dla danego źródła bit flagi. Bit globalnego zezwolenia na przerwania oraz bit aktywujący dane przerwanie muszą być ustawione. Jednostka centralna mikrokontrolera otrzymuje w ten sposób informację, że zgłoszone zostało żądanie obsługi przerwania. Mikrokontroler kończy bieżącą instrukcję, a następnie w pierwszej kolejności automatycznie wyłącza globalne zezwolenie na przerwania i zapisuje na szczycie stosu wartość licznika programu, która stanowi adres powrotny. Zajmuje to 4 cykle zegarowe, po których wykonywana jest instrukcja skoku do podprogramu umieszczona w wektorze właściwego przerwania, co trwa kolejne 2 do 3 cykli. Zerowany jest także bit flagi przerwania. Na stosie zapamiętywane jest także rejestr SREG, który nie może być zmieniony przez podprogram. Dopiero teraz wykonywany jest podprogram obsługi przerwania. Powrót do wykonywania programu głównego realizowany jest za pomocą instrukcji reti, która zdejmuje ze stosu adres powrotny i włącza globalne zezwolenie na przerwania. Odtworzenie rejestrów odbywa się w kolejności odwrotnej, jak były składowane na stosie. W czasie wykonywania podprogramu mogą być zgłaszane żądania przerwań pochodzące od innych źródeł. Jeżeli wyłączone było globalne zezwolenie na przerwania, to flagi poszczególnych przerwań są ustawiane, lecz realizacja poszczególnych procedur obsługi odbędzie się po włączeniu globalnego zezwolenia na przerwania w kolejności określonej przez priorytet przerwania. Po zakończeniu wykonywania procedury obsługi przerwania mikrokontroler wykona zawsze co najmniej jedną instrukcję programu głównego, do którego powrócił. Bardzo ważne jest, aby podczas obsługi przerwanie nie zostały zmienione ważne rejestry bądź flagi. Może być to powodem błędnego działania programu.

Procedurę obsługi przerwania zapisuje się w makrodefinicję (ang. ISR - Interrupt Service Routine), ISR(wektor, atrybut). argument wektor określa numer wektora przerwania, którego dotyczy. W tabeli 1 znajdują się nazwy wektorów, które stosuje się jako argument wektor makrodefinicji ISR. Atrybut funkcji ISR jest opcjonalny i określa sposób w jaki zostanie wygenerowany prolog i epilog funkcji. Atrybut ten ma następujące wartości.

 

ISR_BLOCK

jest to domyślna wartość. Funkcja zadeklarowana z użyciem tego atrybutu wykonana zostanie z zablokowanymi przerwaniami, które odblokowane zostaną po jej zakończeniu.

ISR_NOBLOCK

przerwania odblokowane zostaną najszybciej, jak to jest możliwe. W efekcie procedura obsługi przerwania będzie mogła być przerywana przez inne przerwania. Należy tę opcję stosować w sposób przemyślany, gdyż może dojść do przepełnienia stosu i nieprawidłowego funkcjonowania programu.

ISR_NAKED

kompilator pominie generowanie prologu i epilogu funckji i za zachowanie wartości rejestrów odpowiedzialny jest programista. Opcja ta jest stosowana wyłącznie w przypadku pisania procedury obsługi przerwania w asemblerze.

ISR_ALIASOF(vect)

tworzony jest alias wektora przerwań. Można w ten sposób wywołać jedną procedurę obsługi przerwania przez kilka wektorów. Deklaracja ISR(INT1_vect, ISR_ALIASOF(INT0_vect)); spowoduje, że wektor INT1_vect wskazywać będzie procedurę obsługi przerwania INT0. Konieczne jest wcześniejsze zadeklarowanie procedury obsługi przerwania INT0. W przeciwnym wypadku kompilator zgłosi błąd o niezidentyfikowanym symbolu.

 

Wykorzystując podane informacje przedstawię ogólny schemat implementacji przerwań.

 

#include < avr/io.h >
#include < avr/interrupt.h >
...
ISR(wektor) {
   /*kod obsługi przerwania*/
}
...
int main(void) {
   /*ustawienia rejestrów sterujących*/
   sei(); /*włączenie systemu obsługi przerwań*/
   while(1) {
      /*kod programu*/
   }
}

 

Na tym etapie to wszystkie informacje dotyczące systemu przerwań. W kolejnych artykułach omawiać będę poszczególne przerwania i sposoby ich użycia. Podparte będzie to praktycznym przykładem.

 

 

 

 

 

Autor: Orici
Literatura
1. Francuz T. Język C dla mikrokontrolerów AVR od podstaw do zaawansowanych aplikacji, Helion, Gliwice 2011
2. Witkowski A. Mikrokontrolery AVR programowanie w języku C przykłady zastosowań, PAN, Katowice 2006
Wyświetleń: 853|Komentarzy: 0|Ocena: 0|Głosów: 0