AVR GCC cz7


K U R S
AVR GCC: kompilator C
mikrokontrolerów AVR,
część 7
Przechodzimy do omówienia obsługi przerwań
Obsługa przerwań
za pomocą programów napisanych w AVR GCC.
Jak się okazuje, jest to bardzo skuteczne
AVR GCC pozwala na skuteczną narzędzie do ich obsługi.
kontrolę obsługi sprzętowych prze-
rwań. Jest jednak kilka szczegółów zajrzyjmy jeszcze raz (było to już podstawowy szablon kodu obsługi
często sprawiających na początku kło- wstępnie omawiane) do wygenerowa- oraz umieścił w tablicy wektorów od-
poty  zobaczymy jak sobie z nimi nego kodu assemblera (CTRL+F7). powiedni skok. Wypróbujmy to zaraz
poradzić. Na wstępie dla krótkiego Na początku znajdziemy wektory dopisując w naszym main.c obsługę
przypomnienia spójrzmy na rys. 18, przerwań, które na razie oczywiście np. dla pierwszego z brzegu przerwa-
na którym skrótowo pokazano typowy nie wskazują na żadne konkretne pro- nia zewnętrznego INT0, która na ra-
przebieg operacji podczas przerwania. cedury i ograniczają się do skoku pod zie nie robi nic konkretnego (SIGNAL
Wystąpienie sprzętowego prze- wspólny adres __bad_interrupt obsługi (SIG_INTERRUPT0) {} ;). Koniecznie
rwania powoduje kolejno: błędnego przerwania: musimy też dołączyć nagłówki avr/
test04.elf: file format elf32 avr
 zapisanie na stosie stanu licznika signal.h oraz avr/io.h (zwłaszcza brak
Disassembly of section.text:
00000000 <__vectors>:
rozkazów PC (zauważmy, że stos signal.h może wprawić w zakłopotanie
0: 12 c0 rjmp .+36 ; 0x26
musi być wcześniej prawidłowo 2: 2b c0 rjmp .+86 ; 0x5a kilkoma mało czytelnymi w pierwszej
4: 2a c0 rjmp .+84 ; 0x5a
zainicjalizowany  AVR GCC robi chwili ostrzeżeniami). Wygenerowany
6: 29 c0 rjmp .+82 ; 0x5a
8: 28 c0 rjmp .+80 ; 0x5a
to automatycznie, musimy jedy- kod wygląda następująco:
a: 27 c0 rjmp .+78 ; 0x5a
c: 26 c0 rjmp .+76 ; 0x5a
0000005c <__vector_1>:
nie uważać w przypadku ATmega
e: 25 c0 rjmp .+74 ; 0x5a
SIGNAL (SIG_INTERRUPT0)
10: 24 c0 rjmp .+72 ; 0x5a
{
128 aby wyłączyć zaprogramowa-
12: 23 c0 rjmp .+70 ; 0x5a
5c: 1f 92 push r1
ny fabrycznie fuse bit kompaty- 14: 22 c0 rjmp .+68 ; 0x5a
5e: 0f 92 push r0
16: 21 c0 rjmp .+66 ; 0x5a
60: 0f b6 in r0, 0x3f ; 63
bilności z ATmega 103; w trybie 18: 20 c0 rjmp .+64 ; 0x5a
62: 0f 92 push r0
1a: 1f c0 rjmp .+62 ; 0x5a
64: 11 24 eor r1, r1
kompatybilności ustawiany przez
1c: 1e c0 rjmp .+60 ; 0x5a
1e: 1d c0 rjmp .+58 ; 0x5a
6e: 0f 90 pop r0
kompilator na adres RAMEND
20: 1c c0 rjmp .+56 ; 0x5a
70: 0f be out 0x3f, r0 ; 63
22: 1b c0 rjmp .+54 ; 0x5a
72: 0f 90 pop r0
początek stosu jest fizycznie nie-
24: 1a c0 rjmp .+52 ; 0x5a
74: 1f 90 pop r1
76: 18 95 reti
dostępny gdyż ATmega 103 ma
[....]
}
mniejszy RAM),
0000005a <__bad_interrupt>:
 zablokowanie wszelkich następ- 5a: d2 cf rjmp . 92 ; 0x0 W prologu obsługi znajdujemy
