Prosím o radu:
u atmega 16 používám přerušení na INT2 na spádovou hranu, přerušení funguje, ale program zůstane v rutině pro obsluhu tohoto přerušení a nevrátí se zpět. U přerušení od USARTu tento problém nevzniká. Ale třeba u přerušení od TIMER0 tento problém byl závislý na čase potřebném pro obsluhu přerušení.
Programuji v C přes Codevision.
administrator: přejmenováno z "Potíže s návratem z přerušení"
Když předhodíš zdroják, bude to fakt jednodužší.
Takhle mohu nadhodit jen možnosti.
1.) Překontroluj si příkazy PUSH a POP.
Pokud chybně pracuješ s haldou, tak bych se vážně nedivil, že neví kam se vrátit.
2.) Překontroluj zda stihne celá rutina přerušení proběhnout do jejího
dalšího volání.
Pokud nestihne, tak se přerušení volá okamžitě poté,
co vyklouzne z toho předešlého.
Vytvoří se tak nekonečná smyčka.
Pokud tomu tak je, tak se prostě a jednoduše nedostává času
na program v main().
3.) A nejčastější chybou bývají nekonečné smyčky algoritmické.
Ať už uvnitř přerušení, nebo špatnou obsluhou registrů.
Možná pomůže popis zařízení které dělám: se zdrojákem to nebude v této chvíli tak jednoduché.
atmega16 obsluhuje lcd display, GSM modul,rtc a klávesnici pro kterou jsem pro nedostatek portů realizoval za použití atmega8 a výstup z ní je přiveden na mega16 - na INT2 hodiny a na jiný vstup data. prvním cyklem hodin aktivuju přerušení . Hlavní program zatím jenom čte po I2C z rtc a čas zobrazuje na display. Účelem přerušení na INT2 je pouze přijmout znak a zapsat ho do registru, který pak přečtu v hlavním programu. To že přerušení i přenos mezi IC funguje poznám , když do rutiny přerušení dám i funkci ,která zobrazí přijatý znak na display. Jenže pak si můžu už jen zobrazovat znaky zmáčklých kláves, ale hlavní program( zobrazit aktuální čas) neběží.
Kompiler po zadání:
interrupt [EXT_INT2] void ext_int2_isr(void)
{
}
vygeneruje příslušné příkazy pro uložení a obnovení registrů
a příkazu “reti”
Mezi závorky jenom napíšu kód pro obsluhu přerušení, jako první příkaz “cli” aby mi další cyklus hodin na INT2 neinicioval další přerušení.
Jako poslední pak “sei”
Jak už jsem psal přerušení od USARTu (komunikace s gsm) dělá prakticky to samé (v přerušení uloží jediný znak do bufferu) a problémy to nedělá.
Je mi líto, ale bez kódu nejsem schopen přesně určit závadu.
Přesto jsem v tvém popisu našel jistou … závadu.
-při vyvolání přerušení se příkazy cli a sei “vyvolávají” automaticky, takže je není nutno psát.
To by nebylo na závadu, ale trochu to zastiňuje další problém.
-I když je globální příznak přerušeni i vypnut, tak to neznamená,
že se INT2 nemůže aktivovat.
Pak jen trpělivě čeká, až se i opětovně zapne a vyvolá tak nové přerušení.
Pokud tomu chceš zabránit, musíš místo cli a sei de-aktivovat přímo INT2.
To asi nebude to pravé ořechové. Vstupem do přerušní se nastaví automaticky I=0, takže žádné další přerušení nemůže být vykonáno. Instrukce manipulující s GICR jsou proto zbytečné. Po vykonání RETI se nastaví I=1, čímž se povolí další přerušení.
Instrukce SEI uvnitř přerušení se používá při vnořování přerušení. Podmínkou je, že v přerušní smí být vykonána sei až tehdy, když je příslušný příznak, jež zavolal toto přerušení, už vynulován. V opačném případě se volá přerušení v přerušení do nekonečna.
U INT2 se příznak přerušení ruší vykonáním přerušení nebo zápisem:
GIFR|=1<<INTF2
viz. datasheet
Z toho moc neplyne, zda se INTF2 nuluje při skoku na interrupt vector nebo až instrukcí reti. U každého přerušení je to jinak. U UARTu se příznak nuluje čtením z UDR. To aby to nebylo všechno tak jednoduché.
Zkusil jsem kde-co ale jediná pomoc je během přerušení žádné další neiniciovat . Zrušil jsem tedy hodiny a na INT2 posílám jen jednu aktivační hranu jednotlivé bity pak zchytávám po pevně daném čase.
Problém zmizel, ale řešení se mi moc nelíbí. U obvodů, které zrovna nehýří množstvím I/O pinů ( např. attiny25 aj.) by použitím vstupu výhradně k aktivaci přerušení mohl vzniknout problém s nedostatkem.
Navíc je potřeba se spolehnout na to, že se hodiny obou IC moc nerozejdou.I k dyž při délce 100us a přenosu pouze 1 byte by problém být neměl (zatím nenastal).
Jeli tomu tak, pak je vysvětlení celkem jednoduché. Skokem do přerušovací rutiny se INTF2 vynuluje, ale uvnitř se čeká na nějaké hrany clk ve smyčkách while(clk==0); Není náhodou clk napojen na INT2? Pak by v těchto smyčkách došlo k opětnému nahozeni INTF2 a tedy k opětnému volání přerušení bezpostředně po jeho návratu. (Po RETI je automaticky I=1 a jeli současně INTF2=1, okamžitě se provádí příslušné přerušení).
ad 1
nepoužívám TWI (I2C) samotného mikrokontroleru a proto bych nemohl využít vektoru přerušení od TWI
používám I2C z knihovny funkcí compilleru - výhoda je v tom že sda a scl můžu mít na portu a pinech, které si určím, což značně zjednodušuje návrh DPS - na portuC, kde jsou vývody TWI(I2C) mám třeba LCD
ad 2
“key” je pojmenování vstupu (#define key PIND.5)
je , ale stejný problém by byl i kdybych měl na INT2 data - vždy by přišla nějaká další aktivační hrana (kromě bytu FFh)