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?
* 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.
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...
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"); |
Wow, da hab ich aber verdammt lang' für's Tippen gebraucht :-\
Das mit funcptr = NULL; geht nicht, das Programm hängt sich auf und macht ein REset :-(
Es geht natürlich mit: funcptr = nop; wobei void nop(void) { asm volatile("NOP"); } :->
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'.
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?
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.
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!
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
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.
...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 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.