nych przerwań, AVR GCC oferuje makra, które au- zapamiętanie na stosie wykorzysty-
 skok programu do odpowiedniej tomatyzują proces tworzenia procedur wanych przez kompilator rejestrów
dla przerwania pozycji w tablicy obsługi przerwań (handlerów): SIGNAL r0 (rejestr tymczasowy __tmp_reg__)
wektorów, (signame) oraz INTERRUPT (signame) oraz r1 (rejestr zerowy __zero_reg__).
 na pozycji tej musi być wpisana (znajdziemy je w pliku nagłówkowym Następnie zachowany zostaje rejestr
instrukcja skoku do właściwej pro- signal.h). Signame jest nazwą potrzeb- stanu SREG (0x3f) a rejestr r1 zostaje
cedury obsługi przerwania, nego wektora. Generalnie nazwa ta wyzerowany (AVR GCC wymaga aby
 zakończenie obsługi instrukcją reti może mieć uniwersalną postać _VEC- był on równy zeru przy każdym wy-
powoduje odblokowanie przerwań TOR (numer przerwania). Jednak jest wołaniu funkcji a nie wiadomo jaka
i przywrócenie ze stosu stanu licz- to mało czytelne i dlatego plik nagłów- jest jego wartość w momencie wystą-
nika PC (czyli wznowienie wy- kowy ioxxx.h dla danego typu kostki pienia przerwania).
konywania programu od miejsca, zawiera dużo łatwiejsze w użyciu na- Zakończenie handlera odtwarza
w którym wystąpiło przerwanie). zwy opisowe, np. w io8.h (ATmega 8) poprzedni stan rejestrów oraz za-
Część z tych operacji jest auto- znajdziemy następujące definicje:
#define SIG_INTERRUPT0 _VECTOR(1)
matyczna ale napisanie odpowiedniej
#define SIG_INTERRUPT1 _VECTOR(2)
#define SIG_OUTPUT_COMPARE2 _VECTOR(3)
procedury obsługi i wprowadzenie jej
#define SIG_OVERFLOW2 _VECTOR(4)
adresu do tablicy wektorów spoczy- #define SIG_INPUT_CAPTURE1 _VECTOR(5)
#define SIG_OUTPUT_COMPARE1A _VECTOR(6)
wa na programiście. Popatrzmy jakie
#define SIG_OUTPUT_COMPARE1B _VECTOR(7)
#define SIG_OVERFLOW1 _VECTOR(8)
wsparcie oferuje w tym zakresie AVR
#define SIG_OVERFLOW0 _VECTOR(9)
#define SIG_SPI _VECTOR(10)
 GCC.
