Forum: Compiler & IDEs Funktionspointer ins leere laufen lassen


von Mister mit Kanister (Gast)


Lesenswert?

Hallo liebe Mitcoder,

ich hab mal ein Problem etwas (für mich) ungewöhnlicher Natur:


Ich habe ein Funktionspointer,

void (*funcptr)(void);

der mir auf ein externes Komparator Ereignis reagiert:

ISR(ANA_COMP_vect)
{
funcptr();
}


jetzt will ich in der Main Schleife durch dies Ereignis verschiedene 
Aktionen auslösen, sprich verschiedene Funktionen aufrufen. Das geht 
wunderbar mit:

if (blablabla)
funcptr = funktion1();

if (blablabla2)
funcptr = funktion2();

usw.

wenn ich aber einfach nicht auf das Komparator Ereignis reagieren will 
müsste ich den Funktionspointer deaktivieren, klar könnte ich ihn auf 
eine leere Funktion zeigen lassen, aber gibt es nicht eine elegantere 
Möglichkeit dies zu tun, wie z.B. den Funktionspointer temporär zu 
deaktivieren?


von Oliver (Gast)


Lesenswert?

Man könnte ja einfach den Interrupt sperren.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

* Interrupt sperren

* Funktionspointer auf 0 setzen und im Interrupt
  abfragen

  if( funcptr != 0 )
    (*funcptr)();

* Eine dummy Funktion, die einfahc nur returned.

Ob du 2 oder 3 wählst, hängt auch davon ab, wie oft es
vorkommt, dass der Interrupt 'ins Leere zeigen' soll.
Kommt das nur selten vor, dann ist die Dummy-Funktion
das mittel zur Wahl:
Es ist nämlich nicht einsehbar warum bei 90% aller
Interrupt Aufrufe eine sinnlose Abfrage auf 0 erfolgen
soll, nur um sich in 10% aller Fälle den sinnlosen
Funktionsaufruf zu einer Dummy Funktion zu sparen.

Entsprechend umgekehrt:
Wenn 90% alle Funktionsaufrufe zur Dummy Funktion gehen
ist es u.U schneller, wenn der Aufruf durch die Abfrage
unterdrückt wird.

Langer Rede kurzer Sinn: Auch die Abfrage auf 0 kostet Zeit.
Du musst abschätzen was dir lieber ist: Ständiges Abfragen
um ein paar Funktionsaufrufe einzusparen oder ständige
Funktionsaufrufe um eine Abfrage einzusparen.

Mittel der Wahl ist aber sicherlich 1) Interrupt abdrehen.

von Mister mit Kanister (Gast)


Lesenswert?

Ne das geht nicht, die ISR ruft Standardmäßig noch andere Funktionen 
auf, die aber immer mit der ISR aufgerufen werden. Das mit dem 
Funktionspointer soll mir ermöglichen, noch Zusatzfunktionen auf- oder 
nicht aufzurufen.

Hatte das nur  vergesen zu erwähnen. Schande über mich...

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

1
[..]
2
/* Funktionspointer mit entsprechender Funktion belegen */
3
if (blablabla)
4
    funcptr = funktion1();
5
6
if (blablabla2)
7
    funcptr = funktion2();
8
9
if (machnix)
10
    funcptr = NULL;
11
12
[..]
13
14
/* Funktion ausführen */
15
if (funcptr)
16
    funcptr();
17
else
18
    printf("nix zu tun...\n");

  

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Wow, da hab ich aber verdammt lang' für's Tippen gebraucht :-\

von Mister mit Kanister (Gast)


Lesenswert?

Das mit

funcptr = NULL;

