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.

 Thread beobachten
 Thread beobachten Seitenaufteilung abschalten
 Seitenaufteilung abschalten