Megszakítások az ATMega328 vezérlőben

Nagy fába vágtam a fejszémet ezzel a leírással. Önmagában egyik megszakítás sem túlságosan bonyolult, de elég sok funkciót kell alaposan megismerni ezek használatához. Épp ezért lassan haladok. Mivel már jelenleg is tekintélyes információ gyűlt össze, félkész állapotban teszem elérhetővé ezt a leírást. Amint elkészülök egy-egy megszakítás típus részletes leírásával, azzal kiegészítem az eddigieket. Ami jelenleg hiányzik:

Programjainkat a megszakítások alkalmazásával sokkal hatékonyabbá tehetjük. Ezért érdemes megismerni ezek részletes működését. Az alábbi leírást az ATMega328 adatlapjának áttanulmányozásával, és az alábbi linkeken fellelt leírások átolvasása után készítettem:

https://www.gammon.com.au/interrupts
https://dronebotworkshop.com/interrupts/

Mivel nem volt szükségem minden lehetőségre a legtöbbet nem próbáltam ki, bár a legfontosabb megszakítások (külső megszakítások, időzítők) használatával már írtam programokat. Azonban itt inkább csak továbbadom a megszerezett elméleti tudást.

Az alapelv reményeim szerint érthető! Amikor egy előre hardveresen kialakított feltétel teljesük, a vezérlő megszakítja az éppen futó kód végrehajtását, elmenti a szükséges regiszterek pillanatnyi értékét a későbbi folytatáshoz. Elugrik egy előre meghatározott memóriacímre, és az ott található programkódot hajtja végre, amit hívjunk megszakítás rutinnak. Ha végzett a megszakítás rutin végrehajtásával, akkor visszatölti a félbehagyott program megszakítási pontját, a regiszterek elmentett értékét, és folytatja a végrehajtást. Ha nem lenne megszakítások használatára lehetőség, kénytelenek lennénk folyamatosan vizsgálni a hardver feltételeket pl. kérdezgetni egy bemenet állapotát, és ez nagyon lelassítaná a programunk végrehajtását. Képzeljük el, hogy néhány óránként valaki megnyom egy nyomógombot, minek hatására a vezérlő felkapcsolja a villanyt. Sok milliószor kellene lekérdezni feleslegesen a nyomógomb állapotát, hogy megállapítsuk, hogy nem történt semmi. Vegyünk egy másik, ennél problémásabb esetet! Tegyük fel, hogy adatokat másolunk egy eeprom-ba, ami köztudottan akár másodpercekig is tarthat. Közben nyomják meg a nyomógombot, de a villany nem fog felkapcsolódni, mert épp az eeprom írással vagyunk elfoglalva, és nem érünk rá kérdezgetni a nyomógombot. Ugye mindenkit meggyőztem, kell a megszakítás!! Azonban van még egy nem teljesen nyilvánvaló indokom is. Az ATMega vezérlőket alvó állapotba lehet küldeni. Ekkor a vezérlő felfüggeszti a program végrehajtást (bár a regiszterekben tárolt értékek és a kimenetek állapota fennmarad), és ezáltal jelentősen lecsökken a vezérlő áramfelvétele. Olyannyira, hogy elemről akár évekig fennmarad ez az alvó állapot a felébresztés és a program végrehajtás folytatásának lehetőségével.  Egyes megszakítások képesek arra, hogy felébresszék a vezérlőt, és ezáltal lényegesen nagyobb áramfelvétel kíséretében folytatódhat a program végrehajtás a következő elalvásig. Ehhez is kell a megszakítás!

Mielőtt belekezdünk a megszakítások használatának részletes megismerésébe, érdemes néhány  fontos tulajdonságot, funkciót átolvasni:

  • A vezérlő hardverében megszakításokat hozhatnak létre külső jelzések a kivezetéseken, és belső állapotok pl. egy időzítő túlcsordulása. A vezérlő reset kivezetése is egy megszakítást okozó bemenet. Ez az egyetlen külső megszakítás amit nem lehet kikapcsolni.
  • Minden megszakítás (kivéve reset) letiltható a vezérlő SREG regiszterének egyik bitjével („I” jelzésű bit). Ha ezt a bitet 0-ra írjuk, akkor semmilyen megszakítás nem fog generálódni.
  • Amikor a vezérlő végrehajt egy megszakítást, automatikusan letilt az SREG regiszter „I” bitjének törlésével minden más megszakítást. Tehát egy folyamatban lévő megszakítást nem lehet megszakítani. Azonban a megszakítás végrehajtásakor ez a bit visszabillenthető, és így ha akarjuk és indokunk van rá, mégis megszakíthatóvá válik egy folyamatban lévő megszakítás is.
  • A megszakításokat használat esetén engedélyezni kell egy megfelelő regiszter valamely bitjével.
  • A vezérlő a megszakítási feltétel teljesülésekor beállít egy „jelzőt”. A megszakítást tulajdonképpen ennek a jelzőnek a bebillenése okozza. Ha engedélyeztük a megszakítást, akkor hívódik meg a megszakítás rutin. A megszakítás rutin befejezésekor a vezérlő általában automatikusan törli a megszakítást kiváltó „jelző”-t, így felkészül a következő megszakítási feltétel fogadására.
  • Ha több megszakítást is engedélyeztünk és egyszerre több megszakítási feltétel is teljesül, akkor a vezérlő egy előre beletervezett prioritási sorrend szerint hajtja végre a megszakításokat, sorban egymás után. Alább található táblázatban a megszakítások felsorolásának sorrendje egyben a prioritási sorrend is. Pl. az INT0 (Arduino D2 kivezetés) bemenet megszakítása magasabb prioritású mint az INT1 (Arduino D3 kivezetés) bemenet megszakítása. Ha egyszerre jön létre a megszakítási feltétel a két bemeneten, akkor az INT0 bemenethez rendelet megszakítási rutin lesz végrehajtva. Annak befejezésekor végrehajtódik az INT1-hez rendelt megszakítási rutin.
  • Ha egy megszakítási feltétel teljesül, a hozzá tartozó megszakítási rutin végrehajtásáig nem tud új végrehajtási feltétel teljesülni. Pl. tegyük fel, hogy az INT0 bemeneten megszakítással szeretnénk a bemenetre érkező felfutó éleket megszámolni. Ha olyan gyorsan érkezik egy megszakítást okozó felfutó él után a következő, hogy az INT0-hoz rendelt megszakítási rutin még nem fejeződött be, akkor az nem fog újabb megszakítást generálni, tehát nem fogjuk tudni megszámlálni!
  • A megszakítási feltétel teljesülése és a megszakítási rutin elindulása között idő telik el. Optimális esetben, ha nincs prioritásban magasabb megszakítás ugyanakkor, és az ISR függvényt használjuk, akkor is minimum 23 órajelciklus telik el. 16Mhz órajel esetén ez 23×62.5nSec=1.4345mikroSec. Ha a külső megszakításokhoz előre elkészített attachInterrupt() függvényt használjuk (lásd később), akkor ez az idő 82 órajel ciklusra növekszik. A megszakítási rutinból a visszatérés a főprogramba 19 órajel ciklust igényel. És nyilván ekkor még egyetlen programsort sem hajtottunk végre a megszakítási rutinban.
  • Általános tanács, hogy a megszakítási rutin legyen minél rövidebb. Nem szabad és nem is lehet pl. sorosport-ra adatokat küldeni megszakítási rutinból. Célszerű általában globális változók értékét megváltoztatni a megszakítási rutinban, és a főprogramban kezelni az eseményt. Így minden lehetséges megszakítás gyorsan érvényre jut, és nem veszítünk el eseményeket. Az Arduino IDE-ben megirt millis() függvény a TIMER0 megszakításait használja, azaz 1milliSec időnként növeli egy számláló értékét. Ha további megszakításokat használunk, akkor a hosszú végrehajtású megszakítás rutinok miatt „időt veszíthetünk”, hiszen végrehajtás közben letiltásra kerül minden más megszakítás, így a TIMER0 megszakításai is. Értelem szerűen a delay()-el is problémák lesznek, hiszen megszakítási rutin végrehajtása közben nem ketyeg a delay() órája! Hasonlóan megszakításokkal dolgozik sorosport (Serial függvényosztály) is. Így egy hosszabb megszakítási rutin miatt adatokat is veszíthetünk az adatok fogadás során.
  • Megszakítási rutin nem tud átvenni paramétereket és nem is tud paramétereket visszaadni. Egyetlen lehetőségünk, ha globális változókat hozunk létre és a megszakítási rutinban ezek értékét változtatjuk meg. Ezzel „üzenhetünk” a főprogramnak. A globális változók használata azonban odafigyelést igényel. Ugyanis a fordító programok általában optimalizálják a kódunkat, és egyes esetekben nem olvassák be a változó értékét a memóriából, ha az éppen egy belső regiszterben rendelkezésre áll. Ha egy megszakítás közben megváltoztatja a változó értékét, arról a főprogram nem tud, hiszen az csak a memóriában változott meg, és nem a belső regiszterben. Azonban a fordítónak megadhatjuk, hogy olyan futtatható kódot készítsen, ami minden felhasználás előtt újra olvassa a változó értékét. Ehhez a változó deklarálásakor írjuk a változó típus elé a „volatile” kulcsszót. Pl. volatile int data=0;
  • Ha egy megszakítás épp nincs engedélyezve, de létrejön a megszakítási feltétel, és azt követően engedélyezzük a megszakítást, akkor a vezérlő „emlékszik” a megszakítási feltételre, azaz az engedélyezéskor azonnal meghívásra kerül az eseményhez rendelt megszakítási rutin. Amennyiben ez problémát okozhat, érdemes a megszakítás engedélyezése előtt törölni a megfelelő regiszterben azt a jelző bitet, ami az adott eseményhez tartozik (pl. TIMER-ben a túlcsordulási jelzőbit).