#define SIG_UART_RECV _VECTOR(11)
#define SIG_UART_DATA _VECTOR(12)
Korzystając z już omówionych ele-
#define SIG_UART_TRANS _VECTOR(13)
#define SIG_ADC _VECTOR(14)
mentów rozpocznijmy w subfolderze
#define SIG_EEPROM_READY _VECTOR(15)
[Przyklad 04] nowy projekt test04 #define SIG_COMPARATOR _VECTOR(16)
#define SIG_2WIRE_SERIAL _VECTOR(17)
zawierający na początek pojedyn- #define SIG_SPM_READY _VECTOR(18)
czy plik main.c z szablonem pro- Wystarczy zdefiniować potrzebne
gramu głównego. Po skompilowaniu makro, aby kompilator wygenerował Rys. 18. Przebieg obsługi przerwania
Elektronika Praktyczna 9/2005
97
K U R S
jącego kodu obsługi przerwania (np. nieobecnej w AVR kontroli priory-
taką niespodziankę miałem po sko- tetu przerwań. Pozwala na obsłu-
piowaniu kodu obsługi TWI z pro- żenie krytycznego czasowo prze-
jektu ATmega8 do ATmega88: cały rwania niezależnie od faktu czy
Rys. 19. Okienko autokompletacji interfejs działa i jest opisany iden- program wykonuje pętlę główną
przerwania w AvrSide tycznie z wyjątkiem właśnie zmie- czy też już obsługuje zgłoszone
nionej  z SIG_2WIRE_SERIAL na wcześniej przerwanie o mniejszym
myka obsługę instrukcją reti. SIG_TWI  nazwy wektora). dla nas znaczeniu. Sprawa jest
Adres handlera (w tym przypad- Pojawia się od razu pytanie kie- prosta jeśli mamy do czynienia
ku 0x5c) pojawia się samoczynnie dy stosować SIGNAL a kiedy INTER- z dwoma przerwaniami: dla pod-
w tablicy wektorów: RUPT. Zawsze będzie to oczywiście rzędnego używamy makra INTER-
00000000 <__vectors>:
zależeć głównie od potrzeb konkret- RUPT co pozwala na praktycznie
0: 12 c0 rjmp .+36 ; 0x26
2: 2c c0 rjmp .+88 ; 0x5c
nego programu, jednak można sfor- natychmiastową obsługę drugiego
Zauważmy, że kod zachowu- mułować kilka podstawowych reguł:  ważniejszego. Gorzej jeśli prze-
je  naturalny dla AVR przebieg A. W niektórych przypadkach IN- rwań jest kilka  globalne odblo-
obsługi  z wszystkimi pozostały- TERRUPT nie możemy używać kowanie umożliwia wykonywanie
mi przerwaniami zablokowanymi w ogóle. Przypomnijmy sobie, że wszystkich pozostałych co nie za-
do momentu wykonania instrukcji przerwanie w AVR może być wy- wsze jest pożądane. W takim przy-
reti. Czasem jednak chcemy aby na wołane zdarzeniem (ustawiającym padku selektywne podwyższenie
inne, krytyczne czasowo przerwania odpowiednią flagę we właściwym priorytetu tylko jednego wybranego
reakcja następowała natychmiast rejestrze)  np. przepełnieniem przerwania wymaga każdorazowo
 wtedy musimy w naszej obsłudze licznika; albo warunkiem  prze- szczegółowego przełączania konfi-
