Hallo,
ich habe jetzt genug ausprobiert, weiss nicht mehr weiter.
Funktion 1:
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)
Aufruf:
I2C_GetFlagStatus(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT)
Funktion 2:
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
Aufruf:
I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);
Beide Funktionen möchte ich in eine Timeout Routine packen, da beide
Funktionen in while Schleifen verwendet werden, bis das Ereignis
eintrifft.
Die beide Rückgabetypen sind 32 Bit Integers, einfache enums. Der Typdef
ist ein uint16_t.
Wie formuliere ich nun eine Timeout Funktion, der ich beide
gleichartigen Funktionen als Parameter übergeben kann? Und wie würde
dann der Aufruf lauten?
timeout(.........)
1. Versuch:
uint8_t timed ( uint32_t (*f) (uint16_t*,uint32_t) ) {
.......
return 1;
}
Aber bei den Aufrufen kommen dann nur noch Compilerfehler, egal wie ich
es drehe.
src\eeprom.c|460|error: incompatible type for argument 1 of 'timed'|
src\eeprom.c|439|note: expected 'uint32_t (*)(uint16_t *, uint32_t) {aka
long unsigned int (*)(short unsigned int *, long unsigned int)}' but
argument is of type 'ErrorStatus {aka enum <anonymous>}'|
||=== Build finished: 1 errors, 0 warnings (0 minutes, 1 seconds) ===|
Kriege das nicht hin :-(
Gruss,
Christian
ja, zeiger sind meine Krise, mal das * vor einem Ausdruck, dann mal
dahinter usw...
Das doppelte f1 wird erstmal angemeckert. Setze ich es f2 geht es durch
aber wie sieht dann der Aufruf aus? Ganz verstehe ich den Ausdruck auch
nicht.
Es soll ja nur der passende Stackframe zusammen gebaut werden mit zwei
Zahlen als Parametern. Ok, ein Zahl wird als Zeiger übergeben.
timed (I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));
ergibt
src\eeprom.c|460|error: incompatible type for argument 1 of 'timed'|
src\eeprom.c|439|note: expected 'ErrorStatus (*)(I2C_TypeDef *,
uint32_t) {aka enum <anonymous> (*)(struct <anonymous> *, long unsigned
int)}' but argument is of type 'ErrorStatus {aka enum <anonymous>}'|
src\eeprom.c|460|error: too few arguments to function 'timed'|
src\eeprom.c|439|note: declared here|
||=== Build finished: 2 errors, 0 warnings (0 minutes, 1 seconds) ===|
So, das kompiliert er problemlos durch:
ErrorStatus (*ptr1) (I2C_TypeDef*, uint32_t);
FlagStatus (*ptr2) (I2C_TypeDef*, uint32_t);
ErrorStatus res1;
FlagStatus res2;
ptr1 = I2C_CheckEvent;
res1 = (*ptr1)(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);
ptr2 = I2C_GetFlagStatus;
res2 = (*ptr2)(I2C_KANAL, I2C_FLAG_STOPF);
und das läuft auch:
uint32_t res1,res2;
ptr1 = I2C_CheckEvent;
res1 = (uint32_t)(*ptr1)(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);
ptr2 = I2C_GetFlagStatus;
res2 = (uint32_t)(*ptr2)(I2C_KANAL, I2C_FLAG_STOPF);
Da ErrorStatus und Flagstatus aber beides nur uint32 sind, wenn man sich
in der Lib durchhangelt fehlt mir nur noch das Rezept wie ich da einen
Cast einbringen, so dass ich ptr1 und pt2 zusammen fassen kann.
Christian J. schrieb:> Wie formuliere ich nun eine Timeout Funktion, der ich beide> gleichartigen Funktionen als Parameter übergeben kann? Und wie würde> dann der Aufruf lauten?
Hier ist ein typedef tatsächlich hilfreich.
1
typedefuint32_t(*t_func)(uint16_t*,uint32_t);
Und damit schreibt sich Deine Funktion fast wie von selbst:
Allerdings stimmen Deine Funktionssignaturen der beiden Funktionen nicht
überein, die haben nicht den gleichen Rückgabewert.
Der Funktionspointertyp ist mit uint32_t definiert, "FlagStatus" und
"ErrorStatus" aber heißen jeweils anders.
Moment...
uint8_t timed(t_func fptr1, t_func fptr2)
Du willst da beide Funktionen übergeben aber es wird jeweils nur die
eine oder die andere übergeben.
Diese beiden Aufrufe
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
kommen abwechselnd sehr, sehr häufig vor im Programm, natürlich mit den
entsprechenden Parametern und sie werden jedesmal in eine Timeout
Schleife gekapselt. Aktuell habe ich so rund 20 einzelne kapselungen.
Und das will ich einfacher machen.
FlagStatus und ErrorStatus sind aus den StdPeriph Libs für den ARM.
Beides sind sehr verschwurbelte Integers, einer ein Struct, der andere
ein enum.
Christian J. schrieb:> Du willst da beide Funktionen übergeben aber es wird jeweils nur die> eine oder die andere übergeben.
Dann hättest Du die Aufgabenstellung präzisieren sollen, ich bin nicht
der erste, der drauf reingefallen ist.
Das Prinzip aber sollte erkennbar sein, und wenn Du jetzt noch dafür
sorgst, daß Deine Funktionssignaturen gleich sind, brauchst Du auch
keine Typecasts.
Sonst musst Du halt sowas unschönes schreiben:
1
result=timed((t_func)I2C_GetFlagStatus);
2
result=timed((t_func)I2C_CheckEvent);
Unschön ist so etwas, weil es potentielle Fehler verdeckt.
Ok, ich probiers mal, Prinzip ja soweit verstanden.... und wenn es gar
nicht hinhaut kapsel ich halt beide Funktionen in MyFunc1, MyFunc2 und
bringe die Schleife dort ein. Wollte halt nur mal verstehen wie das
funktioniert mit der Übergabe von Funktionen.
Wie spricht man denn die Parameter I2C_KANAL,
I2C_EVENT_MASTER_MODE_SELECT an im Funktionsrumpf?
typedef ErrorStatus (*t_func) (I2C_TypeDef*,uint32_t);
uint8_t timed(t_func fptr) {
while ((*fptr) != 0) {
timeout--;
if (timeout .....
....
}
return 1;
}
Ich muss fptr ja auch aufrufen.
Christian J. schrieb:> Wie spricht man denn die Parameter I2C_KANAL,> I2C_EVENT_MASTER_MODE_SELECT an im Funktionsrumpf?
Ein Funktionspointer ist genau das, ein Pointer auf die Funktion. Dass
du bei der Deklaration des Pointers die ParamterTYPEN (nicht Namen) mit
angibst, dient dem Compiler nur dazu, die Parameter korrekt übergeben zu
können. Die Parameter musst du gesondert übergeben.
So langsam rieselt der Kalk.... aber leider sind die Funktionssignaturen
nicht gleich, jeder Aufruf scheitert. na egal, heute nicht mehr.
Jedenfalls Danke für die Erklärungen!
typedef uint32_t (*t_func) (I2C_TypeDef*,uint32_t);
uint8_t timed(t_func fptr, I2C_TypeDef* param1, uint32_t param2 ) {
fptr(param1, param2);
return 1;
}
Kompiliert durch.
timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));
Wird angemeckert wegen inkompatiblen Rückgabewert.
src\eeprom.c|440|note: expected 't_func {aka long unsigned int
(*)(struct <anonymous> *, long unsigned int)}' but argument is of type
'ErrorStatus {aka enum <anonymous>}'|
Christian J. schrieb:> timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));>> Wird angemeckert wegen inkompatiblen Rückgabewert.
Jetzt aber vor lauter Pointern die Basics nicht vergessen. die
timed-Funktion hat 3 Parameter. Und du übergibst einen, nämlich den
Rückgabewert von I2C_CheckEvent.
Was du machen willst ist:
Felix U. schrieb:> Christian J. schrieb:>> timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));>>>> Wird angemeckert wegen inkompatiblen Rückgabewert.> Jetzt aber vor lauter Pointern die Basics nicht vergessen. die> timed-Funktion hat 3 Parameter. Und du übergibst einen, nämlich den> Rückgabewert von I2C_CheckEvent.>>> Was du machen willst ist:>
>> Das & ist nicht notwendig, verdeutlicht aber, dass hier nicht die> Funktion gecallt (so wie du es gemacht hast) sondern referenziert wird.
Das hier I2C_CheckEvent nicht aufgerufen wird, sieht man aber daran, das
hinter dem Namen I2C_CheckEvent der call-operator "()" nicht steht.
Was hier passiert ist etwas anderes: I2C_CheckEvent ist der Bezeichner
einer Funktion (function-designator). Für Funktionsbezeichner gibt es
die function-to-pointer Konvertierung: also I2C_CheckEvent evaluiert
(fast überall und immer, wenn als rvalue gebraucht) zu einem Zeigerwert
mit dem Typ "Zeiger auf Funktion ...". Nur wenn der Adress-Operator "&"
auf den Bezeichner einer Funktion wie I2C_CheckEvent angewendet wird,
wird der Bezeichnet nicht implizit konvertiert, und es passiert das
Erwartete: es wird die Adresse, also ein Zeigerwert mit dem Type "Zeiger
auf Funktion ..." bestimmt.
Umgekehrt geht das auch: wird der call-Operator "()" auf den Bezeichner
einer Funktion angewendet wie bei I2C_CheckEvent(), so wird die Funktion
aufgerufen. Wird der call-Operator auf einen Zeiger auf eine Funktion
angewendet, so wird auch die Funktion aufgerufen:
1
voidf(){}
2
intmain(){
3
f();// f wird aufgerufen
4
void(*p)();
5
p=f;// f wird zu einem Zeiger-auf-Funktion implizit konvertiert
6
p();// call-Op angewendet auf Zeiger auf Funktion
7
}
Man kann auch folgendes machen:
1
(******p)();
Hier ist dann `*p` das Funktionsobjekt, das wird implizit zu einem
Zeiger-auf-Funktion konvertiert, so dass `**p` wieder das
Funktionsobjekt ist, u.s.w.
Natürlich ist &p etwas anderes, denn p ist eine lvalue und kein
Funktionsbezeichner ...