Alább található az ATMega328 vezérlő megszakítás táblázata. A családba tartozó vezérlők hasonló táblázattal rendelkeznek, de esetenként kevesebb illetve más megszakítás vektorokat tartalmaznak, ezért minden vezérlőnél lásd az adatlapot!

Figyelem! Az adatlap 4, 5, 6 prioritással jelzett sorban található a megszakítást „Pin Changed” megszakításnak nevezi. Számomra azonban sokkal követhetőbbnek tűnt a „port megszakítás„ elnevezés. Ez azonban a saját önkényes elnevezésem! Az adatlapban ezt a funkciót a „Pin Change” elnevezéssel érdemes keresni. 

PrioritásCím vektorISR Interrupt vektor elnevezéseKiváltó esemény
1 0x0000(1) External reset Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset
2 0x0002INT0_vectKülső megszakítási kérelem INT0 kivezetésen, Arduino  D2
3 0x0004INT1_vectKülső megszakítási kérelem INT1 kivezetésen, Arduino D3
40x0006PCINT0_vectB Port megszakítási kérelem (Pin Change), Arduino D8-D13)
50x0008PCINT1_vectC Port megszakítási kérelem (Pin Change), Arduino A0-A5)
60x000APCINT2_vectD Port mgszakítási kérelem (Pin Change), Arduino D0-D7
70x000CWDT_vectWatchdog időzítő megszakítás
80x000ETIMER2_COMPA_vectTimer2 A összehasonlítás egyezé
90x0010TIMER2_COMPB_vectTimer2 B összehasonlítás egyezés megszakítás
100x0012TIMER2_OVF_vectTimer2 számláló túlcsordulás megszakítás
110x0014TIMER1_CAPT_vectTimer1/bemeneti esemény rögzítés megszakítás
120x0016TIMER1_COMPA_vectTimer1 A összehasonlítás egyezés megszakítás
130x0018TIMER1_COMPB_vectTimer1 B összehasonlítás egyezés megszakítás
140x001ATIMER1_OVF_vectTimer1 számláló túlcsordulás megszakítás
150x001CTIMER0_COMPA_vectTimer0 A összehasonlítás egyezés megszakítás
160x001ETIMER0_COMPB_vectTimer0 B összehasonlítás egyezés megszakítás
170x0020TIMER0_OVF_vectTimer0 számláló túlcsordulás megszakítás
180x0022SPI_STC_vectSPI átvitel kész megszakítás
190x0024USART_RX_vectUSART vétel (Rx) kész megszakítás
200x0026USART_UDRE_vectUSART adatregiszter üres megszakítás
210x0028USART_TX_vectUSART adás (Tx) kész megszakítás
220x002AADC_vectADC konverzió kész
230x002CEE_READY_vectEEPROM írás kész megszakítás
240x002EANALOG_COMP_vectÖsszehasonlítási esemény megszakítás
250x0030TWI_vectI2C (TWI) megszakítás
260x0032SPM_READY_vect Store Program Memory kész megszakítás

A fenti táblázat tartalmazza az első oszlopban a megszakítások prioritási sorrendjét. A második oszlop megadja, hogy a memóriában mely kezdőcímen találja a vezérlő a megszakítás létrejöttekor azt az ugrási címet, ahol a megszakítási rutin található. Ezzel általában nem kell foglalkoznunk, mert a harmadik oszlopba írt konstans nevek fognak nekünk kelleni. A negyedik oszlop a megszakítási feltétel szöveges leírása rövidítve, részleteket lásd később.

Megszakítási rutinok elkészítése a programban

Ahhoz, hogy a megszakításokat saját magunk által megírt megszakítási rutinokkal tudjuk lekezelni, egyrészt engedélyezni kell az adott megszakítást, másrészt a programban kötött formátumban el kell helyezni a kódokat a fordító számára. A dolog roppant egyszerű. A megszakítási rutinok formátuma minden esetben a következő:

ISR (megszakítás vektor) {}