samodzielnie je ponownie włączyć. rwanie jest aktywne cały czas guracji zezwoleń na poszczególne
Robi to samoczynnie drugie makro. dopóki zachodzi określona sytu- przerwania w kodzie handlerów.
Sprawdzmy, że wywołanie: acja  np. w rejestrze odbiornika C. Z powyższych ograniczeń wynika,
INTERRUPT (SIG_INTERRUPT0) {} ;
USART znajduje się nie odczytany że na ogół domyślnym sposobem
generuje taki sam kod ale rozpo- znak. Dodatkowa komplikacja to obsługi będzie SIGNAL, natomiast
czynający się włączającą przerwania fakt, że chociaż zazwyczaj rozpo- INTERRUPT użyjemy w specyficz-
instrukcją sei. Jednak trzeba ten spo- częcie obsługi przerwania zdarze- nych przypadkach, dokładnie roz-
sób stosować w odpowiednią uwagą niowego powoduje w chwili skoku ważając potrzeby, korzyści i możli-
gdyż może spowodować kilka niespo- do wektora przerwania samoczyn- wości wystąpienia niepożądanych
dzianek (do czego zaraz wrócimy). ne (sprzętowe) zgaszenie flagi to efektów.
Makra SIGNAL oraz INTERRUPT jednak są od tego wyjątki. np. D. Ponieważ makro SIGNAL blokuje
zadziałają nawet w przypadku wsta- przerwanie magistrali TWI (i2c). wszystkie inne przerwania (zgod-
wienia dowolnej nazwy nie odpo- W obu ostatnich przypadkach usu-
wiadającej żadnemu z rzeczywistych nięcie przyczyny przerwania (eli- List. 3. Plik z programem obsługi
timerów
wektorów przerwań. Kod zostanie minacja warunku albo zgaszenie
// obsługa timerów
wygenerowany i umieszczony w pro- flagi) musi byc wykonane progra-
#include  projdat.h
gramie ale oczywiście kompila- mowo wewnątrz funkcji obsługi. #include
#include
tor nie będzie mógł mu przypisać Na przykład dla wspomnianego
volatile uchar T2_counter;
żadnej pozycji w tablicy wektorów. odbiornika USART będzie to od-
/* Licznik T2 posłuży nam jako podsta-
W niektórych przypadkach (o czym czyt rejestru UDR. W tych właśnie
wowy timer systemowy, na bazie którego
będziemy
za chwilę) zrobimy tak celowo. przypadkach generowane przez
realizować cykliczne akcje, odliczać
Niestety zazwyczaj ta cecha jest ra- INTERRUPT odblokowanie prze- timeouty oraz uruchomimy prototyp ze-
gara.
czej zródłem zaskakujących błędów rwań na samym początku han- Wykorzystamy tryb pracy CTC  wygod-
ny ze względu na samoczynne zerowanie
wynikających np. z drobnej pomyłki dlera spowoduje natychmiastowe
licznika.
*/
literowej w nazwie wektora albo ze wywołanie tego samego przerwa-
void InitT2(void)
skopiowania handlera z programu nia  program jeszcze nie dotarł
// atmega 8 pracuje z wewnętrznym
oscylatorem 8 MHz  pojedynczy cykl ma
dla innej kostki. Kompilator nie i nigdy nie będzie mógł dotrzeć
długość 0.125 us
// (1 / 8000000)
zgłasza w takim przypadku żadnych do fragmentu kodu wyłączającego
// Jeśli ustawimy preskaler = 64 po-
wątpliwości a przerwanie pozostaje warunek wyzwalający (spójrzmy jedynczy tick licznika ma 64 * 0.125
= 8 us
nie obsługiwane. (W niektórych wer- jeszcze raz na rys. 18  zaraz po
// Dla uzyskania przerwania licznika
co 1 ms ustawimy jego wartość przełado-
sjach AVR GCC są zdaje się łatki wejściu do funkcji obsługi i wyko-
wania na 124
// (8 * (124+1) = 1000 us = 1 ms)
powodujące poinformowanie o wy- naniu sei nastąpi ponowny skok
{
OCR2 = 124; // wartość
stępującej rozbieżności ale general- do wektora). Taka aplikacja nie
przeładowania w trybie CTC
TCCR2 = _BV(WGM21) | _BV(CS22); //
nie lepiej na to nie liczyć). ma szans na poprawne działanie.
tryb CTC bez zewnętrznego wyjścia,
AvrSide wyposażono w dynamicz- Pomyłka ta pojawia się na tyle preskaler 64
TIMSK |= _BV(OCIE2); // włącze-
ną podpowiedz właściwych nazw często, że autorzy avr libc zaczęli
nie przerwania CTC
}
(CTRL+L) co pozwala wyeliminować nawet rozważać ewentualną zmianę
SIGNAL (SIG_OUTPUT_COMPARE2)
taki błąd (rys. 19) jednakże w razie wprowadzającego w błąd nazewnic-
{
if (++T2_counter == 100);
jakichś kłopotów z działaniem pro- twa  ale to na razie tylko wstęp-
{
T2_counter = 0;
gramu zajrzyjmy zawsze do tablicy ne propozycje.
MS100_FLAG = true;
wektorów i upewnijmy się czy za- B. Jak łatwo się domyśleć, INTER- }
}
wiera ona właściwy skok do istnie- RUPT ma służyć do zastąpienia
Elektronika Praktyczna 9/2005
98
K U R S
nie ze sprzętowym działaniem niających. Takie poczynania mogą Plik nagłówkowy projdat.h korzy-
mikrokontrolera) zazwyczaj powin- doprowadzić do utraty jakichś in- sta również z ogólnego, wspólnego
niśmy zadbać aby funkcje obsługi nych przerwań, których mikrokon- nagłówka mynames.h zawierającego
były jak najkrótsze: nie umiesz- troler nie zdąży obsłużyć. ulubione typy i definicje (umieściłem
czać w nich skomplikowanych W wielu typowych zastosowa- go w folderze \AvrSide\Myinc podając
przeliczeń lub konwersji, obsługi niach mikrokontrolera (jak różne odpowiednią ścieżkę w opcjach projek-
zewnętrznych urządzeń itp. a już transmisje, pomiary, akwizycja da- tu)  list. 6.
w żadnym przypadku nie wstawiać nych itp.) dobrze sprawdza się na- Jak widać działanie programu
do nich programowych pętli opóz- stępująca recepta: sprowadza się do kilku podstawo-
 stosujemy wyłącznie makra SI- wych operacji:
