AVR-GCC - jak zmieniać wektory przerwań?

Feb 07, 2007 17 Replies

W moim programie chciałbym zmieniać wektory przerwań w zalezności od kontekstu, w którym jest program. W tej chwili robię to na przykład tak:



uint8_t kontekst = 0;



ISR(TIMER0_OVF_vect){ switch (kontekst){ case 0: (...) break; case 1: (...) break; } }



Jednak, czy można to zrobić w taki sposób, żeby zdefiniować kilka funkcji przerwań i podmieniać ich wektory w zależności od kontekstu? W assemblerze nie ma problemu, a jak to zrobić w GCC?



Dziękuję Lukasz



A po co tak kombinujesz? Chyba sprawdzenie warunku nie jest jakąś bardzo obciażającą dla procka czynnością:)

Pytam z ciekawości, bo nie widzę większego sensu nad zagłebianiem się w ten temat. No chyba, że chodzi Ci o oszczędności miejsca:)

pozdrawiam, Kuba

Dokladnie tak samo jak w assemblerze. O ile cie dobrze zrozumialem to chesz podmieniac adres procedury obslugi przerwania? Kiepski pomysl, bo to wymaga za kazdym razem reprogramowania FLASHa, wiec to co masz w postaci switch/case jest zupelnie ok. Kompilator pewnie i tak to zoptymalizuje tak, ze wielkiego opoznienia nie bedzie, ew. zrob mala plombe w assemblerze.

No tak. Zapomniałem, że wektory przerwań są we flashu... Dzieki!

Lukasz napisał(a): Jednak, czy można to zrobić w taki sposób, żeby zdefiniować kilka funkcji

chyba mozna

void rtcAttach(void (*userFunc)(void)) { rtcIntFunc = userFunc; }

void rtcDetach(void) { rtcIntFunc = 0; }

SIGNAL(SIG_INTERRUPT4) { if ( rtcIntFunc ) rtcIntFunc(); }

rtcAttach(jeden); rtcDetach(); rtcAttach(dwa);

void jeden (void){} void dwa (void){}

Pawel K przemówił ludzkim głosem:

Te przypisania nie są atomowe i program będzie się sypał.

Tutaj kompilator nie będzie w stanie określić, które rejestry są niszczone przez wywoływaną funkcję i profilaktycznie odłoży wszystkie.

Chyba pomysł, ze switchem jest jednak lepszy.

Spinacz biurowy, Lukasz snipped-for-privacy@poczta.onet.wytnijpl>!

Jak sobie to wyobrażasz?

W assemblerze AVR? Niewykonalne. Przecież wektory leżą w przestrzeni programu.

Jak tak bardzo boli cię switch, to zrób tablicę wskaźników do funkcji indeksowaną kontekstem i wywołuj funkcję indeksując tą tablicę.

Użytkownik Adam Wysocki napisał:

W assemblerze... czemu nie ? Może nie wprost, ale pośrednio. W miejscu wektora zamiast tradycyjnego rozkazu RJMP wstawiamy IJMP. Tylko musimy dbać coby niepotrzebnie nie tykać rejesrtów R30:R31. A przy ustawianiu nowego wektora posługiwać się rozkazem movw (lub blokować przerwanie na czas modyfikacji). Wadą jest to, że mozemy tak sobie obskoczyć tylko jeden wektor przerwania no i wspomniana dość bolesna nietykalność pary rejestrów Z. Inna sprawa to zasadność takiego rozwiązania. W wielu przypadkach konstrukcja typu switch(cośtam) powinna być wystarczająca, ale ma pewną wadę. Czas reakcji procedury obsługi przerwania będzie zależał od jej położenia w łańcuszku case co w pewnych przypadkach może być nie do przyjęcia. Wektoryzując przerwanie przez IJMP mamy pewność, że od chwili przyjecia przerwania do wejścia w procedurę obsługi mija zawsze ten sam czas.

Pozdrawiam Grzegorz

Spinacz biurowy, Grzegorz Kurczyk snipped-for-privacy@control.slupsk.pl>!

Ano właśnie...

IMO za duży koszt.

Coś typu (oczywiście printf itd. zamienić, przykład tylko do pokazania zasady, ale kompiluje się i działa na nie-avrowym gcc).

#include <stdio.h>

typedef void (*fn_t) (void);

static void f1(void) { printf("1\n"); } static void f2(void) { printf("2\n"); } static void f3(void) { printf("3\n"); }

static fn_t tab[3] = {f1, f2, f3};

static void dispatch(int n) { tab[n](); }

int main(void) { dispatch(0); dispatch(1); dispatch(2);

return 0; }

Spinacz biurowy, Grzegorz Kurczyk snipped-for-privacy@control.slupsk.pl>!

Ano właśnie...

IMO za duży koszt.

Coś typu (oczywiście printf itd. zamienić, przykład tylko do pokazania zasady, ale kompiluje się i działa na nie-avrowym gcc).

#include <stdio.h>

typedef void (*fn_t) (void);

static void f1(void) { printf("1\n"); } static void f2(void) { printf("2\n"); } static void f3(void) { printf("3\n"); }

static fn_t tab[3] = {f1, f2, f3};

static void dispatch(size_t n) { tab[n](); }

int main(void) { dispatch(0); dispatch(1); dispatch(2);

return 0; }

Zbych napisał(a):

ale to nie wyssane z palca ... to procedury z ktorych kozystam... przypatrz sie dobrze ... zmienia sie tylko wskaznik do wywolywanej funkcji. Poza tym nie jest tak ze wszystkie rejestry ida na stos przy przerwaniu???

Problem polega na tym, ze zmieniasz 16 bitowy wskaznik. Jesli kompilator uzyje movw, lub zablokujesz przerwania na czas zmiany to jest ok, jesli nie to przerwanie ktore nadejdzie w trakcie wymiany polowki wskaznika spowoduje wywolanie procedury pod nieprawidlowym adresem i program pojdzie w maliny. Szansa na to jest bardzo mala, bo przerwanie musi nastapic dokladnie pomiedzy instrukcjami przesylajacymi 8-bitowe czesci wskaznika, stad mogles tego nie zaobserwowac, ale w koncu ten efekt nastapi. Zgodnie z prawami Murphiego w najmniej pozadanym momencie:) Druga rzecz to efektywnosc. To co napisales spowoduje wygenerowanie kodu polegajacego nie na zmianie wektora przerwan. Zawsze nastapi wywolanie: SIGNAL(SIG_INTERRUPT4) { if ( rtcIntFunc ) rtcIntFunc();

natomiast dopiero w procedurze obslugi przerwania nastapi skok do procedury ktorej adres zawiera wskaznik. IMHO niczym sie to nie rozni od jawnej instrukcji switch, mysle, ze nawet generowany kod assemblerowy moze byc podobny. Bedzie nawet gorsze, bo jak pisal Zbych kompilator nie bedzie wstanie zoptymalizowac odkladanych na stos rejestrow.

T.M.F. napisał(a):

takie samo jest ryzyko na bledne odczytanie lub zapisanie

16bit rejestru licznika TIMEROW. Ten problem jest opisany w faq.

Pawel K napisał(a):

[...]

Właśnie nie. 16-bitowe liczniki timerów w AVRach są traktowane specjalnie i dostęp do nich jest synchronizowany pod warunkiem, że najpierw odbywa się do górnej połówki a potem do dolnej przy zapisie a odwrotnie przy odczycie. Cytuję z dokumentacji procesora ATmega 128 (długie ale tłumaczy wszystkie zawiłości):

"The 16-bit register must be byte accessed using two read or write operations. Each 16-bit timer has a single 8-bit register for temporary storing of the high byte of the 16-bit access. The same Temporary register is shared between all 16-bit registers within each 16-bit timer. Accessing the low byte triggers the 16-bit read or write operation. When the low byte of a 16-bit register is written by the CPU, the high byte stored in the Temporary Register, and the low byte written are both copied into the 16-bit register in the same clock cycle. When the low byte of a 16-bit register is read by the CPU, the high byte of the 16-bit register is copied into the Temporary Register in the same clock cycle as the low byte is read. [...] To do a 16-bit write, the high byte must be written before the low byte. For a 16-bit read, the low byte must be read before the high byte."

Takiego wsparcia sprzętowego nie zapewnią własne 16-bitowe zmienne w RAMie ogólnego przeznaczenia więc trzeba wyłączać przerwania na czas ich zmieniania.

Lukasz snipped-for-privacy@poczta.onet.WYTNIJpl> napisał(a):

Ja proponuję takie cuś ;-)

#include <avr/io.h>

#include <avr/interrupt.h>

volatile uint8_t kontekst = 0;

typedef void (*vFuncPtr)(void);

void func1(void){PORTB=PINA;} void func2(void){PORTA=PINB;} void func3(void){PORTC=PORTA;} void func4(void){PORTB=PINC;} void func5(void){PORTA=PINA;} void func6(void){PORTC=PORTB;} //------------------------------------------------------

vFuncPtr faddr[] ={func1,func2,func3,func4,func5,func6};

ISR(TIMER0_OVF_vect){faddr[kontekst]();}

int main(void) { return(0); }

Zdecydowanie lepsze(?) od "switch ... case" Funkcje zmyślone , by uniknąć zoptymalizowania ;)

Piotrek

Adam Dybkowski przemówił ludzkim głosem:

A co się stanie jeśli między te dwa zapisy wejdzie przerwanie, które także będzie chciało coś zapisać do innego 16-bitowego rejestru? :-)

Świetny pomysł. I bardzo bliski tego, co chciałem uzyskać. Lepiej juz się chyba nie da.

Lukasz snipped-for-privacy@poczta.onet.WYTNIJpl> napisał(a):

Lepiej , kto wie ? Np. tablicę z adresami funkcji , umieścić w pamięci programu i w ten sposób zaoszczędzić RAM-u , jeśli ktoś miałby tych funkcji ponad miarę ;-)

Piotrek

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required