Mint láthatjuk, ez nagyon hasonló egy függvényhíváshoz. Nem kell elé void vagy visszatérési típus megadása, mert nem tud visszaadni semmit. A bemenő paraméter a megszakítás vektor neve, amit a táblázatból tudunk kiolvasni. Pl. készítsünk programot, ami be és kikapcsolja az Arduino alaplapon a beépített led-et, amikor lefutó él érkezik az INT0 bemenetre.

volatile bool ledState=false;  //ez a globális változó tárolja, hogy éppen világít-e a led

//megszakítás rutin
ISR (INT0_vect) {
	ledState=!ledState;        //a változót ellentétes logikai értékre állítjuk
	digitalWrite(13,ledState); //beállítjuk a led-et meghajtó kimenetet a változó állapotára
}

setup() {
	pinMode(13,OUTPUT);        //led-et meghajtó kivezetés kimenet
	digitalWrite(13,LOW);      //led nem világit
	pinMode(2,INPUT_POLLUP);   //az INT0 kivezetés bemenet és bekapcsoljuk a felhúzó ellenállást
	//megszakítási feltételek beállítása és megszakítás engedéylezése (részleteket lásd később)
	EICRA &= ~(bit(ISC00) | bit (ISC01));  //megszakítási jelzőbitek törlése
    EICRA |= bit (ISC01);    // lefutó álre történő megszakítási mód beállítása
    EIMSK |= bit (INT0);     // megszakítás engedélyezése
 }

loop() {}

Mint láthatjuk, a fenti programban nincs is kód a loop()-ban. A setup()-ban beállítottuk a 13. kivezetést kimenetnek (ezen van a led), valamint beállítottuk és engedélyeztük az INT0 (Arduino D2 kivezetés) kivezetésre a megszakítást. Ha ezek után egy drótdarabbal összekötjük a GND-t a D2 kivezetéssel, akkor a led villogni fog. Mivel a rögtönzött kacsolónk prellezni fog (prellezésről itt olvashatsz), a drót érintésekor a led biztos, hogy elalszik illetve világítani kezd, hanem szabálytalanul vibrál, ahogy a véletlen generálja a megszakítások sorozatát.

Meg kell jegyezni, hogy az INT0 és INT1 külső megszakításokhoz az Arduino környezetben már előre elkészített megszakítás beállító függvényeket készítettek az alkotók. Így nem szükséges közvetlenül a beállításra szolgáló regisztereket használni, illetve azok működését meg sem kell ismerni. A fenti program ezeknek a függvények a használatával a következőképen néz ki:

//megszakítás rutin
void interrup_rutin() {
	ledState=!ledState;        //a változót ellentétes logikai értékre állítjuk
	digitalWrite(13,ledState); /beállítjuk a led-et meghajtó kimenetet a változó állapotára
}

setup() {
	pinMode(13,OUTPUT);      //led-et meghajtó kivezetés kimenet
	digitalWrite(13,LOW);    //led nem világit
	pinMode(2,INPUT_POLLUP); //az INT0 kivezetés bemenet és bekapcsoljuk a felhúzó ellenállást
	//megszakítás beállítása attachInterrupt() függvénnyel
  	attachInterrupt(digitalPinToInterrupt(2),interrupt_rutin, FALLING); 
 }

loop() {}

A példa programban látható, hogy a megszakítási regiszterek manipulálása helyett egysokkal könnyebben paraméterezhető függvény áll rendelkezésre. Ennek azonban ára van, a megszakítás tutin elindítása lényegesebben hosszabb időt vesz igénybe, hiszen itt is először az ISR függvényt hívja meg a vezérlő, és csak ezt követi a felhasználói programba írt függvény elindítása. Magyarázatra szorul a digitalPinToInterrupt() függvény. Ennek szerepe, hogy a kód könnyebben olvasható és hordozható legyen Arduino környezetben. Az a függvény ugyanis egy megadott kivezetéshez hozzárendeli a megszakítás vezeték megnevezését. Jelen esetben a D2 kivezetést adtuk meg paraméternek (nem „D2”-t írunk csak „2”-t), és így az attachInterrupt() függvény az INT0 kivezetést kapta meg, és állította be megszakításra. Ennek jelentősége, hogy más vezérlőkön at INT0 megszakítás vezeték nem feltétlenül a D2 kivezetéshez tartozik. Lényegében jobban olvashatóvá vált a kódunk! Az attachInterrupt() függvény további paraméterei a megszakítást lekezelő függvény neve (jelen példában „interrupt_rutin”), valamint a megszakítást kiváltó esemény, ami most a lefutó él (FALLING).

Az ATMega328 vezérlő kivezetései közül szinte mindegyik képes megszakításokat generálni. Azonban csak két kivezetés van, amit négyféle megszakítási módban tudunk beállítani. Ezek az INT0 és INT1 kivezetések! A többit kicsit kényelmetlenebbül, pormegszakításokkal tudjuk felhasználni. Ez azt jelenti, hogy a megszakítási rutinnak kell kikeresnie, hogy melyik kivezetés miatt történt a megszakítási rutin elindítása. Mivel az INT0 és INT1 kivezetésekhez közvetlenül hozzárendelhető megszakítás rutin, ezek elindítása sokkal gyorsabb is, hiszen nem kell kideríteni, hogy melyik kivezetés okozta a megszakítást.

Nézzük most az INT0 és INT1 kivezetésünket. Ezen a két kivezetésen meghatározható, hogy a megszakítást a fel vagy lefutóél, jelszint változás, illetve alacsony jelszint idézze elő. Ha a megszakítás engedélyezve van bármelyik kivezetésen, a megszakítások akkor is aktiválódnak, ha a kivezetés kimenetnek van beállítva. Ez a funkció lehetővé teszi szoftvermegszakítás létrehozását.

A megszakítás aktiválásának feltételeit az EICRA regiszter bitjeinek beállításával lehet meghatározni. Az INT0 és INT1 kivezetések fel/lefutó él valamint a jelszint változás érzékeléséhez szükség van a vezérlő I/O órajelének meglétére, mert a változás felismeréséhez az órajel alapján mintát vesz a rendszer, és a következő órajelnél ismeri fel a változást. Ebből logikusan következik, hogy amennyiben ezeket a kivezetéseket a vezérlő alvó módból történő felébresztésére akarjuk használni, azt az alacsony jelszint érzékeléssel illetve megszakítással lehet megvalósítani, ugyanis az alacsony szint megszakítása aszinkron módon keletkezik. Ez azt jelenti, hogy ez a megszakítás nem igényel órajelet, ezért használható az alkatrész felébresztésére az üresjárati üzemmódtól eltérő alvó üzemmódokból is. Az I/O-órajel minden alvó üzemmódban leáll, kivéve a üresjárati üzemmódot.

Ha az alacsony jelszint által kiváltott megszakítást használjuk a kikapcsolásból való felébresztéshez, a szükséges szintet elég hosszú ideig kell tartani ahhoz, hogy az vezérlő befejezze az ébresztést a szintmegszakítás aktiválásához. Ha a szint eltűnik az indítási idő vége előtt, a vezérlő felébred, de nem jön létre megszakítás. Az indítási időt a SUT és a CKSEL biztosíték bitek határozzák meg. Ennek részleteihez lásd az adatlapot.

