je moznost zmenit prioritu preruseni na AVR? Ide mi o to, ci si mozem vybrat urcite prerusenia, ktore uprednostnim rovnako ako pri x51, kedy mozem dovolit prerusit beh ineho prerusenia prerusenim s vyssou urovnou. Zatial som sa docital len, ze je priorita preruseni pevne dana, ale nikde som nenasiel tuto informaciu, pripadne ako to riesit
Pokial viem, tak nijak. Ak sa vykonava nejake prerusenie, ostatne su zakazane. (pre AVR-GCC pouzitie funkcii SIGNAL() ) Ak sa v preruseni povoli dalsie prerusenie, tak myslim ze v principe sa toto moze realizovat od hociakeho zdroja.
Iba ze by ste v ramci prerusenia pre povolenim toho globalneho zakazali individualne, ktore by mali mat nizsiu prioritu. Ale to zavana dost komplikovanym mechanizmom s moznostou nejakeho toho velmi zakerneho chyboveho spravania sa.
to je ale potom dost velka nevyhoda, nie? Co ak potrebujem casovat presne nejaky vystup a pritom obsluhovat ine prerusenia. Na x51 som ho jednoducho zaradil do vyssej urovne a vtedy dokazal prerusit aj ine preusenie z nizzsej urovne. Ako sa toto riesi v asm na avr?
U AVR pokial viem, sa neda “prestavovat” prirodzena prijorita jednotlivych preruseni. V podstate “prirodzena” priorita znamena, ze najvacsiu prijoritu ma vektor prerusenia s najnizsou adresou tj. RESET, potom nasleduje INT0 atd. Najmensiu prijoritu ma prerusenie s najvacsou adr. vektora…vzdy si treba pozriet datashit ku konkretnemu mcu, adr. vektorov prerusenii tam urcite budu
pri pisani sw treba pamatat na tieto skutocnosti a podla toho volit pouzitie jednotlivych prerusenii…
asi ako som to uz pisal pre Ccko nijak
iba napisat kod pod prerusenim tak, aby sa co najviac prace vykonavalo v napr. hl slucke (analya protokolu, piprava dat pre komunikaciu, filtrovanie AD, …) a v preruseni iba kratucke programiky na prenos dat. A takto efetky tejto nevyhody eliminovat.
Pri vstupu do obsluhy preruseni se I nuluje -> vsechna preruseni jsou v ten okamzik zakazana. Potrebujes-li, aby preruseni dale fungovala, je nutne si je povolit (sei - nastavit globalInteruptEnable). Vznikne-li potom behem obsluhy jine preruseni, prave probihajici je preruseno bez ohledu na jejich vzajemnou prioritu a obslouzi se to nove. Teprve pak se dokonci puvodni obsluha a navrat do hlavni smycky.
Pri povoleni preruseni uvnitr obsluhy preruseni dale plati pro nove vznikla preruseni priorita jako obvykle.
Potrbujes-li, aby bylo obsluhu mozne prerusit pouze jednim konkretnim prerusenim, musis si vsechna ostatni povypinat a pred koncem obsluhy vse vratit do puvodniho stavu.
Na rozdíl od x51, lze hranu výstupu načasovat s přesností až na 50 ns. Přerušení je pro tyto věci dost bezzubé, neboť odezva na něj trvá příliš dlouho. Používají se na to timery např. CTC nebo PWM módu. U vnořeného přerušní se odezva ještě více prodlužuje, takže to není žádná výhoda.
AVR nemá vnořování přerušení, priorita přichází v úvahu jen tehdy, vznikne-li více požadavnů na přerušení ve stejném okamžiku, jinak se zpracovává to, které přišlo dříve.
Vnořování a pseudoprioritu lze zařídit softwareově třeba takto:
Interupt:
; instrukce nulováni flagu, jež vyvolal toto preruseni
ldiw X,AdrPreruseni
pushw X
reti
AdrPreruseni:
; instrukce pro obsluhu preruseni
ret
LDIW a PUSHW jsou samozřejmě makra. Instrukce reti vyvolá v procesoru dojem, že přerušení bylo obslouženo a povolí další přerušování. Návrat ale nebude do přerušeného programu nýbrž do procedůry, ve které se přerušní dokončí. Takto vnikne přerušení s nižší prioritou. (nevím, zda toto lze v Céčku napsat).
Dobra vyhytavka…
Pokud se nejak podari ulozit na stact pointer na funkci(bohuzel globalni prom.), melo by to jit. Bohuzel tu nemam HW, na ktrem bych to mohl vyzkouset, ale snad bude stacit sim… Teda jesli me napadne, jak to zaridit s ptrem…
edit: tak to je asi nad moje sily Na stack sice tu adresu ulozim, ale v C se pri vstupu do obsluhy nejake veci na stack ukladaji a na konci vyzvedavaji a nejsem schopen zaridit, na konci obsluhy byla jako navratova adresa te jine obsluzne funkce. Vyzadovalo by to prekopirovat tu cast stacku, ktera se zapise po vstupu do preruseni, o 2 byty nize a vlozit tam tu adresu obsluzne funkce…
Mela by take byt moznost vypnout pouzivani stacku pri prechodu do isra pak by to mohlo chodit, jenze to nemuzu najit…
Lze take povolit vnoreni preruseni
ISR(XXX_vect, ISR_NOBLOCK)
{
...
}
Jde to sice zaridit makrem sei() na zacatku obsluhy, ale pomoci “ISR_NOBLOCK” je to povoleno o neco drive.
To Technik: Objasnite prosim, aka vlastnost sa ziska naviac odskokom na AdrPreruseni oproti instrikcii povolenia globalneho prerusenia pouzitej na zaciatku rutiny pod prerusenim?
Citate z datasheetu:
• Bit 6 – INTF0: External Interrupt Flag 0
When an edge or logic change on the INT0 pin triggers an interrupt request, INTF0 becomes set (one). If the I-bit in SREG and the INT0 bit in GICR are set (one), the MCU will jump to the corresponding interrupt vector. The flag is cleared when the interrupt routine is executed.
Zde není řečeno, která instrukce shodí příznak, jež vyvolává přerušení. Může to být právě RETI. Pokud umístím SEI do přerušní, ihned po ni se volá totéž přerušení a vnořuje se samo do sebe až do zhroucení. Proto je to takto napsáno. Každé přerušení se chová trochu jinak, např. u USARTu se maže příznak RXC až po přečtení UDR, takže by zde mohla být použita instr. SEI, ale až po IN Rx,UDR.
AVR simulator sice bit INTF0 nenastavuje (dalsi chybka ), ale pokud napisu obsluhu jako:
INT0_ISR:
sei
sbi PORTD, 6
cbi PORTD, 6
reti
tak pri nastaveni preruseni na hranu se vyvola 1x a sporadane se ukonci(flag se tedy podle simu nuluje pri vstupu do isr), ale pri reakci na uroven (“0”) se preruseni prerusuje a vyvolava se neustale dokola nove do preteceni stacku a kdovi, co se deje dal… cili nepouzivat
Kdezto Technikovo reseni zpusobi, ze se program zasekne do doby, nez se zmeni uroven na “H”, pote se vykona obsluha (1x) a program pokracuje… Takze to asi prece jen k necemu bude. Kdyby nebylo pouzito “sei” v preruseni, toto by se vyvolavalo a obsluhovalo periodicky, dokud by byla “L” uroven. Vicemene se to chova jako reakce na vzestupnou hranu a tim, ze pri “L” procesor stoji
Ak ukoncite prerusenie cez RETI a vstup INTx je stale aktivny - a hovorime o preruseni na uroven a nie na hranu - nevyvola sa prerusenie okamzite znovu ako pri pouziti toho sei() priamo v rutine prerusenia?
Tym ze sa napushovala adresa, kam sa ma skocit po reti (v Technikovom pripade) by sa zasobnik tiez bude zaplnovat a zaplnovat. Alebo sa mylim? Ten system by nepomohol ani v pripade UARTu, tak ako pise technik, ak by sa v prerusovacej rutine nevycital bajt z registra. Alebo sa mylim?
Alebo aky ma vlastne zmysel, pouzit ten skok z prerusenia na rutinu, ktora sa uz vracia pomocou instrukcie ret? V akych pripadoch je to teda lepsie pouzit tak ako uviedol Technik miesto jednoducheho povolenia prerusenia v prerusovacej rutine (v oboch pripadoch samozrejme po takej cinnosti, ktora ukonci podmienku vlastneho prerusenia, aby sa stack nezaplnal)?
Vyvola, ale az po dokonceni toho prave probihajiciho, tzn. ze neohrozuje zasobnik. Pri vyvolani preruseni se totiz na zasobnik ulozi navratova adresa, pri jeho ukonceni se program vrati a stak se vrati na svoji puvodni hodnotu. Teprve pak se vyvola preruseni znovu. Kdezto pri povoleni (sei) uvnitr preruseni by toto bylo preruseno, na stack by se znovu ulozila navratova adresa, zase by bylo preruseno, dalsi ulozeni adresy atd… az pretece stack a je hotovo…
Povolit preruseni v obsluze extrerniho prer. reagujiciho na uroven se zkratka NESMI.
Asi ano… naposledy jsem to nejak nedomyslel Slo by to osetrit kontrolou zda jiz adresa obsluzne rutiny na stacku neni… (o 2 byty zpatky, aktualne je na stacku navrat ze soucasneho prer.)
Nevrati, lebo uz je tam ten pushw. Stack ostane presne o tu jednu hodnotu navratovej adresy navyseny. Ak sa nemylim
Ale predsa v konstrukcii
kód:
Interupt:
; instrukce nulováni flagu, jež vyvolal toto preruseni
ldiw X,AdrPreruseni
pushw X
reti
AdrPreruseni:
; instrukce pro obsluhu preruseni
ret
stack za rovnakych podmienok pretecie tiez. pushw X musi vlozit adresu rutiny na zasobnik a tym zvysit jeho hodnotu o 1, lebo si musi pamatat, kde sa ma vratit - t.j. na miesto v prgrame, ktore bolo interuptom prerusene. Ak sa takto spracovavane prerusenie (v ramci AdrPreruseni) znova prerusi, zasobnik bude rast rovnako ako v pripade povolenia prerusenia v ramci prerusenia.
Pri pouziti konstrukcie pushw sa dokonca stack vyuziva o jednu uroven naviac ako by bolo nevyhnutne nutne. Pri pouziti sei() v ramci preusovacej rutiny (jasne ze vtedy ak sa povolenim prerusenia toto iste samo nespusti - napr od urovne INT, alebo pred vycitanim UDR - ale to treba dodrazat i pri konstrukcii s pushw), ak nepride dalsie prerusenie, hodnota stacku sa nemeni.
To sa neda jednoznacne posudit. dany interupt sa moze spracovavat o n-urovni nizsie, ak medzi tym prislo este nejake prerusenie a to si odpushovalo kde co. Tazko urcit, co z toho je hodnota a co adresa. Slo by to vyriesit zakazanim konkretneho prerusenia, v ktorom sa chystam pouzit sei(). Pred koncom jeho spracovania sa to konkretne preusenie znovu povoli. Pred povolenim sa moze este glob.prerusenie zakazat, povolit to konkretne a instrukcia reti globalne prerusenie opat povoli.
Konstrukcia, ktoru uviedol Technik je velmi uzitocna, ak adresa funkcie, ktoru potrebujem zavolat je dana indexom do pola, v ktorom su adresy tych funkcii ulozene - napr. spracovanie displaya, kazda obrazovka moze byt generovana samostatnou funkciou a poradove cislo obrazovky urcuje o ktoru funkciu sa jedna. Nie je potrebne robit vyber prislusnej funkcie cez switch/case ktrora zdrzuje beh programu, ale Technikom uvedenou konstrukciou - upravenou pre pracu s polom adries a mimo prerusenia. V C sa to realizuje polom ukazatelov na funkcie. Aka je ale konkretne vyhoda jej pouzitia pri spracovani prerusenia oproti pouziti sei() priamo v prerusovacej rutine?
Ten push jsem nebral v uvahu, protze jsi se o nem zminil v dalsim odstavci, bral jsem ho samostatne. Pokud tam bude, tak stack samozrejme pretece take.
ale on tam nemoze nebyt, lebo ina sa nema ako volat adresa AdrPreruseni.
Inak je to v pohode jedna z ciest ako ukoncit prerusenie a dat sancu inemu.