List. 4. Listing pliku nagłówkowego
GNAL,  inicjalizacja ustawia potrzebne nam
projdat.h
 funkcje obsługi skracamy do nie- linie I/O (PORTB jako wyjściowy),
// plik nagłówkowy globalnych danych
projektu
zbędnego minimum i przekazu- konfiguruje timer T2 (niestety nie
#ifndef _PROJ_DAT_H_
#define _PROJ_DAT_H_ jemy z nich, za pośrednictwem dopisałem jeszcze w AvrSide kre-
// #include:
zmiennych logicznych lub flag atora automatycznej konfiguracji
#include  mynames.h
bitowych, do pętli głównej in- timerów, jest ona wykonana ręcz-
// #define:
formację o konieczności realizacji nie ale została dosyć szczegółowo
// definicje typów typedef
czynności związanych z wystąpie- opisana w komentarzu) i na koniec
// dane globalne
niem przerwania, uruchamia system przerwań (do-
volatile Flags SysFlags;
 pętla główna sprawdza stan takich datkowo znajdujemy tu ustawienie
#define MS100_FLAG SysFlags.Bits.Flag1
flag a w momencie ich ustawienia kalibracji OSCCAL dla pracy z we-
wykonuje potrzebne działania, nie- wnętrznym oscylatorem 8 MHz,
#ifdef _MAIN_MOD_
// definicje danych  tylko w module
kiedy mocno pracochłonne, bez potrzebna wartość jest przechowa-
main()
// char x;
blokowania dostępu do przerwań. na w ostatnim bajcie eeprom; taką
#else
W ten sposób żadne z przerwań metodą posługuje się wbudowany
// deklaracje danych jako importowanych
 w każdym innym module nie przejmuje sterowania na zbyt w AvrSide programator usb ale
// extern char x;
długi czas a wszystkie zadania są wy- każdy użytkownik prawdopodobnie
#endif
konywane mniej więcej równomiernie zastosuje jakiś własny sposób pa-
// deklaracje funkcji
(zauważmy, że jest to bardzo uprosz- sujący do posiadanego sprzętu);
// extern char Myfunc(int,char);
czony model znanego z dużych syste-  1 ms przerwanie timera odmie-
extern void InitT2(void);
mów programowania zdarzeniowego). rza (przy pomocy dodatkowego
#endif
Zróbmy sobie od razu tego typu przy- lokalnego programowego licznika
kład wykorzystujący wiele wcześniej T2_counter) okresy 100 ms i usta-
List. 5. Główny moduł przykładowego
projektu
omawianych technik. Do projektu do- wia flagę bitową MS100_FLAG
// główny moduł projektu
#define _MAIN_MOD_ 1
dajmy plik timers.c o zawartości poka- zdefiniowaną globalnie w projdat.
// pliki dołączone (include):
#include  projdat.h
zanej na list. 3 oraz plik nagłówkowy h (zwróćmy jeszcze raz uwagę na
#include
#include
projdat.h gromadzący globalne zmien- niezbędne klasyfikatory volatile dla
#include
#include ne, deklaracje funkcji i różne definicje zmiennych współużytkowanych
#include
(list. 4). Do pliku main.c dodamy kod przez program główny oraz han-
#define MS100_DELAY 5
pokazany na list. 5. dler przerwania);
// dane:
 pętla główna śledzi nieustannie