Az INT0 és INT1 kivezetésekhez tartozó megszakítások kiemelt szerepet játszanak, mert az itt megvalósított hardver lehetővé teszi a kivezetésekhez közvetlenül hozzárendelt megszakítási rutin futtatását. Így a beállításra szolgáló regiszterekben mindkét kivezetéshez külön-külön tartoznak vezérlőbitek és külön megszakítási vektor is. Valamint ezen a két kivezetésen a megszakítási mód is beállítható (fel és lefutó él, illetve alacsony vagy magas szint).

A külső megszakításvezérlő regiszterben megszakításérzékelés módja állítható be mindkét kivezetéshez.

  • 7..4 bit – Fenntartott bitek Ezek a bitek az ATmega48PA/88PA/168PA/328P nem használt bitjei, és mindig nullaként jelennek meg.
  • 3., 2. bit – ISC11, ISC10: INT1 kivezetés megszakításérzékelés módját meghatározó bitek. Négyféle megszakítás érzékelési módot lehet beállítani ezzel a két bittel az alábbi táblázat szerint:

A megszakítás generálás során Az INT1 kivezetésen lévő értékből mintát vesz a rendszer az élek észlelése előtt. Ha az él vagy a jelszintváltozás megszakítása van beállítva, az egy órajelnél hosszabb ideig tartó impulzusok fognak megszakítást generálni, a rövidebb impulzusok nem garantáltan okoznak megszakítást. Ha alacsony szintű megszakítást választunk, akkor az alacsony szintet tartani kell az aktuálisan végrehajtott utasítás befejezéséig, hogy biztosan megszakítást generáljunk.

A megszakítás generálásának feltétele még, hogy a SREG regiszterben a globális megszakítások engedélyezve legyenek („I” bit) és a EIMSK regiszterben a megfelelő megszakítási maszk be legyen állítva.

1., 0. bit – ISC01, ISC00 INT0 kivezetés megszakításérzékelés módját meghatározó bitek. Négyféle megszakítás érzékelési módot lehet beállítaniezzel a két bittel az alábbi táblázat szerint:

A megszakítás generálás során Az INT0 kivezetésen lévő értékből mintát vesz a rendszer az élek észlelése előtt. Ha az él vagy a jelszintváltozás megszakítása van beállítva, az egy órajelnél hosszabb ideig tartó impulzusok fognak megszakítást generálni, a rövidebb impulzusok nem garantáltan okoznak megszakítást. Ha alacsony szintű megszakítást választunk, akkor az alacsony szintet tartani kell az aktuálisan végrehajtott utasítás befejezéséig, hogy biztosan megszakítást generáljunk.

A megszakítás generálásának feltétele még, hogy a SREG regiszterben a globális megszakítások engedélyezve legyenek („I” bit) és a EIMSK regiszterben a megfelelő megszakítási maszk be legyen állítva.

  • 7..2. bit – Fenntartott bitek. Ezek a bitek az ATmega48PA/88PA/168PA/328P nem használt bitjei, és mindig nullaként jelennek meg.
  • 1. bit – INT1 külső megszakításkérés engedélyezése. Ha az INT1 bit be van billentve (egybe írtuk) és SREG regiszterben a globális megszakítások engedélyezve vannak („I” bit), akkor az INT1 külső kivezetés megszakítás engedélyezve van. Az EICRA regiszter ISC11 és ISC10 bitje határozza meg, hogy a külső megszakítás az INT1 kivezetésen megjelenő fel vagy lefutó él vagy jelszint változás, illetve alacsony jelszintnél aktiválódik-e a megszakítás. A kivezetésen történő jelszint változások megszakítási kérést okoznak akkor is, ha az INT1 kimenetként van konfigurálva.
  • Bit 0 – INT0 külső megszakításkérés engedélyezése. Ha az INT0 bit be van billentve (egybe írtuk) és SREG regiszterben a globális megszakítások engedélyezve vannak („I” bit), akkor az INT0 külső kivezetés megszakítás engedélyezve van. Az EICRA regiszter ISC01 és ISC00 bitje határozza meg, hogy a külső megszakítás az INT0 kivezetésen megjelenő fel vagy lefutó él vagy jelszint változás, illetve alacsony jelszintnél aktiválódik-e a megszakítás. A kivezetésen történő jelszint változások megszakítási kérést okoznak akkor is, ha az INT0 kimenetként van konfigurálva.
  • 7..2. bit – Fenntartott bitek. Ezek a bitek az ATmega48PA/88PA/168PA/328P nem használt bitjei, és mindig nullaként jelennek meg.
  • 1. bit – INTF1 az INT1 külső kivezetés megszakításjelzője. Amikor az INT1 tű fel vagy lefutóéle, vagy jelszint változása illetve alacsony jelszintje megszakítási kérést vált ki, az INTF1 egybe billen. Ha a SREG regiszterben a globális megszakítások engedélyezve vannak („I” bit), az EIMSK-ban pedig az INT1 bit egyre van beállítva, a vezérlő a megfelelő megszakításvektorra ugrik. A jelző törlődik a megszakítási rutin végrehajtásakor. Alternatív megoldásként jelző logikai 1 beírásával is törölhető. Ez a jelző mindig törlődik, ha az INT1 alacsony szint megszakításra van konfigurálva.
  • Bit 0 – INTF0 az INT0 külső kivezetés megszakításjelzője. Amikor az INT0 tű fel vagy lefutóéle, vagy jelszint változása illetve alacsony jelszintje megszakítási kérést vált ki, az INTF0 egybe billen. Ha a SREG regiszterben a globális megszakítások engedélyezve vannak („I” bit), az EIMSK-ban pedig az INT1 bit egyre van beállítva, a vezérlő a megfelelő megszakításvektorra ugrik. A jelző törlődik a megszakítási rutin végrehajtásakor. Alternatív megoldásként jelző logikai 1 beírásával is törölhető. Ez a jelző mindig törlődik, ha az INT0 alacsony szint megszakításra van konfigurálva.

Példa program a INT0 illetve INT1 külső megszakítás használatára

A példa programunk már látható volt a leírás első részében. Most ugyanazt a programot vesszük elő, csak megmagyarázzuk a setup() részben található regisztermanipulációkat.

volatile bool ledState=false;  //ez a globális változó tárolja, hogy éppen világít-e a led

//megszakítás rutin
ISR (INT0_vect) {
	ledState=!ledState;        //a változót ellentétes logikai értékre állítjuk
	digitalWrite(13,ledState); //beállítjuk a led-et meghajtó kimenetet a változó állapotára
}

setup() {
	pinMode(13,OUTPUT);      //led-et meghajtó kivezetés kimenet
	digitalWrite(13,LOW);    //led nem világit
	pinMode(2,INPUT_POLLUP); //az INT0 kivezetés bemenet és bekapcsoljuk a felhúzó ellenállást
	//megszakítási feltételek beállítása és megszakítás engedélyezése (részleteket lásd később)
	EICRA &= ~(bit(ISC00) | bit (ISC01));  //megszakítási jelzőbitek törlése
    EICRA |= bit (ISC01);                  // lefutó álre történő megszakítási mód beállítása
    EIMSK |= bit (INT0);                   // megszakítás engedélyezése
}

