![]() |
Wyjście Spis treści Poprzedni Następny
Autor artykułu: mgr Jerzy Wałaszek Konsultacja: Wojciech Grodowski, mgr inż. Janusz Wałaszek |
©2015 mgr
Jerzy Wałaszek
|
| Tematy pokrewne | Podrozdziały | |
| (w budowie) |
Co to jest przerwanie Rodzaje przerwań Obsługa przerwań |
Rodzaje przerwań |
|||||||||||||||||||||||||||||||||||||||||||||||
|
Przerwania mogą być wewnętrzne
(tzn. pochodzić od wewnętrznych składników mikrokontrolera)
lub zewnętrzne (tzn. generowane przez
urządzenie zewnętrzne, którym steruje mikrokontroler). Przerwania zewnętrzne
Na pewno zauważyłeś już, że wyprowadzenia mikrokontrolera
ATTINY13 (innych mikrokontrolerów też)
1...3 i 5...7 posiadają po kilka opisów. Ich normalna funkcja to
linie portu B od PB0 do PB5. Jednakże w pewnych przypadkach
funkcje te można zmienić. Tak robiliśmy w poprzednim rozdziale,
gdzie PB0 i PB1 pełniły funkcje kanałów PWM OC0A i OC0B i
służyły do sprzętowej generacji przebiegów prostokątnych o
różnych współczynnikach wypełnienia. Również linia PB2 może być
wykorzystywana jako T0, czyli źródło zewnętrznego zegara dla
licznika wbudowanego w mikrokontroler. Przy programowaniu
mikrokontrolera za pomocą programatora wykorzystywane są linie
PB0 (MOSI), PB1
(MISO), PB2 (SCK)
i PB5 (RESET).
Zwróć uwagę, że w opisach końcówek kontrolera mamy dodatkowe nazwy:
Każda z tych końcówek może być źródłem przerwania zewnętrznego dla mikrokontrolera. INT oznacza po prostu przerwanie (ang. INTerrupt), a PCINT oznacza przerwanie przy zmianie stanu końcówki (Pin Change INTerrupt). Końcówkę PB5 już znasz. Jeśli źródło zewnętrzne ustawi na niej stan logiczny 0 (np. poprzez zwarcie do masy), to mikrokontroler jest resetowany bez względu na to, co w danej chwili wykonuje. Po resecie mikrokontroler zaczyna wykonywać swój program od początku. Jest to zatem również przerwanie, tyle że nie wywołuje jakiejś specjalnej funkcji obsługi i nie musi być specjalnie uaktywniane w języku C (z poziomu języka maszynowego, asemblera, można po resecie wykonać określoną funkcję, lecz język C rezerwuje to przerwanie do restartu programu). Z funkcji RESET korzysta programator ISP. Wymusza on najpierw stan niski na tym wejściu, a wtedy mikrokontroler przechodzi w specjalny tryb i końcówki PB0, PB1 i PB2 stają się liniami komunikacyjnymi MOSI, MISO i SCK z programatorem. Po liniach tych programator przesyła informacje dla wewnętrznych pamięci FLASH, RAM i EPROM (sygnały SCK : zegar i MOSI : zapisywane dane bit po bicie) lub odczytuje dane z tych pamięci (sygnały SCK : zegar i MISO : odczytywane dane bit po bicie). Pozostałe przerwania (INT0, PCINT0...PCINT5) muszą zostać odpowiednio uaktywnione, aby mikroprocesor na nie zaczął reagować, tzn, aby w momencie ich pojawienia się była wywoływana odpowiednia funkcja obsługi przerwań (dodatkowo, aby mikrokontroler reagował na PCINT5, należy wyłączyć funkcję RESET – da się to zrobić za pomocą tzw. fusebitów, czyli bitów bezpiecznikowych, lecz wtedy stracisz możliwość programowania mikrokontrolera i dlatego nie polecam tego rozwiązania początkującym koderom, gdyż odzyskanie mikrokontrolera po takim przeprogramowaniu wymaga specjalnego programatora, zobacz tu). O tym za chwilę. Linia INT0 może reagować na poziom niski, zmianę stanu albo zmianę poziomu z 0 na 1 lub z 1 na 0 (to nie to samo co zmiana stanu, ponieważ tutaj przerwanie występuje tylko w jednym przypadku, gdy sygnał na linii INT0 zmienia się np. z 0 na 1, a przy zmianie z 1 na 0 przerwania już nie będzie). Posiada ona najwyższy priorytet (ważność) wśród przerwań, zaraz po RESET. Jest to najbardziej uniwersalna linia przerwań zewnętrznych Linie PCINTx reagują jedynie na zmianę stanu odpowiednich końcówek (zmiana z 0 na 1 lub z 1 na 0, w obu przypadkach przerwanie wystąpi). Przerwania zewnętrzne, jeśli są uaktywnione, działają niezależnie od konfiguracji linii PB0...PB5. Jeśli ustawimy je jako wyjścia, to mikrokontroler będzie mógł programowo wywoływać określone przerwanie przez zmianę stanu linii portu. Jednak najczęściej ustawiamy je jako wejścia, a stan linii wymusza sterowane przez mikrokontroler urządzenie w momencie, gdy należy je w jakiś sposób obsłużyć. Przerwania wewnętrzne
Przerwania wewnętrzne są generowane przez wewnętrzne komponenty
mikrokontrolera. Dla ATTINY13 są zdefiniowane następujące
przerwania wewnętrzne:
Jak to działaAby zrozumieć sposób obsługi przerwań, musisz
posiadać nieco wiadomości na temat sposobu pracy
mikroprocesora, który znajduje się wewnątrz każdego
mikrokontrolera. Z poziomu języka C nie widzimy wewnętrznej
struktury komputera, ponieważ szczegóły są przed nami ukryte
(nie jest to złośliwość twórców tego języka, lecz działanie
celowe – dzięki temu programy w języku C są w większości
przenośne). Każdy program w języku C musi zostać
przetłumaczony na ciąg poleceń dla mikroprocesora. Polecenia
te tworzą kod programu i są umieszczane w komórkach pamięci
programu. Mikroprocesor w trakcie wykonywania go adresuje
odpowiednią komórkę tej pamięci i pobiera z niej kod
polecenia, który następnie dekoduje (czyli
rozpoznaje)
i wykonuje odpowiednie operacje, po czym przechodzi do
kolejnego polecenia w programie. Wynika z tego, że
instrukcje maszynowe znajdują się pod określonymi adresami.
Funkcja języka C jest zbiorem takich instrukcji, które
tworzą w pamięci programu spójny blok kodu. Adres pierwszej
instrukcji w tym bloku jest adresem funkcji. Gdy w języku C
wywołujesz w swoim programie funkcję, to mikroprocesor
zaczyna wykonywać jej kod, począwszy od tej pierwszej
instrukcji. Wywołanie funkcji wymaga zatem podania adresu
początku funkcji w pamięci programu. W języku C adresem tym
jest niejawnie nazwa funkcji. Kompilator w trakcie
tłumaczenia twojego programu w języku C na instrukcje
maszynowe dla mikroprocesora rozpoznaje funkcje i przydziela
im odpowiednie obszary w pamięci programu. Zapamiętuje przy
tym adresy początków tych obszarów jako nazwy funkcji. Obsługa przerwań również polega na wykonaniu funkcji, która jest umieszczona w określonym miejscu pamięci programu. Skąd mikroprocesor wie, gdzie znajduje się odpowiednia funkcja? Otóż do tego celu wykorzystany jest początek pamięci programu, gdzie w kolejnych komórkach umieszczane są skoki do funkcji, które obsługują określony rodzaj przerwania. Komórki przechowujące te skoki nazywamy wektorami przerwań. W mikrokontrolerze ATTINY13 zdefiniowano następujące wektory przerwań:
Każdy wektor przerwań obejmuje dwa bajty, czyli pojedynczą komórkę pamięci programu. Pamięć programu podzielona jest na komórki dwubajtowe, ponieważ każdy rozkaz maszynowy mikroprocesora zawartego w mikrokontrolerze ma długość 16 lub 32 bity, czyli dwa lub cztery bajty. Na przykład pamięć 1KB w ATTINY13 podzielona jest na 512 komórek i tyle może zawierać rozkazów 16 bitowych (rozkazy 32 bitowe stosowane są rzadko). W każdym wektorze przerwań możemy umieścić jeden rozkaz 16-to bitowy. Jest to najczęściej rozkaz skoku w odpowiednie miejsce programu, gdzie znajduje się funkcja obsługująca dane przerwanie. Może to wyglądać następująco (fragment kodu maszynowego z instrukcji producenta):
Gdy pojawia się zdarzenie wyzwalające przerwanie i dane przerwanie zostało uaktywnione (o tym za chwilę), to mikroprocesor wykonuje instrukcję zawartą w określonym wektorze przerwań. Wcześniej zapamiętuje on oczywiście adres miejsca w programie, który wykonywał przed przerwaniem. Instrukcja w wektorze przerwań wykonuje skok (rjmp = relative jump, czyli skok względny) do odpowiedniej procedury obsługi przerwania, zwanej ISR (ang. Interrupt Service Routine), która wykonuje pożądane działania, po czym następuje powrót w miejsce programu, w którym wystąpiło przerwanie i program jest kontynuowany. Nie ma zatem w tym nic tajemniczego. |
|||||||||||||||||||||||||||||||||||||||||||||||
Obsługa przerwań |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
W języku C przerwania są standardowo wyłączone.
Oznacza to, że musisz je jawnie uaktywnić, aby mikrokontroler
zaczął na nie reagować. Najpierw do programu należy dołączyć
plik nagłówkowy: avr/interrupt.h w którym są zdefiniowane wszystkie elementy programowe do obsługi przerwań. W następnym kroku tworzymy funkcję, która dane przerwanie ma obsługiwać. Funkcja ta nie ma typu i posiada specjalną nazwę ISR (ang. Interrupt Service Routine - procedura obsługi przerwania). Parametrem tej funkcji jest wektor odpowiedniego przerwania. Nazwy tych wektorów są następujące:
Wewnątrz funkcji zawieramy kod, który będzie wykonywany po przyjęciu przerwania. Na przykład poniższa funkcja zmienia stan linii PB0 na przeciwny przy każdym przepełnieniu licznika TCNT0 (uwaga: to jeszcze nie jest kompletny program, czytaj dalej!):
Gdy masz już funkcję ISR, musisz w programie uaktywnić obsługę przerwania dla tej funkcji, a wcześniej odpowiednio skonfigurować rejestry mikrokontrolera. Obsługę przerwań uaktywniamy przez ustawienie bitów w rejestrach sterujących tymi przerwaniami. Na koniec należy włączyć reakcję na przerwania. Dokonuje się tego przez wywołanie bezargumentowej funkcji: sei() (ang. Set Interrupts – ustaw przerwania). Z kolei funkcja cli() (ang. Clear Interrupts - blokuj przerwania) blokuje reakcje na przerwania.
Przerwanie INT0
Teraz pokażemy praktycznie, jak w programie obsługiwać
określone przerwania. Na początek zajmiemy się przerwaniem
od linii INT0 (PB1).
Przerwanie to jest najbardziej uniwersalnym przerwaniem
zewnętrznym, ponieważ może być generowane na cztery różne
sposoby.
Najpierw w rejestrze MCUCR (ang. Micro Controler Unit Control Register - rejestr sterujący jednostką mikrokontrolera) ustawiamy sposób generacji przerwania przez linię INT0.
Rejestr sterowania mikrokontrolerem MCUCR Do tego celu przeznaczone są dwa bity ISC1 i ISC0 (ang. Interrupt Sense Control – sterowanie sposobem wykrywania przerwania). Mogą one przybrać jedną z czterech kombinacji wartości:
Gdy określimy sposób generowania przerwania w zależności od stanu linii INT0, musimy uaktywnić przerwanie przez ustawienie na 1 bitu INT0 w rejestrze GIMSK (ang. General Interrupt Mask Register – rejestr maskowania przerwań zewnętrznych).
Rejestr maskowania przerwań zewnętrznych GIMSK Ostatnią rzeczą pozostaje włączenie przerwań za pomocą funkcji sei(). Poniższy program umożliwi ci pobawienie się z przerwaniem INT. Podłącz do płytki bazowej płytkę aplikacyjną APP001 i ustaw zworkę J1 w położeniu dolnym. W ten sposób do linii PB1 będzie dołączony przycisk W1 z płytki. Za pomocą tego przycisku możesz zmieniać stan linii PB1, czyli INT0. gdy przycisk nie jest naciśniety, na linii INT0 mamy stan logiczny 1. Gdy przycisk naciśniesz, stan ten zmieni się na 0.
W programie ustawiamy obsługę przerwań INT0 przy zmianie stanu linii PB1 z 1 na 0, co odpowiada naciśnięciu przycisku. Program główny w pętli zlicza obiegi co 50 ms w zmiennej licznik. Następnie do portu B są przesyłane 3 górne bity licznika i sterują one diodami D2...D4. Przycisk W1 jest obsługiwany niezależnie przez przerwanie INT0. W momencie naciśnięcia tego przycisku zmienia się stan linii PB1 (INT0) z 1 na 0 i następuje wygenerowanie przerwania. W procedurze obsługi mikrokontroler zapala lub gasi diodę D0. W programie nie obsługujemy drgań styków, dlatego czasami reakcja na przycisk nie jest prawidłowa. To cena prostoty. W praktycznych projektach musisz jednak pamiętać o tym niekorzystnym zjawisku i wprowadzać odpowiednie opóźnienia przy odczycie stanu przycisków mechanicznych. Poeksperymentuj w tym programie z innymi ustawieniami bitów ISC1 i ISC2 w porcie MCUCR. Uwaga. Przerwanie INT0 jest generowane bez względu na to czy linia PB1 pracuje jako wejście, czy jako wyjście danych. Jeśli ustawimy ją jako wyjście, to mikrokontroler poprzez zmianę jej stanu będzie mógł programowo generować przerwanie INT0. To samo dotyczy opisanego dalej przerwania PCINT.
Przerwanie PCINTPrzerwanie to powstaje, gdy stan wybranej linii
PB0...PB5 zmienia poziom logiczny (tzn.
zmienia się z 0 na 1 lub z 1 na 0). Najpierw w
rejestrze PCMSK (ang. Pin Change
Mask Register – rejestr maski przerwań zmiany stanu
końcówek) ustawiamy bity określające końcówki
PB0...PB5 mikrokontrolera, które będą uczestniczyć w
generacji przerwania. Tutaj końcówki BP0...PB5 nazywają się
PCINT0...PCINT5 z uwagi na pełnienie nowej funkcji
(końcówka PCINT5 posiada funkcję RESET,
która ma wyższy priorytet od PCINT5, dlatego jeśli RESET nie
jest wyłączony za pomocą ustawień fusebitów, to przerwanie
PCINT5 nie będzie miało szansy wystąpić).
Rejestr maski przerwań zmiany stanu końcówek PCMSK Po określeniu końcówek mikrokontrolera generujących przerwanie PCINT należy włączyć to przerwanie przez ustawienie na 1 bitu PCIE (ang. Pin Change Interrupt Enable – włączenie przerwań zmiany stanu końcówek).
Rejestr maskowania przerwań zewnętrznych GIMSK Na koniec włączamy obsługę przerwań przez wywołanie funkcji sei(). Do płytki bazowej podłącz płytkę aplikacyjną APP001 i ustaw obie zworki w dół. W ten sposób do linii PB0 i PB1 zostaną podłączone przyciski, którymi będziesz mógł zmieniać stan tych linii.
Program cyklicznie przesuwa punkt świetlny na diodach D2...D4. Przyciski W0 i W1 sterują kierunkiem tego przesuwu. Ich naciśnięcie powoduje wygenerowanie przerwania PCINT. W procedurze obsługi tego przerwania sprawdzamy, który przycisk został naciśniety i odpowiednio ustawiamy zmienną dir, która steruje kierunkiem przesuwu punktu świetlnego. Przyciski można naciskać w dowolnej chwili. Nowością jest utworzenie zmiennej dir z dyrektywą volatile. Dyrektywa ta jest informacja dla kompilatora, że zawartość zmiennej może się zmieniać w dowolnej chwili i nie należy jej buforować w programie, co kompilator często robi w celu zwiększenia szybkości działania kodu. Jeśli przed definicją zmiennej umieścimy dyrektywę volatile, to program będzie zawsze pobierał zawartość tej zmiennej z pamięci i nie będzie jej sobie zapamiętywał w tymczasowym miejscu. Jest to konieczne, ponieważ zmienną tą zmienia procedura przerwania PCINT w nieprzewidywalnym momencie. Spróbuj uruchomić ten program bez tej dyrektywy, a przestanie on poprawnie działać. Z przerwaniami INT i PCINT związany jest jeszcze rejestr znaczników przerwań GIFR (ang. General Interrupt Flag Register).
Rejestr znaczników przerwań zewnętrznych GIFR Jak widzisz, ma on podobną budowę do rejestru GIMSK, który maskuje (tzn. uaktywnia lub blokuje) przerwania INT i PCINT na tych samych bitach. Bity INTF0 i PCIF zostają ustawione na 1, gdy wystąpi warunek przerwania INT lub PCINT. Jeśli zostanie przy tym wywołana procedura ISR danego przerwania, to odpowiedni bit rejestru znaczników przerwań zostanie automatycznie wyzerowany. Można też wyzerować dany bit wpisując do niego 1 (nie zero! jak w zwykłych rejestrach). Rejestr ten może czasem być użyteczny, gdy jedna procedura przerwań chce sprawdzić, czy wystąpiło inne przerwanie. Normalnie nie musisz z niego korzystać.
Przerwania licznikoweSą to przerwania wewnętrzne. Powstają wtedy, gdy
licznik TCNT0 ulega przewinięciu, tzn. gdy osiągnie
maksymalną wartość i rozpoczyna nowy cykl zliczania lub
zrównuje swój stan z jednym z rejestrów porównawczych OCR0A
lub OCR0B.
Przerwanie przepełnienia licznika TIM0_OVF powstaje, gdy zostanie ustawiony na 1 znacznik TOV0 w rejestrze TIFR0 (ang. Timer/Counter Interrupt Flag Register – rejestr znaczników przerwań licznika).
Rejestr TIFR0 Znaczniki w tym rejestrze mogą być zerowane programowo przez wpisanie do odpowiedniego bitu 1 (nie zero!). Po przyjęciu przerwania odpowiedni znacznik jest automatycznie zerowany (nie musisz tego specjalnie robić w swojej procedurze ISR). Znacznik TOV0 jest ustawiany zależnie od trybu pracy licznika, który ustawiamy bitami WGMx w rejestrach sterujących licznika TCCR0A i TCCR0B:
Rejestr sterowania licznikiem TCCR0A
Rejestr sterowania licznikiem TCCR0B Reguły ustawiania znacznika TOV0, a zatem również generowania przerwania, są następujące:
Zatem, aby wygenerować przerwanie od licznika TCNT0, należy najpierw ustawić pożądany tryb pracy tego licznika (omówiliśmy te tryby w poprzednim rozdziale), a następnie włączyć przerwania od licznika ustawiając na 1 bit TOIE0 w rejestrze maskowania przerwań licznika TIMSK0:
Rejestr maskowania przerwań licznika TIMSK0 Na koniec uaktywniamy reagowanie na przerwania przez wywołanie funkcji sei(). Do płytki bazowej dołącz płytkę aplikacyjną APP001 i ustaw pierwszą zworkę w dół, aby do linii PB0 był dołączony przycisk W0. Następnie prześlij do mikrokontrolera poniższy program:
Przerwania od licznika są często wykorzystywane w celu okresowego wykonywania określonej procedury. Tak właśnie działa powyższy program. Do licznika trafiają impulsy zegarowe z preskalera ustawionego na dzielenie częstotliwości zegara 1MHz przez 256. Daje to częstotliwość około 3906 Hz. Gdy licznik zliczy 256 takich impulsów, nastąpi przepełnienie i zostanie wygenerowane przerwanie TIM0_OVF. W procedurze obsługi tego przerwania odczytujemy stan portu B, negujemy bit BP0, który pochodzi od przycisku, przesuwamy wszystkie bity o 1 w lewo i zapisujemy je do portu PORTB. Ostatni bit jest zawsze ustawiany na 1, aby do przycisku był podłączony opornik podciągający, inaczej wejście PB0 będzie zbierało zakłócenia z okolicy. Sprawdź, co się będzie działo, jeśli ten bit nie będzie ustawiany na 1, tzn. w procedurze obsługi przerwania zmień instrukcję na:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe