AVR-GCC - jak zmieniać wektory przerwań?

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

Reply to
Lukasz
Loading thread data ...

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

Reply to
invalid unparseable

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.

Reply to
T.M.F.

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

Reply to
Lukasz

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){}

Reply to
Pawel K

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.

Reply to
Zbych

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

Reply to
Adam Wysocki

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

Reply to
Grzegorz Kurczyk

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; }

Reply to
Adam Wysocki

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; }

Reply to
Adam Wysocki

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???

Reply to
Pawel K

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.

Reply to
T.M.F.

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

takie samo jest ryzyko na bledne odczytanie lub zapisanie

16bit rejestru licznika TIMEROW. Ten problem jest opisany w faq.
Reply to
Pawel K

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.

Reply to
Adam Dybkowski

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

Reply to
Piotrek Sz.

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? :-)

Reply to
Zbych

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

Reply to
Lukasz

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

Reply to
Piotrek Sz.

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.