loop() {}

A programnak nincs loop() kódja, hiszen a megszakítási esemény hatására (lefutó él a D2 kivezetésen) lefutó megszakítás rutin végzi el a feladatokat

Port megszakítások (Pin Change) kezelése

Mint azt már említettem, a legtöbb kivezetés felhasználható megszakítások generálására. Ekkor azonban nincs lehetőségünk az INT0 és INT1 kivezetésnél leírt közvetlenül hozzárendelt megszakítási rutin meghívására. Azt azonban be tudjuk állítani, hogy a kiválasztott portnak mely kivezetései okozzanak megszakítást. Viszont az adott porthoz csak egyetlen megszakítás rutint tudunk hozzárendelni. Ez egyben azt is jelenti, hogy a megszakítás rutinnak ki kell derítenie, hogy melyik kivezetés okozta a megszakítást. Ez csak úgy lehetséges, ha a program valamilyen módon folyamatosan nyilvántartja a kivezetések állapotát, és a megszakítás rutinban összehasonlítja, hogy melyik változott meg. Jó hír viszont, hogy a kivezetések megszakítást okoznak akkor is ha a jelszint 0-ról 1-re változik, és akkor is ha 1-ről 0-ra. Tehát pl. egy nyomógomb megnyomásakor és elengedésekor is meghívja a vezérlő a porthoz rendelt megszakításrutint.

A port megszakítások beállítására szolgáló PCINT regiszterben minden egyes porthoz (B, C és D portok) tartozik egy bit. Ezzel a bittel beállítható, hogy az adott port kivezetéseit figyelje-e a rendszer, és megszakítást generáljon valamelyik kivezetés jelszintjének változásakor. Ezen felül minden egyes porthoz tartozik egy mask regiszter (PCMSK0, PCMSK1, PCMSK2), melyekben minden kivezetéshez tartozik egy bit, amivel beállítható, hogy az adott kivezetés generálhasson-e portmegszakítást (részleteket lásd később). Valamint a PCIFR regiszterben minden egyes porthoz tartozik egy jelző bit, ami bebillen amikor a kijelölt kivezetések valamelyikén jelszint változás történik. Ekkor meghívódik a megszakítás rutin is.

A rendszer aszinkron módon észleli a 0-t. Ez azt jelenti, hogy ezek a megszakítások felhasználhatók a vezérlő felébresztésére bármelyik alvó üzemmódból.

Figyelem! Az adatlap ezt a megszakítást „Pin Changed” megszakításnak nevezi. Számomra azonban sokkal követhetőbbnek tűnt ennek a „port megszakítás „elnevezés. Ez azonban a saját önkényes elnevezésem! Az adatlapban ezt a funkciót a „Pin Change” elnevezéssel érdemes keresni.  

  • 7..3 bit – Fenntartott bitek. Ezek a bitek az ATmega48PA/88PA/168PA/328P nem használt bitjei, és mindig nullaként jelennek meg.
  • 2. bit – PCIE2 D port megszakítás engedélyezés. Ha a PCIE2 bit egybe van állítva SREG regiszterben engedélyezve vannak a globális megszakítások („I” bit), akkor D port megszakítás engedélyezve van. Bármely engedélyezett kivezetésen (lásd később a PCIMSK2 regisztert) történő jelszint változás megszakítást okoz. A megszakítás a „PCINT2_vect” által meghatározott megszakítás rutint hívja meg.
  • 1. bit – PCIE1 C port megszakítás engedélyezés. Ha a PCIE1 bit egybe van állítva SREG regiszterben engedélyezve vannak a globális megszakítások („I” bit), akkor C port megszakítás engedélyezve van. Bármely engedélyezett kivezetésen (lásd később a PCIMSK1 regisztert) történő jelszint változás megszakítást okoz. A megszakítás a „PCINT1_vect” által meghatározott megszakítás rutint hívja meg.
  • 0. bit – PCIE0 B port megszakítás engedélyezés. Ha a PCIE0 bit egybe van állítva SREG regiszterben engedélyezve vannak a globális megszakítások („I” bit), akkor C port megszakítás engedélyezve van. Bármely engedélyezett kivezetésen (lásd később a PCIMSK0 regisztert) történő jelszint változás megszakítást okoz. A megszakítás a „PCINT0_vect” által meghatározott megszakítás rutint hívja meg.
  • 7..3 bit – Fenntartott bitek. Ezek a bitek az ATmega48PA/88PA/168PA/328P nem használt bitjei, és mindig nullaként jelennek meg.
  • 2. bit – PCIF2 D port megszakítási jelző. Ha a D port bármely PMSK2 regiszterében engedélyezett kivezetés jelszintje megváltozik, és az SREG regiszterben a globális megszakítások engedélyezve vannak, illetve a PCICR regiszter PCIE2 bitje is egybe lett állítva, akkor ez az esemény megszakítást eredményez. A megszakítás a „PCINT2_vect” által meghatározott megszakítás rutint hívja meg. A jelző törlődik a megszakítási rutin végrehajtásakor. Alternatív megoldásként a jelző törölhető, ha logikai egyet írunk erre a bitre.
  • 1. bit – PCIF1 C port megszakítási jelző. Ha a C port bármely PMSK1 regiszterében engedélyezett kivezetés jelszintje megváltozik, és az SREG regiszterben a globális megszakítások engedélyezve vannak, illetve a PCICR regiszter PCIE1 bitje is egybe lett állítva, akkor ez az esemény megszakítást eredményez. A megszakítás a „PCINT1_vect” által meghatározott megszakítás rutint hívja meg. A jelző törlődik a megszakítási rutin végrehajtásakor. Alternatív megoldásként a jelző törölhető, ha logikai egyet írunk erre a bitre.
  • 0. bit – PCIF0 B port megszakítási jelző. Ha a B port bármely PMSK0 regiszterében engedélyezett kivezetés jelszintje megváltozik, és az SREG regiszterben a globális megszakítások engedélyezve vannak, illetve a PCICR regiszter PCIE0 bitje is egybe lett állítva, akkor ez az esemény megszakítást eredményez. A megszakítás a „PCINT0_vect” által meghatározott megszakítás rutint hívja meg. A jelző törlődik a megszakítási rutin végrehajtásakor. Alternatív megoldásként a jelző törölhető, ha logikai egyet írunk erre a bitre.