geht nicht, das Programm hängt sich auf und macht ein REset :-(

von Mister mit Kanister (Gast)


Lesenswert?

Es geht natürlich mit:


funcptr = nop;


wobei

void nop(void)
{
asm volatile("NOP");
}


:->

von Karl H. (kbuchegg)


Lesenswert?

Mister mit Kanister wrote:
> Das mit
>
> funcptr = NULL;
>
> geht nicht, das Programm hängt sich auf und macht ein REset :-(

Dann hast du vergessen vor dem Aufruf der
Funktion über den Funktionspointer vorher den
Funtionspointer zu testen, ob er nicht 0 ist.
Auch ein cli() vor der Zuweisung und ein sei() nach der
Zuweisung sind kein Fehler. Ist ja eine 16 Bit Zuweisung.
Falls der Interrupt zuschlägt nachdem zwar das eine Byte
aber noch nicht das andere Byte zugewiesen wurde, kriegst
du einen lustigen Funktionspointer 'in the wood'.

von Mister mit Kanister (Gast)


Lesenswert?

Hmm ok

ich hab jetzt

if( funcptr != 0 )
    (*funcptr)();

so wie Du es vorgeschlagen hast. Damit funktionierts super.

Ich verstehe  noch nicht ganz den Unterschied zwischen
(*funcptr)(); und funcptr();

Rufen ja beide jeweils die Funktion auf, die ich mit funcptr = 
funktion1(); zugewiesen habe.

Würde es auch gehen wenn ich die Zuweisung mache
funcptr = &funktion1();

und dann der Aufruf *funcptr(); würde dann die Funktion starten?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mister mit Kanister wrote:

> Ich verstehe  noch nicht ganz den Unterschied zwischen
> (*funcptr)(); und funcptr();

Es gibt keinen.  Früher[tm] (also im K&R-C vor ANSI C89/ ISO C90)
musste man die erste Form schreiben, und manche Leute sind auch heute
noch der Meinung, man müsste auf diese Weise besonders darauf
aufmerksam machen, dass die Funktion hier ja über einen Zeiger gerufen
wird.

> Würde es auch gehen wenn ich die Zuweisung mache
> funcptr = &funktion1();

Nein.  Die Klammern würden das Ausführen von funktion1 veranlassen,
und du versuchst danach, die Adresse des Rückkehrwertes zu bilden.
Das hat keinen Sinn und dürfte in C so auch nicht zulässig sein (hab's
aber weder probiert noch jetzt Lust, den Standard danach abzusuchen).

Generell funktioniert C so, dass Funktionen und Arrays als Adresse
vermittelt werden durch bloßes Nennen ihres Namens, während sie
dereferenziert (und bei einer Funktion damit auch ausgeführt) werden,
wenn man danach die eckigen oder runden Klammern schreibt.

von Mister mit Kanister (Gast)


Lesenswert?

Ich hab das mal im Compiler ausprobiert:

es gibt beim Aufrufen wirklich kein Unterschied zwischen:
(*funcptr)() und funcptr()

und es gibt bei der Zuweisung kein Unterschied zwischen:

funcptr = &funktion1; und
funcptr = funktion1;

Ich denke dass der Adressoperator schon vom Compiler weggelassen wird. 
Adresse von Adresse abfragen macht ja kein Sinn.

Danke an alle Beteiligten!

von Stefan K. (_sk_)


Lesenswert?

Hallo Mister,

vergiss nicht, während der Zuweisung an den funcptr die Interrupts zu 
sperren:
1
if (blablabla)
2
{
3
  cli();
4
  funcptr = funktion1();
5
  sei();
6
}
Das Speichern nach funcptr ist nicht atomar. Ohne das Sperren der IR 
kann es Dir passieren, dass der IR genau zwischen dem Speichern des 1. 
und 2. Byte von funcptr auftritt. Da in funcptr in diesem Moment keine 
gültige Adresse gespeichert ist, bewirkt das einen Totalabsturz. Kommt 
selten vor, ist dafür aber umso schwieriger zu finden ...

Gruß, Stefan

von Gast (Gast)


Lesenswert?

Hallo,

wo finde ich zu solchen Themen weitere Infos (Wann stört ein Interrupt, 
ich hätte gedacht, eine Interruptroutine hinterlässt die Umgebung, so, 
wie sie sie vorgefunden hat).

In normalen C-Büchern eher nicht, oder? Spezielle Embedded C-Bücher?

Gruß,
Torsten.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

...dann solltest Du der Vollständigkeit halber aber kein 'sei()' 
verwenden, da das ebenso ungewollte Nebeneffekte erzielen kann.

'sei()' setzt das globale Interruptflag, welches aber bereits vorher von 
einem anderen Teil des Programmes abgeschaltet worden sein könnte.

Nutze dazu das SREG:
1
[...]
2
3
if (foo)
4
{
5
    uint8_t st_reg = SREG
6
    cli();
7
8
    funcptr = funktion1;
9
10
    SREG = st_reg;
11
}

  

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Die SREG-Variante braucht man nur, wenn nicht aus dem Kontext klar
ist, ob die Interrupts derzeit aktiv sind oder nicht.  Insbesondere
also in Code, der sowohl von einer ISR als auch aus dem primären
Kontext gerufen werden kann.

Wenn es klar ist, dass sie aktiv sind (z. B. in main()), dann erzeugt
es nur mehr Code ohne weiteren Nutzen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.