Poleg timerjevih prekintiev, ki smo jih spoznali v predhodnih poglavjih, poznamo še zunanje prekinitve. Skok v prekinitveno rutino (ISR => Interrupt service routine) lahko dosežemo s spremembo stanja na določenih pinih mikrokontrolerja. Pri Arduino Uno sta pina, s katerima lahko prožimo prekinitve pin 2 (int.0) in pin 3 (int.1). Pri Arduino Mega lahko za proženje zunanjih prekinitev poleg teh dveh pinov uporabimo še pin 21(int.2), pin 20 (int.3), pin 19 (int.4) in pin 18 (int.5).
Mikrokontroler AVR vsebuje seznam prekinitvenih virov oziroma prekinitvenih vektorjev, ki vsebujejo opis dogodkov, ki lahko sprožijo prekinitev. Če so prekinitve omogočene in pride do katerega od teh dogodkov, se bo izvajanje programa nadaljevalo na določeni lokaciji v programskem pomnilniku - na prekinitvenem vektorju.
Pri uporabi zunanjih prekinitev moramo narediti naslednje tri stvari:
Omogočiti globalne prekinitve AVR (ATMega 328P) mikrokontrolerja. V registru STATUS postavimo 7 bit (bit 8) na 1.
V registru EIMSK (External Interrupt Mask Register) moramo setirati ustrezni bit, bit 0 oziroma bit 1, odvisno od tega, na katerem pinu želimo prožiti prekinitve.
Napisati prekinitveno rutino ISR z izbranim prekinitvenim vektorjem.
Slika 1: Register STATUS
Slika 2: Register EIMSK (External Interrupt Mask Register)
Določiti moramo na kakšen način bomo prožili prekinitev na izbranem pinu. Lahko jo prožimo s spremembo signala iz 0 na 1 (RISING), s spremembo signala iz 1 na 0 (FALLING), s katerokoli spremembo signala na izbranem pinu (CHANGE) ali pa z nizkim nivojem na izbranem pinu (LOW). Te izbire nastavimo z določitvijo ustreznih bitov registra EICRA (External Interrupt Control Register A).
Slika 3: Register EICRA (External Interrupt Control Register A)
Tabela 1: Način proženja prekinitve izberemo z nastavitvijo ustreznih ICS bitov.
Primer:
Izdelajmo program, ki bo krmilil utripanje LED1 diode, priključene na pin 4 s frekvenco 2,5 Hz. Program za krmiljenje bomo napisali v glavni zanki void loop (). Omogočili bomo zunanje prekinitve na INT0 (pin 2), kamor bomo priključili tipko. Ob vsakem pritisku na tipko bomo sprožili prekinitev. V prekinitveni rutini se naj preklopi (spremeni stanje) LED2 dioda, priključena na pin 5. Prekinitvena rutina se naj sproži ob spremembi signala na pinu 2 iz stanje 0 na 1 (RISING).
Tipko bomo priključili na pin 2 (INT0) tako, da bomo s pomočjo vezave tipke onemogočili odbijanje kontaktov na tipki. Zaradi odbijanja kontaktov na tipki z enim pritiskom na tipko pošljemo več impulzov na pin, kamor je tipka priključena.
Slika 4: Vezava tipke za odpravo vpliva odbijanja kontaktov
Programskega odpravljanja motenj zaradi odbijanja kontaktov ne moremo uporabiti. Funkcija delay() je v prekinitveni rutini onemogočena, ker so v tem času onemogočene vse prekinitve, zato je ne moremo uporabiti. V času izvajanja prekinitvene rutine se ne more sprožiti nova prekinitev. V vezju (Slika 4) smo uporabili integrirano vezje IC 74132, ki vsebuje štiri NAND logična vrata s Schmittovim prožilnikom. Mi smo uporabili prva, katerima smo vhoda (pin 1 in pin 2) povezali in priključili na tipko po vezavi. Integrirano vezje IC 74132 je družine TTL (tranzistorsko tranzistorska logika), katere lastnost je ta, da na svojem izhodu da za stanje logične 0 napetost 0 V, za stanje logične 1 pa napetost 5 V. NAND logična vrata pa smo uporabili zato, da ob sklenitvi tipke dobimo na izhodu stanje logične 1 oziroma napetost 5 V.
Program:
/*
Proženje zunanje prekinitve na INT0 (pin 2) s tipko, Arduino Uno.
Avtor: Milan Ivič
April 2015
*/
int LED1 = 4;
int LED2 = 5;
int Tipka1 = 2;
volatile int Sprememba = LOW;
void setup()
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(Tipka1, INPUT);
cli(); //Onemogoči globalne prekinitve
//Omogočanje prekinitev nastavimo z biti v registru EIMSK (External Interrupt Mask Register):
EIMSK = _BV(INT0); //Omogočanje prikinitev na INT0 (pin 2)
//Kako se prekinitev sproži nastavimo v registru EICRA (External Interrupt Control Register A):
EICRA = _BV(ISC00) | _BV(ISC01); //Prekinitev se bo sprožila na prehodu signala iz 0 na 1 na pinu 2 (INT0).
sei(); //Omogoči globalne prekinitve.
}
void loop() //V glavni zanki lahko pišemo poljubni program ali pa je lahko prazna.
{
digitalWrite (LED2, HIGH);
delay(200);
digitalWrite (LED2, LOW);
delay(200);
}
ISR(INT0_vect) //Prekinitvena rutina
{
Sprememba =!Sprememba; //Sprememba stanja spremenljivke Sprememba.
digitalWrite(LED1, Sprememba); //Preklop LED1 diode.
}
Ker smo v glavnem programu izdelali program za utripanje LED2 diode s frekvenco 2,5 Hz, bo ta LED dioda neprestano utripala, tudi takrat, ko s tipko sprožimo prekinitev. V prekinitveni rutine se bo LED1 preklopila nato pa se bo naprej izvajal glavni program.
Spremenljivko z imenom Sprememba tipa int smo določili volatile. To moramo storiti zato, da se podatki med izvajanjem prekinitvene rutine ne spremenijo oziroma izgubijo.
V okolju Arduino lahko napišemo program za enake zahteve enostavneje:
/*
Proženje zunanje prekinitve na INT0 (pin 2) s tipko, Arduino Uno.
Avtor: Milan Ivič
April 2015
*/
int LED1 = 4;
int LED2 = 5;
int Tipka1 = 2; //Na pin 2 je priključena tipka preko vezja (Slika 4).
volatile int Sprememba = LOW;
void setup()
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(Tipka1, INPUT); //Pin 2 je vhod, priključena tipka = INT0
/*
Z attachInterrupt() določimo prekinitveno rutino, da jo lahko program pokliče, ko pride do prekinitve.
0 => INT0, pin 2
Preklop => Ime prekinitvene rutine.
RISING => Pri spremembi stanja na INT0 (pinu 2) iz logične 0 na logično 1.
*/
attachInterrupt(0, Preklop, RISING);
void loop()
{
digitalWrite (LED2, HIGH);
delay(200);
digitalWrite (LED2, LOW);
delay(200);
}
void Preklop() //Prekinitvena rutina.
{
Sprememba =!Sprememba;
digitalWrite(LED1, Sprememba);
}