Bit 7..0 – PCINT7..0 B port kivezetések engedélyezése. A fenti táblázatból beazonosítható, hogy melyik bit, melyik kivezetés engedélyezésért felel. Amennyiben egy kivezetéshez tartozó bitet bebillentünk, és az SREG regiszterben a globális megszakítások engedélyezve vannak, valamint a PCICR regiszterben a PCIE0 bit is be van billentve, a kivezetésen történő jelszint változás megszakítást generál. A táblázatban látható, hogy az órajelgenerátorhoz szükséges külső kvarc kristály számára fenntartott kivezetések is képesek megszakítást generálni. Ez nyilván akkor lehetséges, ha a vezérlő a belső RC oszcillátorról üzemel, és a kvarc kristályra nincs szükség. Ekkor ezek a kivezetések normál I/O kivezetésként felhasználhatók. Arduino alaplapokon a beépített kvarc miatt ez nem lehetséges.

  • 7. bit – Fenntartott bit. Ez a bit egy nem használt bit az ATmega48PA/88PA/168PA/328P-ben, és mindig nullaként jelenik meg.
  • Bit 6..0 – PCINT14..8 C port kivezetések engedélyezése. A fenti táblázatból beazonosítható, hogy melyik bit, melyik kivezetés engedélyezésért felel. Amennyiben egy kivezetéshez tartozó bitet bebillentünk, és az SREG regiszterben a globális megszakítások engedélyezve vannak, valamint a PCICR regiszterben a PCIE1 bit is be van billentve, a kivezetésen történő jelszint változás megszakítást generál. A táblázatban látható, hogy a RESET kivezetésnek is megfelel egy bit.
  • Bit 7..0 – PCINT23..16 D port kivezetések engedélyezése. A fenti táblázatból beazonosítható, hogy melyik bit, melyik kivezetés engedélyezésért felel. Amennyiben egy kivezetéshez tartozó bitet bebillentünk, és az SREG regiszterben a globális megszakítások engedélyezve vannak, valamint a PCICR regiszterben a PCIE2 bit is be van billentve, a kivezetésen történő jelszint változás megszakítást generál.

A táblázatban látható, hogy  az INT0 és INT1 külső megszakításokhoz tartozó Arduino D2 és D3 kivezetésekhez is tartozik engedélyező bit. Ez azt jelenti, hogy ezekhez a kivezetésekhez kétféle módon is megszakítást rendelhetünk. Kipróbáltam, hogy mindkét megszakítást beállítottam ezekre a kivezetésekre. Mindkét megszakítás rutin meghívásra került, de a port megszakítás nem minden esetben történt meg. Ha azonban nem lett engedélyezve az INT0 illetve INT1 megszakítás, akkor a port megszakítások hibátlanul működtek. Mivel az adatlapban nem találtam meg az erre vonatkozó információkat, feltételezem, hogy a kétféle megszakítási lehetőségből egyszerre csak az egyik használható.

A Watchdog egység rendelkezik egy 128Khz-s RC oszcillátorral, valamint egy számlálóval. Ha a Watchdog működése engedélyezve van és a számláló elér egy előre beállított értéket, akkor megszakítást generál, illetve beállítástól függően reset jelet állít elő a vezérlő számára. A Watchdog részletes működéséről itt lehet olvasni.

Összefoglalásul a működés röviden a következő:
Megszakítás csak akkor keletkezik, ha a Watchdog számláló túlcsordul és a vezérlő WDTON fuse bitje 1-re van állítva és a Watchdog üzemmód a WDTCSR regiszterben a WDIE bitjének 1 értékével megszakítás generálásra van beállítva (a WDE bit felel a reset jel generálásáért, illetve ha a WDTON fise bit 0, akkor csak reset keletkezik). Valamint megszakítás keletkezésnek feltétele még, hogy az SREG regiszterben a globális megszakítások engedélyezve legyenek („I” bit). Mindaddig nem keletkezik megszakítás, amig a felhasználói program a számláló túlcsordulása előtt rendszeresen törli annak értékét.

A Watchdog beállítására a WDTCSR regiszter szolgál. Ebben beállítható az előosztó, amivel az időzítés (időtúllépés) időtartama beállítható 16 millisec és 8 másodperc között. Valamint ebben a regiszterben található még egy módosítás engedélyező bit és egy reset generálását engedélyező bit. A megszakítások szempontjából azonban csak két bitnek van jelentősége:

WDIF bit: Watchdog megszakítás jelző. Ez a bit akkor kerül beállításra, amikor időtúllépés történik a Watchdog Timer-ben, és a Watchdog megszakításra van beállítva. A WDIF-et a hardver törli a megfelelő megszakításkezelő vektor végrehajtásakor. Alternatív megoldásként a WDIF törléséhez nullát kell írni a regiszter ezen bitjére. Ha a vezérlő SREG regiszterében a megszakítás globálisan engedélyezve van, és WDIE bit is be van állítva, a Watchdog időtúllépési megszakítása végrehajtásra kerül a jelző bebillenésekor.

WDIE bit: Watchdog megszakítás engedélyezése. Ha ezt a bitet egybe írjuk, és a vezérlő SREG regiszterében a megszakítások engedélyezve vannak, akkor a Watchdog megszakítás engedélyezve van. Ha a WDE bit nincs beállítva, de a WDIE bit be van állítva, időtúllépés esetén a megszakítás végrehajtásra kerül. Ha WDE is be van állítva, a Watchdog időzítő megszakítás és rendszer-visszaállítás módban van. Ekkor a Watchdog Timer első időtúllépése WDIF-et állít be. A megfelelő megszakításvektor végrehajtásakor a hardver automatikusan törli a WDIE-t és a WDIF-et (a Watchdog rendszer-visszaállítási módba lép). Ez a Watchdog Timer biztonságának megőrzéséhez szükséges a megszakítás használata közben. Ahhoz, hogy megszakítási és rendszer-visszaállítási módban maradjon, a WDIE-t minden megszakítás után be kell állítani. Ezt azonban nem szabad magán a megszakítási szolgáltatási rutinon belül megtenni, mivel ez veszélyeztetheti a Watchdog rendszer-visszaállítási mód biztonsági funkcióját. Ha a megszakítás nem történik meg a következő időtúllépés előtt, a rendszer visszaállítja a rendszert.

Példa program a Watchdog időtúllépési megszakítás beállításához

Az alábbi példaprogramban létrehozunk egy ISR megszakítási rutint, ami az időtúllépés esetén felkapcsolja az Arduino 13. kivezetésére kapcsolt LED-et, ha a D2 bemenetre kapcsolt nyomógombot nem nyomja meg 8 másodpercen belül a felhasználó. A felkapcsolt LED-et a nyomógomb lenyomása minden esetben törli. A példa program leírt működéshez a WDTON fuse bitet 1-re kell állítani (programozatlan állapot, ez az alapértelmezett, tehát nem kell csinálni semmit, ha még az adott Arduino alaplapunkon illetve egy ATMega328 chip-en soha nem változtattuk meg). Engedélyezni kell a Watchdog-ot megszakítás módba (WDTCSR regiszterben: WDIE=1 és WDE=0), be kell állítani az előosztót 8 másodperces időt ülepesre (WDTCSR regiszterben: WDP3=1, WDP2=0, WDP1=0, WDP0=1). Valamint a forráskódban létre kell hozni az ISR() megszakítás rutint.

