Přerušení INT2-program se nevrátí zpět z obsluhy přerurušení

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.

:arrow_right: administrator: přejmenováno z "Potíže s návratem z přerušení"

No a zdrojak neprihodis…lebo ked povod prerusenia sa ukonci .nevidim dovod preco by nemo odist z prerusenia…

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ště jsem dal přeložit do asm:

interrupt [EXT_INT2] void ext_int2_isr(void)
{

}

@00000000: .text
+00000000: 940C002B JMP 0x0000002B Jump
+00000002: 940C0000 JMP 0x00000000 Jump
+00000004: 940C0000 JMP 0x00000000 Jump
+00000006: 940C0000 JMP 0x00000000 Jump
+00000008: 940C0000 JMP 0x00000000 Jump
+0000000A: 940C0000 JMP 0x00000000 Jump
+0000000C: 940C0000 JMP 0x00000000 Jump
+0000000E: 940C0000 JMP 0x00000000 Jump
+00000010: 940C0000 JMP 0x00000000 Jump
+00000012: 940C0000 JMP 0x00000000 Jump
+00000014: 940C0000 JMP 0x00000000 Jump
+00000016: 940C0000 JMP 0x00000000 Jump
+00000018: 940C0000 JMP 0x00000000 Jump
+0000001A: 940C0000 JMP 0x00000000 Jump
+0000001C: 940C0000 JMP 0x00000000 Jump
+0000001E: 940C0000 JMP 0x00000000 Jump
+00000020: 940C0000 JMP 0x00000000 Jump
+00000022: 940C0000 JMP 0x00000000 Jump
+00000024: 940C005B JMP 0x0000005B Jump
+00000026: 940C0000 JMP 0x00000000 Jump
+00000028: 940C0000 JMP 0x00000000 Jump
+0000002A: 0000 NOP No operation
+0000002B: 94F8 CLI Global Interrupt Disable
+0000002C: 27EE CLR R30 Clear Register
+0000002D: BBEC OUT 0x1C,R30 Out to I/O location
+0000002E: E0F1 LDI R31,0x01 Load immediate
+0000002F: BFFB OUT 0x3B,R31 Out to I/O location
+00000030: BFEB OUT 0x3B,R30 Out to I/O location
+00000031: BFE5 OUT 0x35,R30 Out to I/O location
+00000032: E1F8 LDI R31,0x18 Load immediate
+00000033: BDF1 OUT 0x21,R31 Out to I/O location
+00000034: BDE1 OUT 0x21,R30 Out to I/O location
+00000035: E08D LDI R24,0x0D Load immediate
+00000036: E0A2 LDI R26,0x02 Load immediate
+00000037: 27BB CLR R27 Clear Register
+00000038: 93ED ST X+,R30 Store indirect and postincrement
+00000039: 958A DEC R24 Decrement
+0000003A: F7E9 BRNE PC-0x02 Branch if not equal
+0000003B: E080 LDI R24,0x00 Load immediate
+0000003C: E094 LDI R25,0x04 Load immediate
+0000003D: E6A0 LDI R26,0x60 Load immediate
+0000003E: 93ED ST X+,R30 Store indirect and postincrement
+0000003F: 9701 SBIW R24,0x01 Subtract immediate from word
+00000040: F7E9 BRNE PC-0x02 Branch if not equal
+00000041: E5E4 LDI R30,0x54 Load immediate
+00000042: E0F0 LDI R31,0x00 Load immediate
+00000043: 9185 LPM R24,Z+ Load program memory and postincrement
+00000044: 9195 LPM R25,Z+ Load program memory and postincrement
+00000045: 9700 SBIW R24,0x00 Subtract immediate from word
+00000046: F061 BREQ PC+0x0D Branch if equal
+00000047: 91A5 LPM R26,Z+ Load program memory and postincrement
+00000048: 91B5 LPM R27,Z+ Load program memory and postincrement
+00000049: 9005 LPM R0,Z+ Load program memory and postincrement
+0000004A: 9015 LPM R1,Z+ Load program memory and postincrement
+0000004B: 01BF MOVW R22,R30 Copy register pair
+0000004C: 01F0 MOVW R30,R0 Copy register pair
+0000004D: 9005 LPM R0,Z+ Load program memory and postincrement
+0000004E: 920D ST X+,R0 Store indirect and postincrement
+0000004F: 9701 SBIW R24,0x01 Subtract immediate from word
+00000050: F7E1 BRNE PC-0x03 Branch if not equal
+00000051: 01FB MOVW R30,R22 Copy register pair
+00000052: CFF0 RJMP PC-0x000F Relative jump
+00000053: E5EF LDI R30,0x5F Load immediate
+00000054: BFED OUT 0x3D,R30 Out to I/O location
+00000055: E0E4 LDI R30,0x04 Load immediate
+00000056: BFEE OUT 0x3E,R30 Out to I/O location
+00000057: E6C0 LDI R28,0x60 Load immediate
+00000058: E0D1 LDI R29,0x01 Load immediate
+00000059: 940C005C JMP 0x0000005C Jump
@0000005B: ext_int2_isr
---- int2__.c -------------------------------------------------------------------------------------
31: }
+0000005B: 9518 RETI Interrupt return

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.

takže jestli jsem to pochopil,tak místo “cli” dám GICR&=0x20;( bit 5 GICRu povoluje INT2)a místo “sei” GICR|= 0x20; ?

A místo těch nic neříkajících čísel bys mohl použít následující :wink::
GICR &= ~(1<<INT2);
GICR |= (1<<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é.

rutina vypadá takto:

interrupt [EXT_INT2] void ext_int2_isr(void)
{
znak=prijmi_znak();
prisel_znak=1; // nastavi priznak
}

byte prijmi_znak()
{
byte i,zn;
while(clk==0);
while(clk==1);
while(clk==0); //ceka jeden cyklus hodin
delay_us(20);

for (i=0;i<8;i++)
{
if(key==1)zn|=0x80; //key= vstup dat
zn>>=1;
while(clk==1);
while(clk==0); //ceka na dalsi nabeznou hranu hodin
};

return zn;
}

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).

proc nepouzijes I2C i pro komunikaci s tou megou8?

Pro přístup k portům používáš struktury? Je ze mi neni moc jasny to “key”…

 return zn; ???

Isr nemůže vracet hodnotu.

Podle mé zkušenosti se v externím přerušení nuluje flag na začátku.
Proto jako poslední příkaz v obsluze externího přerušení dávám vymazání INTF.

Pardon, nějak rychle jsem četl a nevšiml si, že ten return není v isr.

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)

Pak musí být PIND jako struktura, protože jinak nemá takový zápis v C smysl… To bude nějaká fičurka překladače…