mikrocontroller.net

Forum: Compiler & IDEs Funktionspointer ins leere laufen lassen


Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?


Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Man könnte ja einfach den Interrupt sperren.

Oliver

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[..]
/* Funktionspointer mit entsprechender Funktion belegen */
if (blablabla)
    funcptr = funktion1();

if (blablabla2)
    funcptr = funktion2();

if (machnix)
    funcptr = NULL;

[..]

/* Funktion ausführen */
if (funcptr)
    funcptr();
else
    printf("nix zu tun...\n");

  

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

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

Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das mit

funcptr = NULL;

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

Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es geht natürlich mit:


funcptr = nop;


wobei

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


:->

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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'.

Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mister mit Kanister (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Mister,

vergiss nicht, während der Zuweisung an den funcptr die Interrupts zu 
sperren:
if (blablabla)
{
  cli();
  funcptr = funktion1();
  sei();
}
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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
[...]

if (foo)
{
    uint8_t st_reg = SREG
    cli();

    funcptr = funktion1;

    SREG = st_reg;
}

  

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.