//megszakítás rutin
ISR(WDT_vect) {
  	digitalWrite(13,HIGH);       //bekapcsoljuk a LED-et
}

void setup() {
    pinMode(13,OUTPUT);          //led-et meghajtó kivezetés kimenet
    digitalWrite(13,LOW);        //led nem világit
    pinMode(2,INPUT_PULLUP);     //a D2 kivezetés bemenet és bekapcsoljuk a felhúzó ellenállást
  
    //watchdog megszakítás beállításának lépései ----------------
    cli();                            //globálisan tiltunk minden megszakítást, mert a Watchdog 
      	                              //beállításokat nem szabad megszakítani
  	__asm__ __volatile__ ("wdr");     //beillesztünk a kódba egy assembler utasítást ami törli a Watchdog számlálót
    WDTCSR |= (1<<WDCE) | (1<<WDE);   //WDCE és WDE egybe állítása, előosztó bitek megtartásával, 
                	                  //ez engedélyezi az előosztó beállítást
  	WDTCSR = B01100001;               //WDT reset kikapcsolás, WDT megszakítás bekapcsolása, 
                                      //előosztó beállítás 8S időzítésre (WDE=0, WDIE=1, WDP3=1, WDP0=1)
    sei();                            //globális megszakítás engedélyezés
    //Watchdog beállításának vége ----------
}

void loop() {
    if (digitalRead(2)==LOW) {         //megnyomtuk a nyomógombot
        digitalWrite(13,LOW);          //lekapcsoljuk a LED-et (nem tudjuk, hogy fel volt-e kapcsolva)
        __asm__ __volatile__ ("wdr");  //töröljük a Watchdog számlálót 
    }
}

Annyi megjegyzést fűznék még hozzá, hogy tapasztalatom szerint (az adatlap általam megértett leírásával ellentétben) a Watchdog előosztó beállításának megváltoztatásához a WDCE és a WDE bitet is egyszerre egybe kell írni egy írási művelettel, és ezt követő 4 órajel cikluson belül lehet a regiszterbe beállítani előosztó bitjeit. Az előosztó bitjeivel együtt ugyanabban az írási műveletben kikapcsolhatjuk a Watchdog reset engedélyezést (WDE bit=0), valamint bebillenthetjük a WDIE bitet egybe, ami engedélyezi a Watchdog megszakítást. Ha mindezt megtesszük, akkor programok mindaddig kikapcsolva tartja a LED-et, amig minimum 8 másodpercenként (vagy annál rövidebb időközönként) nyomkodjuk a D2-re kötött nyomógombot. Ha többet várunk mint 8 sec, akkor a LED világítani kezd. Ez egyébként a klasszikus mozdonyvezető éberség figyelő, ami leállítja a vonatot, ha a mozdonyvezető elaludt és ezért nem nyomta meg az éberség figyelő nyomógombot.

Az ATMega328 vezérlőben 3 TIMER egység található. A hasonló AVR vezérlőkben ez a szám változhat, pl. az ATMega8-ban csak kettő van. A TIMER egységek alapelvükben azonosak, de működésük több kisebb nagyobb részletben különbözik. Pl. a TIMER1 egység 16 bites, míg a TIMER1 és TIMER2 8 bites számlálót tartalmaz. Ezek a különbségek különböző időzítési és számlálási feladatokra teszik alkalmassá ezeket az egyégeket, melyekről részletes leírás található itt.

Jelen leírásban csak az időzítők által generált megszakításokkal foglalkozunk. Mindhárom időzítő azonos módon működik ebből a szempontból. Összefoglalásul a működés a következő: A TIMER egységek tartalmaznak egy számláló egységet. A számláló bemenetére érkező órajelet egy előosztóval lehet széles határok között beállítani. A számláló amikor a TIMER egység enegedélyezve van, folyamatosan számol. Egy idő után természetesen a számláló túlcsordul, és újra 0-tól kezd számolni felfelé. Túlcsorduláskor keletkezhet egy megszakítás! Ezen kívül elhelyeztek két regisztert (A és B komparátor regiszterek), melybe beállíthatunk egy értéket. Amennyiben a számláló aktuális értéke elére valamelyik komparátor regiszter értékét, megszakítás generálódik. A komparátor egyezés esetén sokféle módon működhet a TIMER egység (pl. törli a számlálót, vagy éppen tovább számol a számláló stb.), de ezekkel a részletekkel az említett leírás foglalkozik.

A megszakítások szempontjából két regiszter fontos számunkra. A TIMSKx és TIFRx regiszter, ahol az x az adott TIMER egység sorszáma. Pl. a TIMER0-hoz a TIMSK0 és a TIFR0 regiszterek tartoznak. Lássuk elősször ezen regiszterek bitjeinek jelentését:

TIMSKx – megszakítás maszk regiszter. Ennek a regiszternek három bitje a három lehetséges megszakítás (számláló túlcsordulás, A komparátor egyezés, B komparátor egyezés) engedélyezésért felel. Ha az illető bit 1, akkor lesz megszakítás, ha 0 akkor nem.

Az 5. bitnek csak a TIMER1 esetén van funkciója. Az előzőekben említett rögzítési üzemmód esetén ezzel a bittel lehet engedélyezni, az ehhez kapcsolódó megszakítást. Részletekért lásd az adatlapot.

TIFRx – megszakítás jelző regiszter. Ennek a regiszternek három bitje a lehetséges három esemény  bekövetkeztének jelzésére szolgál: számláló túlcsordulás, A komparátor egyezés, B komparátor egyezés. Független ezen bitek bebillenése attól, hogy a hozzá tartozó megszakítás engedélyezve lett-e! A bit mindenképpen 1-be billen, ha az esemény bekövetkezik. Ha van megszakítás, akkor a megszakítás program meghívásakor a jelző automatikusan törlődik. Ha nem engedélyeztünk megszakítást, akkor a jelzőt nekünk kell törölni azzal, hogy kiolvassuk a regiszter tartalmát.

Az 5. bitnek csak a TIMER1 esetén van funkciója. Részletekért lásd az adatlapot.

Mindezek után azt hiszem beazonosthatóak a megszakítás vektorok is. Pl. a TIMER0 egység három megszakítással rendelkezik TIMER0_OVF_vect (túlcsordulás megszakítás),  TIMER0_COMPA_vect (A komparátor egyezés) és TIMER0_COMPB_vect (B komparátor egyezés). Természetesen a megszakítások használata azonos az előzőekben leírtakkal, azza be kell állítani az adott TIMER egység megfelelő üzemmódjait, engedélyezni kell a megszakítást, és el kell készíteni a programban a megszakítás rutint.

