Hi
ich bastle gerade an einen recht universellen Kommunikationsstack für
verschiedene Plattformen.
Prinzipiell wird über ein zu definierendes Interface ein Paket empfangen
(und vom Stack zusammengesetzt). Wenn der Empfang soweit erfolgreich war
(versch. Checksummen etc. stimmen) wird das Paket an das "Protokoll" an
das es adressiert ist, weitergereicht. Das "Protokoll" besteht aus einer
Funktion, die beim Stack (in Form eines Funktionspointers in einem Array
aus Handles (s.u.)) registriert wird. Diese Funktion übernimmt als
Parameter (in der non-dynamic-memory-version des Stacks) das Paket (in
Form eines structs) als call by value (by reference ist nicht möglich;
der Empfangsbuffer ist nach der Übergabe wieder freigegeben...) der
Funktion übergeben.
Soweit die Theorie. Problem: Die Übergabe funktioniert nicht.
Ich habe den Code soweit getestet, das Programm kommt beim dynamischen
Aufruf an; die Funktion wird aber nicht aufgerufen...
Der Code ist sehr umfangreich und in x Dateien verteilt ich versuche das
relevante herauszufiltern...
(Außer ihr wollt das gesamte Projekt in seinem gesamten Umfang haben;
das wären dann 62 Dateien und ~2700 Zeilen nur Code...)
Typen:
Function to register a protocol object. When you use a protocol object, you have to register it with this function.
3
It will "familiarize" COM with the object (it MUST be derivated from com_protocol) and link it to its UPID (used protocol id)
4
@param[in,out] *p pointer to the protocol object
5
@param[in] id the upid
6
@return bool (allways true)
7
8
*/
9
boolcom_RegisterProtocol(com_FPTRp,uint8_tid){//registers a protocol to the interface.
10
11
uint8_ti=0;
12
13
for(i=0;i<com_rx_protocol_handle_size;i++){
14
if(com_rx_protocol_handles[i].protocol==255){
15
com_rx_protocol_handles[i].protocol=id;
16
com_rx_protocol_handles[i].function=p;
17
com_rx_protocol_handle_amount++;
18
returntrue;
19
}
20
}
21
22
returnfalse;
23
}
24
25
//Beispielaufruf:
26
27
com_RegisterProtocol(rfCMD_TakePacket,rfCMD_pid);
Eine Protokoll-Funktion:
1
/**
2
Function which takes the packet.
3
@param *p the packet to work with.
4
@return bool success / fail
5
*/
6
voidrfCMD_TakePacket(com_packetp){
7
8
uint8_t*data;
9
10
data=p.data;
11
12
PORTB|=(1<<4);
13
PORTB^=(1<<3);
14
15
switch(data[0]){
16
17
caserfcmd_report_in:
18
PORTB|=(1<<2);
19
break;
20
}
21
22
}
(Die LEDs an PortB gehen NICHT an...)
So. Nun die Handover-Funktion. Diese Funktion ist dafür zuständig das
Protokoll-handle-array zu durchsuchen und die entsprechende Funktion zu
callen:
1
/**
2
Function to hand over a received packet to the specified protocol (next_protocol field)
Ich weiß dass es nicht gerade praktisch ist, nicht den gesamten Code zu
posten; aber ich habe - wie gesagt - das Problem bereits auf diesen
Bereich eingegrenzt.
Ich habe mir gedacht dass es evtl. ein Stack-Problem sein könnte; finde
aber keine Möglichkeit den Stack manuell zu vergrößern?
Irgendein Runtime-Problem...
Ich verwende WinAVR-20080610 mit Code::Blocks; mein Code compiliert
völlig anstandslos.
Jemand ne Idee?
Tobias Schlegel schrieb:
Hast du bereits mehrere Protokolle angemeldet und auch wieder entfernt?
Wenn ja, dann musst du eventuell beachten (je nachdem was du in der
remove Funktion machst), dass der Wert in com_rx_protocol_handle_amount
nicht bedeutet, dass die ersten com_rx_protocol_handle_amount Einträge
gültig sind, sondern nur, dass es im Array insgesamt
com_rx_protocol_handle_amount gültige Handler gibt.
Ergo:
> uint8_t com_handover(com_packet p){> uint8_t i = 0, j = 255;>> for(i = 0 ; i < com_rx_protocol_handle_amount ; i++){
for(i = 0; i < com_rx_protocol_handle_size ; i++){
> if(com_rx_protocol_handles[i].protocol == p.protocol){> //PORTB ^= (1<<3); //funktioniert> com_rx_protocol_handles[i].function(p); //<- FUNKTIONIERT> NICHT???
Hier wäre eine Absicherung angebracht und versuch mal die 'schöne
Funktion über Pointer rufen'-Syntax, auch wenn es eigentlich keinen
Unterschied machen sollte:
if( com_rx_protocol_handles[i].function )
(*com_rx_protocol_handles[i].function)(p);
Das Handles Array hast du sauber initialisiert?
Du solltest dir auch noch überlegen, wieviele Kopien des Pakets du noch
machen möchtest (kostet ja alles Zeit und Speicher). In com_handover
wird schon 1 Kopie erzeugt und beim Aufruf der Protokollfunktion wird
die nächste Kopie erzeugt. Wenn du so weiter machst, wird dir ziemlich
schnell, vor allem auf kleineren µC, der Speicher ausgehen, zumal
com_max_payload_data ja wohl auch nicht gerade klein sein wird.
Hi,
die Möglichkeit Protokolle zu entfernen gibt es (noch) nicht (es könnten
u.A. auch Lücken im Lookup entstehen etc.). Daher sollte
com_rx_protocol_handle_amount immer up to date sein ... eigentlich.
Ja, die Arrays werden initialisiert:
Leider bleibt PortB2 LOW und sonst auch alles beim Alten. An einem
fehlerhaften Pointer liegts also wohl auch nicht...
Es wäre evtl. noch zu erwähnen, dass meine Display-Menu-Library (
http://www.tobias-schlegel.de/?page_id=382 ), die auch mit dynamischen
Funktionscalls arbeitet (allerdings ohne Übergabeparameter), auf dem
selben System anstandslos funktioniert.
Tjoa. Ich bin ratlos.
Danke auf jeden Fall!
Tobias Schlegel schrieb:
Mach da mal ganz schnell ...
> //Initialisierung des Handle-arrays; protocol == 255 -> "leeres" handle> for(i = 0 ; i < com_rx_protocol_handle_size ; i++){> com_rx_protocol_handles[i].protocol = 255;
com_rx_protocol_handles[i].function = NULL;
> }
... rein.
Einen Pointer lässt man nicht auf irgendeinem Wert rumstehen.
> uint8_t com_handover(com_packet p){> uint8_t i = 0, j = 255;>> for(i = 0 ; i < com_rx_protocol_handle_amount ; i++){
Noch mal.
Hier nicht über amount gehen.
Wenn der Wert aus irgendeinem Grund nicht stimmt, bist du angeschmiert.
Dein Array ist com_rx_protocol_handle_size gross, also solltest du auch
das ganze Array abgrasen, egal in welchem Zustand es ist.
Über die size zu gehen ist weder schneller noch langsamer. Aber du hast
die Sicherheit, dass der Handler auf jeden Fall gefunden wird, wenn der
eigentliche Eintrag stimmt. Die amount Variable braucht im Grunde kein
Mensch und ist 'nice to have' aber ansonsten ist es nur ein Wert der
falsch sein kann.
Aber das wird jetzt, zu diesem Zeitpunkt, nicht dein eigentliches
Problem sein.
> Leider bleibt PortB2 LOW und sonst auch alles beim Alten. An einem> fehlerhaften Pointer liegts also wohl auch nicht...
Extrahiere einmal den ganzen Handler-Teil in ein eigenständiges Projekt,
damit du erst mal den ganzen restlichen Overhead zum Testen los wirst.
Dann kann man auch auf dieser Seite des Schirms den kompletten Code
untersuchen anstatt immer nur Fragmente.
ich sehe da auch keinen Fehler, außer wie bereits genannt
die Übergabe als Kopie auf dem Stack.
Hilfreich wäre es, ein vollständiges und möglichst kleines
komplettes Programm mit dem Fehler zu sehen.
Karl heinz Buchegger schrieb:
> Über die size zu gehen ist weder schneller noch langsamer. Aber du hast> die Sicherheit, dass der Handler auf jeden Fall gefunden wird, wenn der> eigentliche Eintrag stimmt. Die amount Variable braucht im Grunde kein> Mensch und ist 'nice to have' aber ansonsten ist es nur ein Wert der> falsch sein kann.
Pikanterweise sehe ich zb. in deiner Init Funktion nirgends, das dieser
Wert irgendwann einmal auf 0 gesetzt worden wäre.
Also: Komm ohne ihn aus. Du brauchst ihn nicht. Und wenn du ihn
tatsächlich mal für irgendetwas benötigst (zb Anzeigen), kannst du
genausogut im Array die belegten Plätze abzählen. Ist sicherer. Alte
Datenbankweisheit: Speichere niemals denselben Sachverhalt zweimal ab.
Im besten Fall hast du einfach nur Redundanz. Im schlimmsten Fall hast
du widersprüchliche Werte.
Hi
Ok, ich verwende jetzt ..._size .. auch ohne Erfolg. Der Pointer wird
jetzt auch initialisiert.
Ich hab euch alles mal in ein kleines (20 Files und ~900 Zeilen code
only) funktionsfähiges (incl. Fehler) Beispiel zusammengepfercht (->
Anhang). (Einfach alle Files compilieren und dann alles zammenlinken...)
Ich habe das File auch auf meinen AVR überspielt; der Fehler tritt auch
hier auf (ich kann die LED aus der handover-Funktion ein und aus
schalten, aber den Rest nicht); was sehr gegen den Stack spricht, da
jetzt das halbe AVR-Ram leer sein dürfte (Korrektur >90% sind leer).
Ich versuche jetzt mal eine Übergabe by Reference mit anschließender
Kopie des Inhalts...
hm hm hm.
Tobias Schlegel schrieb:
> Ok, ich verwende jetzt ..._size .. auch ohne Erfolg. Der Pointer wird> jetzt auch initialisiert.> Ich hab euch alles mal in ein kleines (20 Files und ~900 Zeilen code> only) funktionsfähiges (incl. Fehler) Beispiel zusammengepfercht (->> Anhang).
Sei mir nicht böse.
Aber das ist nicht klein.
Klein ist
1
voidfoo(com_packetp)
2
{
3
;
4
}
5
6
intmain()
7
{
8
com_packetp;
9
10
p.protocoll=8;
11
12
com_init_internal(...);
13
com_RegisterProtocol(foo,8);
14
15
com_handover(p);
16
}
Und alles, was nichts mit diesen 3 Zeilen in main zu tun hat (Frames und
sonstiges) fliegt erst mal raus oder wird auskommentiert oder mittels
#define/#ifdef stillgelegt.
Ich habe das Ding halt so gehalten um es auch auf der Zielhardware
testen zu können...
Puh. Ich seh mal was man da machen kann...
Please hold the line...
Tobias Schlegel schrieb:
> Ich habe das Ding halt so gehalten um es auch auf der Zielhardware> testen zu können...
Nur haben wir die hier nicht.
Zuerst müssen wir sicherstellen, dass der Effekt real ist und nicht
durch irgendwelche Nebeneffekte von Code, der erst mal nichts mit deinen
Handlern zu tun hat, entsteht (ein Array Overflow irgendwo kömmt mir da
zb ganz schnell in den Sinn)
Bau in foo von mir aus eine LED Ausgabe rein, das ist schon ok.
Irgendwas, damit du auf deiner Zielhardware mit der absoluten
Mindestsoftware den Effekt überprüfen kannst.
Aber es muss soviel wie möglich abgespeckt werden! Natürlich nur soviel,
dass der Effekt noch da ist. Wenn obiges mit einer LED-Ausgabe in foo
funktioniert, dann gibt es höchst wahrscheinlich irgendeine ungewollte
Interaktion mit einem anderen Softwaremodul. Das wird dann schwieriger
zu finden. Aber zumindest hat man dann einen kleinen Anhaltspunkt wonach
man suchen muss. Aber 20 Files aufs Geratewohl zu durchforsten, ist
nicht sehr zielführend.
Also, so abgespeckt wie nur geht.
Lustiger Weise schient die hier Sache zu funktionieren...
Ein überlaufenes Array o.ä. ist denk ich tatsächlich ein guter Kandidat.
stand by...
Ich hab mir dein großes Projekt ins AVR Studio gezogen. Aber da pasiert
einfach zuviel und das Faken einer Übertragung im Debugger ist dort
extrem mühsam.
Tobias Schlegel schrieb:
> Also, so abgespeckt wie nur geht.>> Lustiger Weise schient die hier Sache zu funktionieren...>> Ein überlaufenes Array o.ä. ist denk ich tatsächlich ein guter Kandidat.
OK. Das ist schon mal was.
Karl heinz Buchegger schrieb:
> Ich hab mir dein großes Projekt ins AVR Studio gezogen. Aber da pasiert> einfach zuviel und das Faken einer Übertragung im Debugger ist dort> extrem mühsam.
Wenn du es so hinkriegen könntest, dass von gefakten UART-Funktionen die
richtigen Bytes in der richtigen Reihenfolge in die Übertragungskette
eingespeist werden, wäre das extrem hilfreich (jetzt bezogen auf dein
'großes' Testprojekt)
Sowas in der Art
1
staticunsignedcharFake[]="was auch immer über die Schnittstelle kommen"
2
"muss um letztendlich den Call auszulösen";
3
staticintnextByte=0;
4
5
boolcom_IF_check_byte_ready()
6
{
7
returntrue;
8
}
9
10
unsignedcharcom_IF_read_byte()
11
{
12
nextByte++;
13
returnFake[NextByte];
14
}
Du könntest auch bei den uart Funktionen (also noch tiefer) diesen Fake
aufsetzen.
Im Idealfall brauch ich mich um nichts kümmern, setz mir einen
Breakpoint in die handle Funktion, lass das Teil laufen und lande
irgendwann beim Breakpoint. So ein Fake ist sowieso sehr hilfreich, weil
du dann bei jedem Testlauf identische Bedingungen hast (und ich
dieselben Daten habe, die du auch benutzt)
Hier eine com_interface.c mit gefaketem Interface; wie bestellt.
Das Paket ist ein low-level Broadcast mit dem Befehl 1 an das Protokoll
1... sollte funktionieren.
Tobias Schlegel schrieb:
> Hier eine com_interface.c mit gefaketem Interface; wie bestellt.>> Das Paket ist ein low-level Broadcast mit dem Befehl 1 an das Protokoll> 1... sollte funktionieren.
Hmm.
Habs im AVR Studio für einen Mega16 compiliert und ... funktioniert so
wie gedacht. Die Funktion wird aufgerufen. Habe beides -O0 und -Os
ausprobiert.
Tobias Schlegel schrieb:
> Gut, im Simulator gehts; im Reallife gehts nicht.> Somit fängts hiermit an, kompliziert zu werden... oder..?
Jep.
Tut mir leid, dass ich dir nicht weiter helfen kann, aber ohne reale
Hardware ist das jetzt sauschwer, wenn in der Simulation der Fehler
nicht passiert.
Wenn du kannst:
lass dir am Anfang die Adresse der Funktion irgendwo ausgeben (UART?)
lass sie dir beim indirekten Aufruf noch einmal ausgeben
Und dann kann man eigentlich nur hoffen, dass die beiden Ausgaben nicht
identisch sind. Sind sie identisch und wird die Funktion trotzdem nicht
ausgeführt, tippe ich auf Stack-Unordnung.
Aber ich hoffe mal, dass sie nicht übereinstimmen. Denn dann kann man
zusätzliche Ausgaben an 'stategischen Stellen' einbauen und rausfinden,
wo sie verändert wird und so den Bug immer weiter einkreisen.
hm viel mehr fällt mir da auch nicht ein; ich lass mal die Adressen
ausgeben.
Und sollte das immernoch nicht klappen, vergleiche ich mal die
nano-variante mit der anderen. Da muss es einen signifikanten
Unterschied geben...
Kann man irgendwo am Stack rumspielen ohne im Linkerscript zu drehen?
Hmm. Wenn com_rx_protocol_handles hier extern ist (überraschenderweise),
wo befindet sich dann die Definition dieses Arrays, ich kann sie nicht
finden? (Irgendwo muss es ein com_rx_protocol_handles geben, das nicht
als extern markiert ist)
Wenn com_IF_read_byte nur in diesem File benutzt wird, dann mach dieses
Funktion besser static. Wenn auch von ausserhalb diese Funktion
aufgerufen werden soll, warum ist ihr Prototyp dann nicht im Header
File? (Dito natürlich für alle anderen Funktionen)
Aber das hat jetzt nicht wirklich was mit deinem Problem zu tun. Denke
ich mal.
com_rx_protocol_handles[com_rx_protocol_handle_size] wird in com.c
definiert.
Das ist so; die Files die du hast sind ein Port der C++-Variante des
Stacks (die wiederum ein Port dieser Variante ist). Das ist also alles
ein bisschen hin und her portiert. Sprich manche Funktionsdeklarationen
kommen sowohl in der Datei als auch in den Headern vor. Das Chaos ist
mir bekannt; allerdings hast du recht, das ist gerade auf meiner
to-do-list recht weit unten (solange es funktioniert) ;)
§=%("=( warum geht das nicht...
Hm ich sehe keine Chance ausser du hast zufällig einen Mega16/32
rumliegen...
Hm Hm Hm. Ich versuch mal die Variante mit call by reference.
Es bleibt spannend ;)
Das funktioniert; es scheint also nicht am dynamischen Call zu liegen...
die Frage ist... an was liegt es dann?!
Soll ich mal meinen WinAVR updaten und schauen obs dann geht?
Irgendwas ist hier gewaltig faul...
Hi,
Problem durch Erkennen der Nichtexistenz gelöst...
Ich habe festgestellt, dass genau die Funktion des anderen Protokolls
aufgerufen wurde.
Und warum?
Die Protokolle des C++-Stacks (auf dem PC) hatten zwar die gleichen
PIDs, allerdings vertauscht. Also kam Das Paket immer beim falschen
Protokoll an.
Oh mannn so blöd muss man erstmal sein.
Sorry für dieses Riesentheater und den Aufwand. Und danke für die
tatkräftige Unterstützung (@Karl heinz Buchegger!) !!