Hallo, ich muß ein Stück Code nachträglich an ein Programm anfügen. Mein Programm des AVR besteht aus zwei Teilen. Ein zweiter kann verändert werden. Da ich nun das Problem habe, das ich nicht weis wo im Zweiten Teil meine Funktionen stehen, habe ich mir eine Structur erzeugt und diese dynamisch (zum Test) gefüllt. Klappt alles prima und die passenden Funktionen werden aufgerufen. Nun muß ich dem Compiler beibringen, das er mir eine Statische Funktionsliste zur Compilezeit erzeugt und an den Anfang des Blocks schreibt. Wie macht man sowas ? Das ganze an den Anfang stellen geht ja mit Sections, nur wie fülle ich nun meine Structur zur Compilezeit ? Oder ist das der falsche Ansatz ? Im Prinzip will ich eine ".o" Datei per Bootloader flashen und die Funktionen darin nutzen. Mein Structur: struct _sFuncTable { void (*InitUA) (void); // Funktion zum Init des Device void (*mainUA) (void); // funktionen der UA die pro Mainloop einmal ausgeführt werden soll void (*setLevel) (uint8_t level, uint8_t value); // Level setzen void (*setChannel) (uint8_t channel, uint8_t value); // Channel setzen void (*processCommand) (char *cmd, uint8_t length); // Verarbeite Kommando void (*processString) (char *str, uint8_t length); // Verarbeitet String // funktionen für Managment _sFuncTable void InitFuncTable(void); // Setzt Dummy funktionen _sFuncTable() {InitFuncTable();}; }; Danke Juergen
Ich kenne keinen Weg, die Struktur zur Compilezeit mit den Funktionsadressen zu initialisieren. Hoffentlich bekommen wir da noch Tipps ;-) Ich würde derzeit eine bekannte Adresse der Struktur einrichten (vielleicht unterhalb vom Stack und oberhalb der DATA und BSS Sektion in der Art wie man einen Heap einrichtet?) oder eine bekannte Adresse wo eine Funktion selbst oder ein Pointer darauf steht (Anfang/Ende vom Bootloaderbereich?), die dann aufgerufen wird und die übergebene Strukturadresse initialisiert.
Hallo, wie ist es dann mit diesem Ansatz ? : ------------ #define Func void (*)(void) // Die Funktionen die in die Tabelle sollen void setLevel(uint8_t level, uint8_t value) {} // Level setzen void processCommand(char *cmd, uint8_t length) {} // Verarbeite Kommando void processString(char *str, uint8_t length) {} // Verarbeitet String void initUA(void); void mainUA(void); void setChannel(uint8_t ch, uint8_t val); // Die Tabelle ! UA_SECTION void (*FC[])(void) = {initUA, mainUA, (Func)setLevel, (Func)setChannel, (Func)processCommand, (Func)processString}; ------------ UA_SECTION ist wie folgt definiert: #define UA_SECTION _attribute_ ((section (".uasection"))) und wird im Makefile mir "LDFLAGS += -Wl,--section-start=.uasection=$(0x1700)" dem Linker Parametern hinzugefügt Ich sehe auch, das eine section mit 12 Byte angelegt wird, was ja meinen 6 Funktionspointern a 2 Byte entspricht, nur ich finde sie nirgens. Ist diese nun Statisch beim Linken bereits vorhanden oder nicht ? Sections: Idx Name Size VMA LMA File off Algn 0 .bootloader 0000005c 00001e00 00001e00 000016f6 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .uasection 0000000c 00001700 00001700 000016ea 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .text 000015ee 00000000 00000000 000000f4 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE 3 .data 00000008 00800060 000015ee 000016e2 2**0 CONTENTS, ALLOC, LOAD, DATA 4 .bss 0000002e 00800068 00800068 00001752 2**0 ALLOC 5 .noinit 00000000 00800096 00800096 00001755 2**0 CONTENTS 6 .eeprom 00000003 00810000 00810000 00001752 2**0 CONTENTS, ALLOC, LOAD, DATA 7 .stab 000046e0 00000000 00000000 00001758 2**2 CONTENTS, READONLY, DEBUGGING 8 .stabstr 000036eb 00000000 00000000 00005e38 2**0 CONTENTS, READONLY, DEBUGGING Danke Juergen
Warum probierst du es nicht einfach aus? Es wird funktionieren, genau wie alle anderen initialisierten Daten. Benötigt sowohl RAM (für die Variable) als auch ROM (für den Initializer), aber das ist hier sicher nicht tragisch.
Hallo, hab ich schon versucht, nur funktioniert es nicht. Dadurch das es zum Linkerzeitpunkt schon feststehen muß, kommt es wohl zu Problemen. Wie gesagt es ist ja die Einsprungstabelle in die UA, damit das eigendliche Programm weis WO welche Routine steht. Da das eigendlich Programm (PA) und die Routinen(UA) nicht zusammen gelinkt werden, weis der Compiler nichts von den Funktionen. Das PA prüft also nach einem Start ob die Adressen in der Sprungtabelle gültig sind (stehen an einer fixen Adresse). Wenn JA update ich meine Structur mit den Werten aus der Sprungtabelle. Wenn NEIN zeigt meine Structur auf Dummy Funktionen die sofort wieder zurückspringen. Dadurch erspare ich mir das Prüfen auf Gültigkeit während der Laufzeit. Die PA wertet also das Protokoll des Buses aus und ruft dann Funktionen in der UA auf, die die Reaktion auslösen (z.B. Relais schalten). Mann könnte den UA Bereich als "dynamisch Funktionsbibliothek" verstehen, die durch ein Flashupdate über den Bus die Aplikation verändert. Gruss Juergen Sachs
Dann ist wohl deine Prüfung daneben. (Ich weiß nicht, was deine UA und PA sein sollen.) Funktionszeiger funktionieren mit Sicherheit im GCC, und sie funktionieren auch etwa so, wie du das skizziert hast. Die restliche Magie dahinter darfst du gern dem C-Laufzeitsystem überlassen, dafür ist das da.
PA = Protokoll Area (Die Allgemeine Implementierung des Protokolls) UA = User Area (Die Gerätespezifische Implementierung) Ich habe folgendes gemacht: #define Func void (*)(void) UA_TABLE_SECTION void (*FuncTable[])(void) = {(Func)initUA, (Func)mainUA, (Func)setLevel, (Func)setChannel, (Func)processCommand, (Func)processString}; sehe ich mir nun das ".lss" File an, liegen die Funktion an folgenden Stellen: initUA: 17aa mainUA: 1a92 setLevel: 1b32 setChannel: 17ca processCommand: 1b34 processString: 1b36 Der Abstand zu "1740" bei der ertsen Routine ergibt sich durch andere Funktionen in der gleichen Sektion, die aber nicht in der Sprungtabelle enthalten sind. Sehe ich mir das Hex File für die Adressen der Sprungtabelle an, steht dort aber: :10170000D50B490D990DE50B9A0D9B0D9B0D9B0D6E :1017400080918B00809580938B0080918C00809598 :1017500080938C00F89492B3977080918C00887F6E :10176000982B92BB789498B3937080918B008C7F68 Also stehen komplett wirre Adressen in Sprungtabelle.... Die Function Table ist auf 0x1700 und die nachfolgenden Funktionen auf 0x1740 per Sections gelegt, was nachfolgende Tabelle ja auch bestätigt: Sections: Idx Name Size VMA LMA File off Algn 0 .bootloader 00000000 00001e00 00001e00 00001739 2**0 CONTENTS 1 .uatablesection 00000010 00001700 00001700 0000132e 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .uasection 000003f8 00001740 00001740 0000133e 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 3 .text 00001232 00000000 00000000 000000f4 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE 4 .data 00000008 00800060 00001232 00001326 2**0 CONTENTS, ALLOC, LOAD, DATA 5 .bss 0000002e 00800068 00800068 00001736 2**0 ALLOC 6 .noinit 00000000 00800096 00800096 00001739 2**0 CONTENTS 7 .eeprom 00000003 00810000 00810000 00001736 2**0 CONTENTS, ALLOC, LOAD, DATA 8 .stab 00004500 00000000 00000000 0000173c 2**2 CONTENTS, READONLY, DEBUGGING 9 .stabstr 00003670 00000000 00000000 00005c3c 2**0 CONTENTS, READONLY, DEBUGGING Aber vielleicht mache ich hier auch einen Auswerte Fehler. Es Funktioniert jedenfalls nicht wenn ich es in den Controller lade. Ich mache mal ein kleines Beispiel Projekt zur Verdeutlichung und Poste das dann hier. Gruss Juergen Sachs
So, hier nun das Beispiel. Komplett schnell Runterprogrammiert. Also nicht "sauberster Stiel" Das Programm sollte erst alle LEDs an Port B blinken lassen, dann Bit 7, dann Bit 6, in initUA() Bit 5, in mainUA() Bit 4, und am Ende Bit 3. Bit 4 und Bit 5 bleiben Dunkel. die Funktionen initUA() und mainUA() werden also nicht ausgeführt. Ich hab mal die Quellcodes, Makefile , das .lss file usw mit eingepackt. Das ganze ist jetzt eben für einen atmega8515. Was anderes hab ich im Moment nicht zur Hand für mein STK200. Achja, die Sprungtabelle muß zur Compilezeit angelegt werden, nicht zur Laufzeit ! Gruss Juergen Sachs
Jürgen Sachs wrote:
> Ich habe folgendes gemacht:
1 | > #define Func void (*)(void) |
2 | > UA_TABLE_SECTION void (*FuncTable[])(void) = {(Func)initUA, |
3 | > (Func)mainUA, (Func)setLevel, (Func)setChannel, (Func)processCommand, |
4 | > (Func)processString}; |
Lass die Typecasts weg. Sie riskieren nur, dass du dir Programmierfehler verbergen lässt. > Sehe ich mir das Hex File für die Adressen der Sprungtabelle an, steht > dort aber: Aua. Guck dir doch keine Hexfiles an, das ist doch Masochismus! Es gibt so viele andere Varianten dafür, einschließlich diverser Optionen von Disassemblern... Ich bin zu faul das nachzurechnen, aber ich vermute mal, dass du einfach nur drüber stolperst, dass beim AVR alle Codeadressen die durch 2 geteilten Byteadressen sind, d. h. es wird in 16-bit-Worten adressiert. > Es Funktioniert jedenfalls nicht wenn ich es in den Controller lade. Dann musst du das debuggen, statt den Fehler beim Compiler zu suchen.
Habe nur oberflächlich über den Code geschaut. Wie von Jörg bereits angemerkt, kann es ein Problem zwischen Byte- und Word-Adressen sein. Es lohnt sich allerdings auch ein Blick darauf, welche Daten von welcher Adresse aus welchem Speicherbereich geladen werden. Falls die Addressen der Funktionen im Flash-Speicher liegen, dann werden die lds vor icall wie im disassembly nicht helfen, lpm wird dann benoetigt, oder man kopiert den Inhalt der Tabelle um (ist vielleicht schon so, kann ich auf Anhieb aber nicht am Code nachvollziehen). Einfach mal AVR-Studio Simulator anwerfen mit der elf-Datei und passendem Target. Sollte helfen, das nochzuvollziehen. Martin Thomas
Jörg Wunsch wrote: > Lass die Typecasts weg. Sie riskieren nur, dass du dir > Programmierfehler verbergen lässt. Die sind deshlab drinn, weil nicht alle Funktionen Parameterlos sind alos void (*)(void). Da geht ohne Cast nichts. > >> Sehe ich mir das Hex File für die Adressen der Sprungtabelle an, steht >> dort aber: > Aua. Guck dir doch keine Hexfiles an, das ist doch Masochismus! Ich begnüge mich normalerweise mit dem ".lss" File :-) Nur wenn ich 100% Wissen will was an der Adresse steht, hilft nur das HEX File ?! > Es gibt so viele andere Varianten dafür, einschließlich diverser > Optionen von Disassemblern... > Ich bin zu faul das nachzurechnen, aber ich vermute mal, dass du > einfach nur drüber stolperst, dass beim AVR alle Codeadressen die > durch 2 geteilten Byteadressen sind, d. h. es wird in 16-bit-Worten > adressiert. Hmm, sollte das der Compiler nicht berücksichtigen ?? Ok, dann sollte ich z.B nicht 0x1740 für die erste funktion im Speicher stehen haben, wie ich es für die Section vorgebe, sondern 0xBA0 (die Hälfte eben) >> Es Funktioniert jedenfalls nicht wenn ich es in den Controller lade. Interessanter Weise verhält sich der Proz so als gäbe es die Zeilen nicht. Rennt nicht ins Nirvana, oder sonstiges.... uff. > Dann musst du das debuggen, statt den Fehler beim Compiler zu suchen. Das ist so meine schwache Seite. Keine Ahnung wie ich das unter Linux mache.... Noch keine Einfache Anleitung gefunden, hat jemand einen Tip ? Oder ist es das beste doch noch das ganze unter Windows zu Debugen ? Danke Juergen
Martin Thomas wrote: > angemerkt, kann es ein Problem zwischen Byte- und Word-Adressen sein. Es > lohnt sich allerdings auch ein Blick darauf, welche Daten von welcher > Adresse aus welchem Speicherbereich geladen werden. Falls die Addressen > der Funktionen im Flash-Speicher liegen, dann werden die lds vor icall > wie im disassembly nicht helfen, lpm wird dann benoetigt, oder man > kopiert den Inhalt der Tabelle um (ist vielleicht schon so, kann ich auf > Anhieb aber nicht am Code nachvollziehen). Einfach mal AVR-Studio > Simulator anwerfen mit der elf-Datei und passendem Target. Sollte > helfen, das nochzuvollziehen. Wie ich oben schon schrieb, bin ich in Sachen Debuging ein blutigster Anfänger. Und die ganze Entwicklerumgebung auch noch unter Windows zu Installieren hätte ich mir gerne verkniffen. Unter Linux läuft das so schön rund. Da muß ich vermutlich mein Makefile usw. anpassen. Wenn ich das Richtig verstehe, muß ich die Funktionsadressen / 2 teilen um die Adressen zu haben, die Letztendlich im Speicher der Sprungtabelle stehen müssen. Das wusste ich jetzt noch nicht. Aber man lernt ja nie aus. Wie gesagt einer nen guten link für ein Debuging HOWTO. Wäre ich Dankbar für. Gruss Juergen
Unter Linux kann ich dir auch nicht weiterhelfen. > Und die ganze Entwicklerumgebung auch noch unter Windows zu > Installieren hätte ich mir gerne verkniffen Das geht ziemlich problemlos. Von Atmel das AVR-Studio holen. Von http://winavr.sourceforge.net/ das Compiler-Packet holen. AVR-Studio installieren WinAvr installieren Fertig. AVR-Studio aufmachen. Neues Projekt anlegen. Alle C und H Files auf das Projekt-Verzeichnis kopieren. Dann im AVR-Studio die Files zum Projekt hinzufügen. F7 drücken und warten bis alles kompiliert und gelinkt ist (merkst du was? Du brauchst kein Makefile erstellen, macht AVR-Studio für dich). Ist das Pgm soweit fertig, mit Strg-Alt-Shift-F5 (kein Witz, die Kombination ist wirklich so! Wer sich das ausgedacht hat, müsste an die W...) den Debugger starten und mit F10 bzw. F11 im Singlestep durch das Pgm durchgehen. Variablen-Inhalte werden angezeigt, wenn man mit der Maus auf den Variablennamen zeigt. Mit F9 wird ein Breakpoint gesetzt, bzw. gelöscht. Im Kontextmenü, bzw. im Debug-Menü gibts noch ne Menge mehr zu entdecken. Der springende Punkt ist, dass sich WinAvr relativ nahtlos in AVR-Studio integriert. Ein paar Stolpersteine gibt es, allerdings nichts was dich im Moment berührt.
Ok, sehe ich mir demnächst an. Aber: Ein Hinweis hat mich doch Stuzig gemacht, Zitat: > Falls die Addressen > der Funktionen im Flash-Speicher liegen, dann werden die lds vor icall > wie im disassembly nicht helfen, lpm wird dann benoetigt..... Argh, Ja tun Sie natürlich. Testprogramm wie folgt abgeändert: void (*initUA)(void) = (Func)pgm_read_word(UA_TABLE_START_ADR); void (*mainUA)(void) = (Func)pgm_read_word(UA_TABLE_START_ADR+2);//( _sFuncTable *)UA_TABLE_START_ADR; also mit "pgm_read_word()" auf die Adressen zugegriffen, und siehe da es geht. Danke für den Hinweis. Zigmal schon Strings aus dem Flash gelesen.... Naja, der Fehler passiert einem Vermutlich nur einmal :-) Danke für den Wink mit dem "LPM" (Auch wenn ich erst nachschauen mußte was der macht :-) ) Ich werde mal das in meinem Großes Projekt übernehmen und lasse mich überraschen. Gruss Juergen
Jürgen Sachs wrote: >> Lass die Typecasts weg. Sie riskieren nur, dass du dir >> Programmierfehler verbergen lässt. > Die sind deshlab drinn, weil nicht alle Funktionen Parameterlos sind > alos void (*)(void). Da geht ohne Cast nichts. Das ist doch aber ziemlicher Quatsch dann, wie sollen die Funktionen, die Parameter erwarten, denn dann zu ihren Parametern kommen? Dann kannst du auch gleich alles auf (void *) casten. Das ist zumindest (sogar im Sinne des C-Standards) sauber: ein beliebiger Zeiger darf in einen void * umgewandelt werden und von dort wieder in den ursprünglichen Zeiger. Die Information, welchen Typ der ursprüngliche Zeiger genau hatte, muss dann natürlich irgendwo ,,außerhalb'' ermittelt werden.
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.