Az említett leírásban több példa program is található. Itt most kiemeltem az egyiket példának. Ebben a példa programban folyamatosan indítgatja az időzítő a megszakításhoz rendelt programot, és minden indításkor ki illetve be kapcsoljuk a beépített led-et (ez a klasszikus blink program máshogy). Legyen a változások között eltelt idő 1 másodperc. Többféle lehetőségünk van arra, hogy az egyes megszakítások között eltelt idő azonos legyen. Egyik megoldás szerint (ezt fogjuk most megírni), beállítunk a TCNT1 regiszterbe egy előre kiszámított értéket, és a megszakítás programrészben mindig ugyanazt írogatjuk be ugyanebbe a regiszterbe. Így a számlálás minden esetben ugyanarról az értékről indul, azaz ugyanannyi időt vesz igénybe, hogy a számláló túlcsorduljon és meghívja a túlcsordulás megszakítást.

ISR(TIMER1_OVF_vect) // ez a megszakításkor lefutó programrész 
{ 
    TCNT1=49911;  //Az időzítő számlálójának mindig ugyanazt a kezdő értéket adjuk (túlcsordulásig
                  //15625 tick telik el, ami 1024-es előosztóval pont 1 sec
   	digitalWrite(13, !digitalRead(13)); //beépített LED felkapcsolása, ha előtte nem világított, és 
                                        //kikapcsolás ha világított
}

void setup()
{
pinMode(13,OUTPUT);          //beépített led kivezetése kimenet
digitalWrite(13,LOW);        //led nem világít
    	cli();               //az időzítő beállításának idejére tiltjuk a megszakításokat
    	TCCR1A=0;            //A kontroll regiszter beállítás (ez egyszerűbb időzítéseknél általában 0)
    	TCCR1B=B00000101;    //beállítjuk az 1024-es előosztót, időzítő ettől kezdve számol
    	TCNT1=49911;         //időzítő számláló kezdőértékének beállítása
    	TIMSK1=B00000001;    //engedélyezzük a túlcsordulás megszakítást
    	sei();               //vége a beállításnak, engedélyezzük a megszakításokat
}

void loop() {
}

Az SPI egység ezzel a megszakítással képes jelezni, ha befejeződött az átvitel. Részletes leírás található itt, ami a megszakítás működését is tartalmazza. Mivel ez a leírás kevés információt tartalmaz a kapcsolódó megszakításokról, tervezem ennek elkészítését.

Az USART egység, tekintve, hogy elég lassú átvitelről van szó, több ponton is képes megszakításokkal jelezni az eseményeket. Megszakítás keletkezhet, ha üres az adatregiszter, és beleírhatjuk a következő átvinni kívánt adatot. Megszakítás jelzi azt is, ha befejeződött egy adat továbbítása, illetve megszakítás jelzi ha egy érvényes adat érkezett a vevő egység pufferébe. Az USART működéséről már készítettem egy részletes leírást, ami megtalálható itt.

Az analóg digitális konverzió is a lassú folyamatok közé tartozik, ezért programunkban nem szükséges megvárni az átalakítás végét. Ez a megszakítás akkor jön létre, ha elkészült a vezérlő az átalakítással és az eredmény a megfelelő regiszterekben várakozik. A későbbiekben ennek az eszköznek a működéséről és a kapcsolódó megszakításokról is tervezem részletes leírás készítését.

Az EEPROM memória byte-onként írható és olvasható. ajnos az adatok írása az EEPROM működési elve miatt hosszú folyamat, kb 3.4millisecundum ideig tart. Ezért ha a felhasználói program lehetővé teszi, megfontolandó az „EEPROM írás kész”  megszakítás használata. Az EEPROM működési elvéről itt található részletes leírás, valamint az EEPROM használatához szükséges belső regiszterek és az írási folyamat részletesen megtalálható itt!

Mielőtt ezte a megszakítást részleteznénk, foglaljuk össze az EEPROM írásához és olvasásához szükséges regisztereket.

Ebben a regiszterben kell beállítani az írni illetve olvasni kívánt EEPROM memória byte címét.

Ebbe a regiszterbe kell az EEPROM írás megkezdése előtt beírnunk a tartalmat, amit az EEPROM memória byte-ban tárolni szeretnénk. Olvasáskor ide kerül a megcímzett memória byte tartalma

A regiszter többi bitjének jelentéséről részletes leírás található itt. Összefoglalva ezek jelentését:

  • 7,6 bitek: fenntartott bitek
  • EEPM1 és EEPM0 bitek: EEPROM programozási mód bitek (Törlés, írás vagy törlés és írás egy lépésben)
  • EERIE bit: EEPROM Ready megszakítás engedélyezése. Részletesen lásd lejjebb.
  • EEMPE bit: EEPROM Master írási engedélyezés. Ezt be kell billenteni az írás megkezdése előtt a véletlen adat felülírás érdekében.
  • EEPE bit: EEPROM írási engedélyezés. Ennek a bitnek a bebillentése indítja el az EEPROM írását. Automatikusan törlődik ha kész az írás, ekkor keletkezik a megszakítás is, ha EERIE bit egy.
  • EERE bit: EEPROM olvasás engedélyezése. Ennek a bitnek a bebillentése indítja el az EEPROM memória byte kiolvasását.

Számunkra a megszakítások szempontjából az EERIE bit érdekes. Az EERIE bit bebillentése (egybe írása) lehetővé teszi az EEPROM Ready megszakítást, ha az SREG regiszterben a globális megszakítások engedélyezve vannak. Az EERIE bit törlése letiltja a megszakítást. Az EEPROM Ready megszakítás akkor jön étre, amikor az EEPE bit törlődik az EEPROM-ba írás befejezésekor. A megszakítás nem jön létre ha egy előző EEPROM írása vagy flash programozás van folyamatban.

Az analóg komparátor amint az nevében is benne van, analóg jelek összehasonlítására szolgál. Ez a megszakítás teszi lehetővé, hogy a vezérlő megfelelő kivezetésére kapcsolt analóg feszültség jel változása során megszakítás keletkezzen. Ez az esemény jelezheti, ha az egyik feszültség nagyobb lesz mint a másik, vagy ha éppen kisebb lesz mint a másik, illetve akkor is ha változik az összehasonlított feszültségek viszonya. Még nem készült el ennek a megszakításnak a részletes leírása, illetve az analóg komparátor működésével sem foglalkoztam.

Az I2C átvitelt biztosító TWI egység igen összetett működésű, és különféle állapotok jönnek létre az átvitel során. Ez a megszakítás egyes állapotokban keletkezik! A megszakítási rutinnak le kell kérdezni az éppen aktuális állapotot, és az állapottól függő kódrészletet kell végrehajtania. A megszakításokra vonatkozó részletesebb leírás még folyamatban, de magáról a TWI egységről már készítettem leírást, ami a megszakításokkal is foglalkozik. Az említett leírás itt található.

Ez a megszakítás a bootloader funkció működéséhez kapcsolódik. Azt jelzi amikor egy memória lap programozásával végzett a vezérlő. Sajnos ezt a funkciót érdeklődés hiányában még nem sikerült feldolgoznom. Eléggé összetett és bonyolult funkcióról van szó. Mivel nem várható a közeljövőben ezzel a témával érdemes foglalkoznom, tekintve, hogy van már jó bootloader az Arduino alaplapokra, nem valószínű, hogy ezt a részt részletes leírással fogom ellátni.

Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!