Reklama

Externí přerušení nejen s ATtiny24 – 2. Díl Praktické ukázky s přerušením od INT0

Úvod

Následující článek navazuje na teoretický základ o externím přerušením pro ATtiny24 uvedený v předchozím díle: Externí přerušení nejen s Attiny24 – 1. Díl Teoretický popis, a to praktickými ukázkami použitím přerušení INT0.

Popis

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 s tím nemáte zkušenost, viz obr. 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. 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 vzestupné 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.

 
Obr. 1. - Zákmity tlačítka

Obr. 1. – Zákmity tlačítka

 

A teď k programu. Komentáře jsou samovysvětlující, ale pro jistotu. Nejprve konfiguruji PB2 jako vstup s „pull-up“ 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, protože tak krátké bliknutí byste 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 ladění . Vy pravděpodobně program “debugovat” nebudete, takže můžete nechat smyčku prázdnou.

 

001: // Přerušení na sestupnou hranu na INT0
002: #include <avr/io.h>
003: #include <avr/interrupt.h>
004:
005: ISR(EXT_INT0_vect){
006: // vytvoříme krátký pulz
007: PORTB |= (1<<PORTB0); // nastavujeme na PB0 log.1
008: PORTB &= ~(1<<PORTB0); // PB0 do log. 0, zakomentujte pokud chcete jednorázově rozsvítit třeba LED
009: }
010:
011: int main(void){
012: DDRB |= (1<<DDB0); // nastavujeme PB0 jako výstup
013: DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup
014: PORTB |= (1<<PORTB2); // na PB2 zapínáme interní pull-up rezistor
015: MCUCR |= (1<<ISC01); // nastavujeme přerušení na sestupnou hranu
016: GIMSK |= (1<<INT0); // povolujeme přerušení INT0
017: sei(); // globální povolení přerušení
018:
019: while(1){
020: asm("nop"); // nic nedělej – pro snazší ladění
021: }
022:
023: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

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 1 MHz je vidět rychlost odezvy 19.6 us. Je potřeba si uvědomit, že první operace v rutině přerušení se ale neskládá z jediné instrukce. Pokud mě paměť na assembler neklame, bude potřeba instrukce čtení, pak “orování” (logický součet) a následně zápis. Tedy nejméně tři instrukce. Při taktu 1 MHz jsou to nejméně 3 us. Měli byste tedy brát změřenou hodnotu s rezervou, jen jako řádový odhad.

Obr. 2. - Reakce na stisk pomocí INT0 s 1MHz taktem (v aktivním režimu)

Obr. 2. – Reakce na stisk pomocí INT0 s 1MHz taktem (v aktivním režimu)

 

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 bity nulové, takže není nutné je explicitně nulovat. Ale zdrojový kód je takhle čitelnější. 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. 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í.

 

001: // přerušení INT0 na nízkou úroveň
002: #include <avr/io.h>
003: #include <avr/interrupt.h>
004:
005: ISR(EXT_INT0_vect){
006: // vytvoříme krátký pulz
007: PORTB |= (1<<PORTB0); // nastavujeme na PB0 log. 1
008: PORTB &= ~(1<<PORTB0); // PB0 do log. 0
009: }
010:
011: int main(void){
012: DDRB |= (1<<DDB0); // nastavujeme PB0 jako výstup
013: DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup
014: PORTB |= (1<<PORTB2); // na PB2 zapínáme interní pull-up rezistor
015: // nastavujeme přerušení na Low Level (nulujeme ICSC01 a ICSC00)
016:
017: MCUCR &= ~((1<<ISC01) | (1<<ISC00));
018: GIMSK |= (1<<INT0); // povolujeme přerušení INT0
019: sei(); // globální povolení přerušení
020:
021: while(1){
022: asm("nop"); // nic nedělej – pro snazší ladění
023: }
024:
025: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

 
Obr. 3. - Reakce na stisk pomocí INT0 s detekcí nízké úrovně

Obr. 3. – Reakce na stisk pomocí INT0 s detekcí nízké úrovně

 

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 de facto 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 5 V odebírá přibližně 0.6 uA a při 1.8 V jen 0.28 uA. Nezapomeňte u toho ale odpojit programátor. ;-)

 

001: // přerušení na Low Level s probuzením z režimu spánku
002: #include <avr/io.h>
003: #include <avr/interrupt.h>
004: #include <avr/sleep.h>
005:
006: ISR(EXT_INT0_vect){
007: // vytvoříme krátký pulz
008: PORTB |= (1<<PORTB0);// nastavujeme na PB0 log. 1
009: PORTB &= ~(1<<PORTB0);// PB0 do log. 0
010: }
011:
012: int main(void){
013: DDRB |= (1<<DDB0); // nastavujeme PB0 jako výstup
014: DDRB &= ~(1<<DDB2); // nastavujeme PB2 jako vstup
015: PORTB |= (1<<PORTB2);// na PB2 zapínáme interní pull-up rezistor
016: // nastavujeme přerušení na Low Level (nulujeme ICSC01 a ICSC00)
017:
018: MCUCR &= ~((1<<ISC01) | (1<<ISC00));
019: GIMSK |= (1<<INT0); // povolujeme přerušení INT0
020: sei(); // globální povolení přerušení
021: // vybíráme režim spánku
022: set_sleep_mode(SLEEP_MODE_PWR_DOWN);
023:
024: while(1){
025: asm("nop");// nic nedělej – pro snazší ladění
026: sleep_mode();// dobrou noc…
027: }
028: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

 
Obr. 4. - Reakce na INT0 z režimu Power Down

Obr. 4. – Reakce na INT0 z režimu Power Down

 

Jak vidíte na průběhu probuzení čipu z režimu spánku, přidalo v tomto případě asi 10 us k původním necelým 20 us. Orientační hodnoty jsou v následující tabulce. Většinou vás ale nebudou vůbec zajímat.

Tab. 1: Srovnání doby příchodu do rutiny přerušení

Napětí Takt Režim spánku Přibližný čas probrání
5 V 8 MHZ nespí 2.2 us
5 V 8 MHZ Idle 2.8 us
5 V 8 MHZ Power Down 5.9 us
5 V 1 MHZ Nespí 19.6 us
5 V 1 MHZ Idle 23.3 us
5 V 1 MHZ PowerDown 31.6 us

 

V příštím díle si ukážeme co se stane, když dojde najednou k přerušení od INT0 a také od PCINT.

 
Autor: Michal Dudka

 

[1] ATMEL. 8-bit Microcontroller with 2K/4K/8K Bytes In-System Programmable Flash ATtiny24A ATtiny44A ATtiny84A.[online] citováno 24. července 2017. Dostupné na www: http://www.atmel.com/images/doc8183.pdf
Následující a předchozí příspěvek v kategorii:

 
Následující: Externí přerušení nejen s Attiny24 – 3. Díl Praktické ukázky se souběhem přerušení INT0 a PCINT
Předchozí: Externí přerušení nejen s Attiny24 – 1. Díl Teoretický popis

 
Tajned facebook
 

Za případné chyby v textu, ve zdrojovém kódě, nebo ve schématickém zapojení se omlouváme.
AUTOŘI NEBEROU ŽÁDNOU ODPOVĚDNOST ZA PŘÍPADNÉ ÚJMY NA ZDRAVÍ ČI MAJETKU.