V článku budu předpokládat, že máte jisté minimální zkušenosti s programováním AVR v jazyce C, dále předpokládám že zvládáte ovládání I/O portů a že máte představu co je to přerušení. Zdrojové kódy budou psány pro AVR studio . Kdo programuje například v Codevision, bude mít jiné názvy rutin přerušení a typicky využije Codewizzard, který za něj externí přerušení nastaví. Bude-li chtít kód použít, bude si jej muset upravit. Ukázky budu provádět na čipu Attiny24A protože jsou levné a mám jich slušnou zásobu. Ovládání bude ale principiálně podobné jako na většině čipů řady Atmega (ty budou ale bohatěji vybaveny).
Napíšu to jen ve zkratce. V principu k tomu samému jako jakékoli jiné přerušení. Umožňuje mikrokontroléru (zkráceně čipu) reagovat na asynchronní událost. Kdyby nebylo přerušení, čip by vykonával program instrukci za instrukcí a pokud by jste chtěli nějak reagovat na vnější událost, museli by jste neustále kontrolovat zda nenastala. V případě časovače by jste se pořád museli dívat na vlajku zda nepřetekl, u AD převodníku by jste stále museli kontrolovat zda nedokončil převod, u přijímání dat sériovou linkou by jste neustále museli sledovat zda vám nedorazila nová data. Takový styl programování je nejen komplikovaný ale také nespolehlivý. Představte si, že máte provádět relativně náročný výpočet, na který sice nespěcháte, ale trvá vám třeba 5 ms. Ale zároveň chcete kontrolovat příchod dat z vnějšku (třeba z počítače po sériové lince). Sledujte modelovou situaci, zahájíte výpočet, během něj vám přijde první znak zprávy a čeká na vás, vy ale počítáte, chvíli nato přijde druhý znak zprávy ... vy stále počítáte. Přirozeně druhý příchozí znak smaže ten první, protože jste si ho nevyzvedli. Vy totiž počítáte. Aby jste se toho vyvarovali museli by jste do výpočtu vkládat kód který bude sledovat příchod dat po sériové lince. Zdrojový kód takového výpočtu by byl úplným rodištěm různých chyb, byl by neuvěřitelně nepřehledný, těžko modifikovatelný a typicky i nespolehlivý. A přesně proto je tu institut přerušení. Po vyvolání přerušení program odskočí do jiné části vašeho kódu kde vykoná vše potřebné a pak se vrátí na své místo a pokračuje kde přestal (ve vašem případě ve výpočtu).
Zdrojů přerušení, tedy i událostí, jež mohou nějaké přerušení vyvolat, má čip hodně. Jedním z nich je externí přerušení a jak už název napovídá, bude sloužit k reakci na vnější událost. Modelových situací je mnoho. Začneme nějakou rozumnou. Měříme zrychlení čidlem ADXL345. Než abychom se čidla stále dokola doptávali zda už má k dispozici nová data (tzv polling), nakonfigurujeme ho tak aby nám na jednom drátě poslalo logickou úroveň (typicky 0V) ve chvíli kdy bude mít data připravena. My si nakonfigurujeme externí přerušení a kdykoli jsou dat připravena můžeme je okamžitě z čidla vyčíst a zpracovat. Jiný příklad. Používáte teploměr LM75 k detekci přehřátí, když ho vhodně nakonfigurujete, bude také schopen jedním vývodem signalizovat přehřátí (nebo podchlazení). Váš čip se opět nebude muset ptát na teplotu a kontrolovat ji sám, pouze počká na přerušení. Podobná situace může nastat s obvody RTC (hodiny, kalendáře). RTC může pomocí jednoho výstupu čipu signalizovat třeba naprogramovaný alarm (čas vstávat do práce/školy !). Další využití najde externí přerušení v časově kritických aplikacích. V příkladu s RTC vám je celkem jedno jestli budík zazvoní o pár milisekund dříve či později. Můžete, mít ale například aplikaci která přepíná zdroj napájení. Jakmile zadetekuje, že primární zdroj přestává dodávat energii (například pokles napětí, nebo signalizace o poruše) budete potřebovat rychle přepnout na záložní zdroj. Externí přerušení je schopno reagovat už během mikrosekundy. Další užitečné použití externího přerušení najdete u "low power" aplikací. Čip můžete uspat aby jste snížili odběr (klidně na zlomky uA). A jen čas od času ho ze spánku probrat a provést nějakou akci. Čip může spát, čekat na stisk tlačítka (který vyvolá externí přerušení), probrat se a vykonat nějakou akci. Například po stisku změří hodnotu, zobrazí ji na displeji a zase usne. Případně čeká na vyvolání alarmu, pomocí externího přerušení se probudí ze spánku, aktivuje GSM modul a pošle vám SMSku. V poslední řadě bych zmínil ještě třeba obsluhu rotačního enkodéru ("potenciometr" co se může točit furt dokola a má "kroky"). Kdo nevíte co to je strčte si do googlu "rotary encoder". U některých typů enkodérů vám stačí jeden jeho kanál připojit na externí přerušení a v rutině přerušení pak jen sledovat úroveň druhého kanálu a přičítat nebo odečítat kroky.
K dispozici máme jedno plnohodnotné přerušení na pinu PB2 (INT0). Mikrokontrolér Atmega32 má tři, Atmega128A dokonce osm. Čip umí na INT0 detekovat 4 různé události. Prvně může detekovat přítomnost logické úrovně 0. Přerušení je pak voláno stále dokud není na INT0 zpět log.1. V tomto režimu (a pouze v tomto) je možné budit čip z hlubokého spánku (z režimů POWER DOWN a STAND BY, které mají největší úsporu energie). Dále je možné pomocí INT0 detekovat nástupnou hranu (přechod z log.0 do log.1), sestupnou hranu a jakoukoli změnu (tedy jakoukoli hranu). V těchto třech případech je rutina přerušení zavolána vždy jen jednorázově. Další volání rutiny přerušení proběhne až po detekování další události. Jisté trable mohou nastat pokud přijdou události v rychlém sledu za sebou. To si ale rozebereme později. Krom INT0 nám čip nabízí ještě takzvané "Pin Change Interrupt - PCINT". Pomocí něj může čip reagovat na změnu logické hodnoty na libovolné skupině vstupů. PCINT nemá takový komfort jako INT0, můžeme detekovat jen změnu na vstupu, nikoli třeba konkrétně sestupnou hranu. Chcete-li tedy reagovat pouze na sestupnou hranu pomocí PCINT, musíte si v rutině přerušení situaci ošetřit a sestupnou hranu rozpoznat, přerušení totiž bude vyvoláno i nástupnou hranou. Pozitivní na celé věci je to, že PCINT může hlídat libovolné piny procesoru. Chcete-li tedy detekovat změnu na PB1, PA2 a PA3, máte možnost. Piny pro PCINT jsou rozděleny do dvou skupin. První skupinou jsou piny 0 až 7, ve druhé skupině pak piny 8 až 11 (v případě jiných čipů může být skupin více). Každá skupina má svoji vlastní rutinu přerušení. Tady vás opět může čekat komplikace. Spustíte-li externí přerušení na PA1,PA2 a PA3 (tedy PCINT1, PCINT2 a PCINT3) zavolá se jedna jediná rutina přerušení a vy budete muset sami rozpoznat který z pinů ji vyvolal. Není to až tak obtížné, ale stojí to za trochu rozmyšlení ... které nechám na vás :) Ve stručnosti je to asi tak vše co vám systém externího přerušení na Attiny24 nabízí.
Tak jako všechny periferie mikrokontroléru i externí přerušení se řídí nastavováním a čtením bitů ve speciálních registrech. Používat budete tyto:
Začneme první ukázkou. Předvedeme si přerušení na sestupnou hranu INT0 a změříme si za jak dlouho na něj čip zareaguje. Uspořádání bude jednoduché. INT0 se nachází na pinu PB2. Nakonfigurujeme ho jako vstup a zapneme pull-up rezistor. Ten zajistí, že na vstupu bude log.1. Tlačítko pak připojíme mezi PB2 a zem. Stiskem tlačítka se PB2 spojí se zemí a na PB2 bude log.0. Uvolněním tlačítka se pin díky pull-up rezistoru zase vrátí do log.1. Při testech je ale dobré si uvědomit, že tlačítko není nijak filtrované, může tedy zakmitávat (bouncing). Záměrně nějaký bouncing ukážu pro případ že nemáte tu zkušenost (obrázek č.1). Na spuštění přerušení může stačit i velmi krátký jehlový impulz. Minimální šířka může záviset na typu sledované události a na taktu procesoru. Toto je jedno z největších úskalí při použití externího přerušení na detekci stisku tlačítka. Musíte hardwarově nebo softwarově ošetřit zákmity. Třeba na obrázku č.1 je průběh, kde by klidně mohlo dojít k volání rutiny přerušení třikrát, pokud by byla nastavena na detekci nástupné hrany. Podobné problémy může způsobovat i elektrické rušení. V takových případech je výhodné detekovat stisk tlačítka raději pollingem (tedy se například 50x za vteřinu koukat zda je tlačítko stisknuto). Pravděpodobnost, že se s dotazem trefíte právě do nějakého jehlového impulzu (vzniklého ať rušením nebo zákmitem) je mizivá. Obsluha tlačítko většinou stiskne na rozumně dlouhou dobu. Nevýhodou je, že si musíte softwarově ošetřit detekci právě jednoho stisku. Tlačítko je v této situaci voleno spíše pro snadnou dostupnost. Klidně bychom mohli přerušení vyvolávat generátorem.
A teď k programu. Komentáře jsou samovysvětlující, ale pro jistotu. Nejprve konfiguruji PB2 jako vstup s pullup rezistorem, PB0 konfiguruji jako výstup. V rutině přerušení si pak na PB0 vytvořím krátký pulz abych na osciloskopu viděl, že čip zareagoval. Klidně si můžete na PB0 připojit LED a blikat s ní. Rutinu přerušení si ale budete muset upravit, tak krátké bliknutí by jste asi okem nezaznamenali. Déle provedeme konfiguraci MCUCR v něm nastavím do log.1 bit ISC01 čímž vyberu detekci sestupné hrany. Dále pak v registru GIMSK nastavením bitu INT0 povolím přerušení od INT0. Následně ještě povolím přerušení globálně. Pak už ve while smyčce nedělám nic. asm("nop"); je příkaz pro "no operation". Mám ho zde jen kvůli snazšímu debugu. Vy pravděpodobně program debugovat nebudete, takže můžete nechat smyčku prázdnou.
// Přerušení na sestupnou hranu na INT0 #include <avr/io.h> #include <avr/interrupt.h> ISR(EXT_INT0_vect){ // vytvoříme krátký pulz PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1 PORTB &= ~(1<<PORTB0); // PB0 do log.0, zakomentujte pokud chcete jednorázově rozsvítit třeba LED } int main(void){ DDRB |= (1<<DDB0); // nastavujeme PB0 jako výtup DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup PORTB |= (1<<PORTB2); // na PB2 zapínáme interní pullup rezistor MCUCR |= (1<<ISC01); // nastavujeme přerušení na sestupnout hranu GIMSK |= (1<<INT0); // povolujeme přerušení INT0 sei(); // globální povolení přerušení while(1){ asm("nop"); // nic nedělej - pro snazší ladění } }
Na následujícím obrázku uvidíte průběh. Žlutá stopa představuje napětí na tlačítku. Modrá stopa pak napětí na PB0, tedy na výstupu z čipu. Při frekvenci 1MHz je vidět rychlost odezvy 19.6us. Je potřeba si uvědomit, že první operace v rutině přerušení se ale neskládá z jediné instrukce. Jde o čtení, oření (logický součet) a opětovný zápis. Pokud mě paměť na assembler neklame bude potřeba instrukce čtení, pak oření a následně zápis. Tedy nejméně tři instrukce. Při taktu 1Mhz jsou to nejméně 3us. Měli by jste tedy brát změřenou hodnotu s rezervou, jen jako řádový odhad.
Zkusme si přerušení nakonfigurovat jako detekci na nízkou úroveň na vstupu. Bude se nám to hodit k probouzení z režimu spánku. Zdrojový kód zůstane v podstatě stejný. Jen změníme hodnotu bitů ISC01 a ISC00 v registru MCUCR na nuly. Po startu procesoru by měly být tyto hodnoty nulové, nemuseli bychom tedy ručně obě hodnoty nulovat tak jak je ve zdrojovém kódu. Pro názornost a pro obecné použití kdekoli jinde, kde už můžete mít v MCUCR něco zapsáno jsem ale příklad ponechal takto. Opět nehledejte v kódu žádné složitosti. Ty totiž budou až v průběhu za ním. Vzpomeňme na to, že detekce nízké úrovně volá přerušení tak dlouho dokud je vstup INT0 v log.0. A to je po celou dobu co je stisknuté tlačítko. Takže na obrázku č.3 vidíte opakující se sekvenci pulzů. Jakmile přerušení skončí je ihned voláno znovu a tak stále dokola až do uvolnění tlačítka. Tady by přirozeně stálo za to přerušení zablokovat vynulováním bitu INT0 v registru GIMSK. A povolit jej až po uvolnění tlačítka nebo obecně po umlčení zdroje přerušení.
// přerušení INT0 na nízkou úroveň #include <avr/io.h> #include <avr/interrupt.h> ISR(EXT_INT0_vect){ // vytvoříme krátký pulz PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1 PORTB &= ~(1<<PORTB0); // PB0 do log.0 } int main(void){ DDRB |= (1<<DDB0); // nastavujeme PB0 jako výtup DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup PORTB |= (1<<PORTB2); // na PB2 zapínáme interní pullup rezistor MCUCR &= ~((1<<ISC01) | (1<<ISC00)); // nastavujeme přerušení na Low Level (nulujeme ICSC01 a ICSC00) GIMSK |= (1<<INT0); // povolujeme přerušení INT0 sei(); // globální povolení přerušení while(1){ asm("nop"); // nic nedělej - pro snazší ladění } }
Za ukázku ještě stojí kód předvádějící probuzení z režimu spánku. Není to cílem článku, takže ve stručnosti shrnu, že máte k dispozici defakto dva režimy spánku. Mělký spánek IDLE ze kterého vás může probudit každé přerušení, probuzení je rychlé ale režim nesníží odběr nijak závratně. Hluboký spánek (režim POWER DOWN), ze kterého vás může probudit jen externí přerušení, restart a nebo watchdog, probuzení je relativně pomalé, ale odběr klesá velmi rapidně. My budeme chtít přirozeně zamachrovat a když nám to INT0 umožňuje necháme procesor hluboce usnout. A podíváme se za jak dlouho se nám ho podaří probrat. Na funkce související se spánkem se dívejte jako na kouzelné formule, k jejich popisu se snad dostaneme v jiném článku. Kdo by nevěřil že mu čip spí, tak ať si zkusí připojit k napájení ampérmetr, pokud na to bude mít rozsah může zjistit, že čip při 5V odebírá přibližně 0.6uA a při 1.8V jen 0.28uA. Nezapomeňte u toho ale odpojit programátor. ;)
// přerušení na Low Level s probuzením z režimu spánku #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> ISR(EXT_INT0_vect){ // vytvoříme krátký pulz PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1 PORTB &= ~(1<<PORTB0); // PB0 do log.0 } int main(void){ DDRB |= (1<<DDB0); // nastavujeme PB0 jako výtup DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup PORTB |= (1<<PORTB2); // na PB2 zapínáme interní pullup rezistor MCUCR &= ~((1<<ISC01) | (1<<ISC00)); // nastavujeme přerušení na Low Level (nulujeme ICSC01 a ICSC00) GIMSK |= (1<<INT0); // povolujeme přerušení INT0 sei(); // globální povolení přerušení set_sleep_mode(SLEEP_MODE_PWR_DOWN); // vybíráme režim spánku while(1){ asm("nop"); // nic nedělej - pro snazší ladění sleep_mode(); // dobrou noc... } }
Jak vidíte na průběhu probuzení čipu z režimu spánku přidalo v tomto případě asi 10us k původním necelým 20us. Orientační hodnoty jsou v následující tabulce. Většinou vás ale nebudou vůbec zajímat.
Když jsme si vyzkoušeli INT0, měli bychom si vyzkoušet i PCINT a rovnou si u toho ukážeme co se bude dít když dojde k vyvolání dvou přerušení "zároveň". Postup nastavení přerušení jsme probrali v předešlém textu, takže by jste měli rozumět předloženému kódu. V rutinách přerušení jsme si schválně udělali malou smyčku aby její vykonávání zabralo nějaký čas. Z toho co za chvíli uvidíte pochopíte, že by jste rutinu měli psát co nejkratší. Čím kratší tím lepší. Neodpustím si dvě malé poznámky. ISR znamená Interrupt Service Routine, tedy v češtině přesně to co tu pořád omíláme - rutina obsloužení přerušení. Slovo volatile před deklarací proměnné slouží k tomu aby jsme překladači vysvětlili, že proměnnou i může měnit nějaká vnější událost. V tomto případě ji sice žádná událost měnit nebude, ale překladač ji pak nebude optimalizovat. On totiž vidí, že celý forcyklus i s proměnnou i je na nic a jinak by ji vyřadil.
// přerušení od INT0 a od PCINT7 #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> // rutina přerušení INT0 ISR(EXT_INT0_vect){ volatile unsigned int i; // vytvoříme delší pulz na PB0 PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1 for(i=0;i<50;i++){i=i;} // simulujeme práci na nějakém úkolu PORTB &= ~(1<<PORTB0); // PB0 do log.0 } // rutina přerušení PCINT (0..7) ISR(PCINT0_vect){ volatile unsigned int i; // vytvoříme delší pulz na PB1 PORTB |= (1<<PORTB1); // nastavujeme na PB1 log.1 for(i=0;i<50;i++){i=i;} // simulujeme práci na nějakém úkolu PORTB &= ~(1<<PORTB1); // PB1 do log.0 } int main(void){ DDRB |= (1<<DDB0) | (1<<DDB1); // nastavujeme PB0 a PB1 jako výtup DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup (INT0) DDRA &= ~(1<<DDA7); // nastavujeme PA7 jako vstup (PCINT7) MCUCR |= ((1<<ISC01) | (1<<ISC00)); // nastavujeme přerušení na Nástupnou hranu GIMSK |= (1<<INT0) | (1<<PCIE0); // povolujeme přerušení INT0 a PCINT skupiny 0..7 PCMSK0 |= (1<<PCINT7); // ze skupiny PCINT 0..7 vybíráme pouze PCINT7 (ostatní piny nesledujeme) sei(); // globální povolení přerušení while(1){ asm("nop"); // nic nedělej - pro snazší ladění } }
Tentokrát jsem si k testování přerušení vzal generátor. Ne že by mačkání tlačítek bylo problematické, ale za chvíli si budeme chtít vyzkoušet co se stane když přijdou dvě přerušení zároveň a stisknout dvě tlačítka v přesném časovém sledu, jedno o pár mikrosekund dřív jak druhé je dosti problém. Věnujme chvíli průběhům na obrázku č.5. Na žluté křivce je vstupní signál na INT0 (to je nastaveno na detekci nástupné hrany). Červený průběh odpovídá rutině přerušení INT0, ve které tvoříme pulz na pinu PB0 (viz zdrojový kód výše). Světle modrá křivka odpovídá signálu odeslanému na PA7 (PCINT7), tmavě modrá křivka je signál na PB1. S PB1 manipulujeme v rutině přerušení od PCINT. Vzhledem k tomu že PCINT detekuje jakoukoli změnu na vstupu, vidíme že rutina přerušení proběhne jak po nástupné hraně vstupního signálu tak po sestupné. Aby jste se toho vyvarovali, tak by pro většinu úkolů stačilo připsat do rutiny podmínku, která zkontroluje stav pinu PA7 a pokud je například nulový, tak nic nevykoná. Tím by jste si efektivně připravili detekci nástupné hrany. Problém by jste měli v případě, kdy by vstupní pulz byl natolik krátký že by stihl změnit stav před tím než zkontrolujete logickou hodnotu na vstupu. Ale s tím stejně nic neuděláte, takže vás to nemusí trápit. Předpokládám že do této chvíle bylo celé chování jasné a předvídatelné. A teď se podíváme co se stane jestliže přerušení přijde v okamžiku kdy se provádí rutina jiného přerušení. S výše uvedeným kódem dopadne výsledek jako na obrázku č.6.
Vidíte že nejprve přichází signál pro přerušení INT0. Ihned se začíná vykonávat rutina přerušení (červený signál). Během jeho vykonávání přichází přerušení pro PCINT7 (světle modrý průběh). K jeho okamžitému obsloužení ale nedochází. Čip nejprve dokončí rutinu od INT0. Až po jejím dokončení se začne provádět rutina PCINT (tmavě modrý signál), která už notnou dobu (1ms) čeká na provedení. To je tím že překladač na začátku každé rutiny zakáže globálně všechna přerušení. Teprve až po skončení celé rutiny zase přerušení povolí. Dělá to právě proto aby nebylo možné program během vykonávání rutiny přerušení znovu přerušit. Může se vám stát, že z nějakého důvodu potřebujete mít přerušení vnořená. Tedy chcete jednomu přerušení dát možnost přerušit probíhající rutinu jiného přerušení. Pak musíte buď hned na začátek rutiny vložit globální povolení přerušení, tedy sei(), a nebo nějak přesvědčit překladač aby nevkládal globální zákaz přerušení. Druhý způsob je slušnější. Ve zdrojovém kódu vidíte ukázku jak na to.
// vnořovaná přerušení od INT0 a od PCINT7 #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> // rutina přerušení INT0 ISR(EXT_INT0_vect, ISR_NOBLOCK){ volatile unsigned int i; // vytvoříme delší pulz na PB0 PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1 for(i=0;i<50;i++){i=i;} // simulujeme práci na nějakém úkolu PORTB &= ~(1<<PORTB0); // PB0 do log.0 } // rutina přerušení PCINT (0..7) ISR(PCINT0_vect, ISR_NOBLOCK){ volatile unsigned int i; // vytvoříme delší pulz na PB1 PORTB |= (1<<PORTB1); // nastavujeme na PB1 log.1 for(i=0;i<50;i++){i=i;} // simulujeme práci na nějakém úkolu PORTB &= ~(1<<PORTB1); // PB1 do log.0 } int main(void){ DDRB |= (1<<DDB0) | (1<<DDB1); // nastavujeme PB0 a PB1 jako výtup DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup (INT0) DDRA &= ~(1<<DDA7); // nastavujeme PA7 jako vstup (PCINT7) MCUCR |= ((1<<ISC01) | (1<<ISC00)); // nastavujeme přerušení na Nástupnou hranu GIMSK |= (1<<INT0) | (1<<PCIE0); // povolujeme přerušení INT0 a PCINT skupiny 0..7 PCMSK0 |= (1<<PCINT7); // ze skupiny PCINT 0..7 vybíráme pouze PCINT7 (ostatní piny nesledujeme) sei(); // globální povolení přerušení while(1){ asm("nop"); // nic nedělej - pro snazší ladění } }
Na obrázku č.7 vidíte výsledek, když v rutinách přerušení povolíme další přerušení. Žlutý průběh spustí vykonání rutiny pro INT0 (červený průběh), než ale stihne svoji práci (projít forcyklus) dokončit, přijde signál pro PCINT7 (světle modrý) a program přeruší svoji činnost (tedy vykonávání rutiny pro INT0) a začne vykonávat rutinu přerušení od PCINT7 (tmavě modrý). Po jejím dokončení se vrátí ke své rozdělané práci v rutině INT0. U vnořených přerušení hrozí riziko, že se vnoříte sami do sebe, jinak řečeno že vás přeruší to samé přerušení které zrovna vykonáváte. A pokud se to stane několikrát po sobě, tak to nemusí dopadnout dobře. Ale to jsou jen moje domněnky :) První způsob, kdy nové přerušení čeká na dokončení posledního je bezpečný. Na druhou stranu také vás může potkat situace kde musíte reagovat na několik přerušení, ale na jedno z nich s největší urgencí, pak možnost vnořování oceníte.
Existují ještě různé finty jak si v malém Atmelu počet externích přerušení navýšit. Třeba modul USI jde znásilnit tak aby se jako externí přerušení choval. Externím přerušením také můžete spouštět AD převodník. Například v situaci kdy potřebujete změřit hodnotu napětí v krátkém okamžiku po příchodu vnějšího signálu.
Jak už jsem napsal v úvodu, pro testy jsem používal čip attiny24A. A protože jsem jako student býval tvor spořivý, koupil jsem si atmely raději v SMD provedení, byly totiž levnější. Takže pro tyto pokusy jsem jej musel připájet na malou bastl desku. Tu si koupíte v číně za pár korun nebo si vyrobíte vlastní (doporučuji vyrábět ve větších sadách :D ). K programování můžete používat klasický laciný USBASP (v číně tak od 30 korun). Jiná volba může být i AVR Dragon (dosti drahý +- 1700,-) nebo ATMEL-ICE (k sehnání tak od 1000,-). Oba dva mají možnost využívat debugWire rozhraní. To vám umožní ladit program přímo v čipu, včetně všech breakpointů a podobných vychytávek. Kdo neví ať hledá termíny "in cirtuit debug" nebo "in circuit emulation". Dragon, je ale po elektrické stránce dosti "křehký". Fóra jsou plná hesel "dead avr dragon". Možnosti programování by jistě samy vydaly na delší článek, ale já bych ho nemohl sepsat pocitvě,neboť mám k dispozici jen tři nástroje a to by bylo jen takové polovičaté. Snad pokud se mi časem dostane do rukou více nástrojů. Metod je totiž opravdu hodně. Bootloaderem počínaje přes ISP, JTAG, debugWire a paralelním programováním konče. Celá měření probíhala na různých napětích. Ze začátku se pracovalo s 5V napájením, některé testy jsem provedl s pracovním napětím 1.8V (zvlášť abych viděl spotřebu v režimech spánku). Nakonec jsem pracoval s 3.3V kvůli nízkým úrovním signálů z generátoru. Všechna zapojení byla natolik triviální že jsem se neobtěžoval s kreslením schémat.
Doufám, že jste smysl i použití externího přerušení pochopili a že vám tento článek něco dal. Přirozeně bych vám doporučil provést pár vlastních pokusů aby jste získali důvěru ve vlastní kód. Většinu času při programování člověk stejně hledá chyby a je dobré umět rozpoznat na kterou část programu se můžete spolehnout a na kterou ne. A na to přijdete nejlépe zkouškou. Zdroják takhle na monitoru vypadá úplně jasně, ale když ho píšete sami, vynoří se tolik záludností, že vás to až překvapí :D