static char Ms100_counter;
static volatile uchar LedState = 1;
stan flagi MS100_FLAG, po jej
List. 6. Plik nagłówkowy z deklara-
// funkcje:
ustawieniu przystępuje do wykona-
cjiami ulubionycyh typów i definicji
// ulubione oznaczenia
INTERRUPT (SIG_INTERRUPT0) nia przypisanych fladze procedur:
{
#ifndef _MY_NAMES_H
 kasuje flagę (co jest konieczne
}
#define _MY_NAMES_H
aby wykonanie było tylko jedno-
//====================
#include
// funkcja main()
krotne);
int main(void)
#define uint unsigned int
{
#define uchar unsigned char  przy pomocy lokalnego programo-
// inicjalizacja
#define ulong unsigned long
OSCCAL=eeprom_read_byte((uchar*)E-
wego licznika Ms100_counter od-
2END); // zapis kalibracji w ostat-
#define forever while(1)
niej komórce eeprom mierza interwał czasowy określony
#define EEPROM __attribute__ ((sec-
DDRB=0xff;
tion( .eeprom )))
stałą MS100_DELAY (w przykładzie
#define NOINIT __attribute__ ((sec-
InitT2();
tion( .noinit )))
jest to 0,5 s);
sei();
#define NAKED __attribute__ ((naked))
 co 0,5 s przesuwa okrężnie poje-
// pętla główna
typedef struct
while (1)
{ dynczy ustawiony bit w lokalnej
{
uchar Flag1:1;
if (MS100_FLAG)
zmiennej stanu portu LedState
uchar Flag2:1;
{
uchar Flag3:1;
MS100_FLAG = false; oraz przepisuje ją na wyjście
uchar Flag4:1;
if (++Ms100_counter == MS100_DELAY)
uchar Flag5:1;
portu B.
{
uchar Flag6:1;
Ms100_counter = 0;
uchar Flag7:1;
Jerzy Szczesiul, EP
// nasza okresowa akcja (przełą-
uchar Flag8:1;
czenie wyjścia) uruchamiana
} FlagBits;
jerzy.szczesiul@ep.com.pl
// zegarem systemowym co 100ms *
MS100_DELAY (0,5s)
typedef union
PORTB=LedState;
{
if(LedState==128) LedState=1;
UWAGA!
FlagBits Bits;
else LedState = LedState<<1;
uchar Byte;
Środowisko IDE dla AVR-GCC opracowane
}
} Flags;
}
przez autora artykułu można pobrać ze
}
#endif
strony http://avrside.ep.com.pl.
}
Elektronika Praktyczna 9/2005
99


Wyszukiwarka

Podobne podstrony:
Using the EEPROM memory in AVR GCC
AVR GCC w Linuksie przykład instalacji ze źródeł
Kurs AVR GCC cz 5
Kurs AVR GCC, cz 3
Kurs AVR GCC Wyświetlacz LCD od Nokii310
AVR GCC kompilator C dla mikrokontrolerów AVR, część 12
AVR GCC kompilator C dla mikrokontrolerów AVR, część 11
Kurs AVR GCC cz 2
AVR GCC cz8
AVR GCC cz5

więcej podobnych podstron