Hallo zusammen. Frage: Warum kann ich beim gcc keinen Funktionspointer, wie in C üblich, bei der Deklaration mit einer Adresse initialisieren? unsigned char (*fu_pointer)(unsigned char) = 0x1f14; Was ist an dieser Deklaration falsch? Gruß, Kurt
Weil man das auch sonst nicht kann, jedenfalls nicht so. Wenn doch und ohne Warnung, gib von mir eine Ohrfeige an den Compiler weiter. 0x1f14 ist nunmal kein Pointer, also stimmt der Typ nicht. Besser: typedef unsigned char (*myfcnptr)(unsigned char); fcnptr fu_pointer = (fcnptr)0x1f14; Empfehlen kann ich es freilich nicht. Denn ob das eine Byte- oder Wortadresse ist, mag je nach Compiler verschieden sein.
Tippfehler. Richtig: typedef unsigned char (*myfcnptr)(unsigned char); myfcnptr fu_pointer = (myfcnptr)0x1f14;
Hallo A.K. Vielen Dank für die schnelle Antwort. Der Compiler meckert nicht mehr, aber die Pointerinitialisierung funktioniert leider nicht richtig, da beim Funktionsauruf im gestarteten Programm, die Funktion leider nicht angesprungen wird sondern das Programm sich irgendwohin verirrt! Die zugewiesene Funktions-Adresse (Funktion im Boot-Loader-Flash) ist richtig (habe ich durch auslesen des Boot-Loader-Flash überprüft). Wie kann ich die Zuweisung überprüfen (Mapfile oder sowas?). Kurt
Yep, ich sach noch, pass auf die Byte/Wort-Adressierung auf. Flash-Adressen sind je nach Kontext Byte- oder Wort-Adressen. Kann also sein dass du mit 0x1f14*2 oder 0x1f14/2 mehr Erfolg hast.
Ja, aber ich bin doch hier im GCC-Forum und spreche doch auch vom GCC. Also noch einmal meine zweite Frage: Wie kann ich überprüfen, ob die initialisierung richtig vorgenommen wird (Mapfile oder so)? Übrigens, laut C-Lehrbuch und auch Informationen aus dem Net, ist die Deklaration eines Funktions-Pointers mit direkter Initialisierung in C wie: unsigned char (*fu_pointer)(unsigned char) = 0x1f14; richtig und dürfte vom Compiler nicht beanstandet werden! HILFE..., Kurt
Übrigens würde ich dafür eine jump table an den Anfang des Bootloaders setzen, damit sich die Adressen nicht mehr verändern. In der Applikation bekommen all die Einsprünge da eine struct of function pointers in eine eigene .section, die dann per --section-start im Linker auf die Adresse im Bootloader gesetzt wird.
Ich habe die Initialisierung des typedef-Funktions-Pointers (wie oben von A.K. vorgeschlagen) mal innerhalb einer Funktion durchgeführt. Das macht der Compiler dann aus dem Funktionsaufruf: fu_pointer(72); 6e: 88 e4 ldi r24, 0x48 ; 72 70: 7f df rcall .-258 ; 0xffffff70 <__eeprom_end+0xff7eff70> Der Funktions-Pointer ist mit der Adrese 0x1f14 initialisiert. Wie kommt der Compiler darauf, dass sich die Funktion im EEPROM befindet oder was bedeutet der angefügte eeprom-end-Kommentar? Muss ich dem Compiler irgendwie mitteilen das es sich um eine Funktion im Flash handelt? Kurt
Ich finde nicht, dass der Compiler die Funktion im EEPROM vermutet. Wenn du deine normalen Daten im EEPROM liegen hast, liegt natürlich auch die Variable fu_pointer im EEPROM. Für den Funktionsaufruf sollte das keine Rolle spielen, wenn der Inhalt der Variablen eine gültige Flash-Adresse einer Funktion ist. D.h. wie kommst du auf den absoluten Wert 0x1f14?
Dass hier EEPROM-Adressen angezeigt werden, hat weniger mit dem EEPROM und viel dem etwas hilflosen Adressmapping vom Von-Neumann-GCC auf eine Hardward-CPU zu tun, und dem verzweifelten Versuch des Disassemblers, darin einen Sinn zu erkennen. Hilfreicher wäre es gewesen, den Typ vom AVR und die Adresse den RJMP zu kennen. Wenn es sich um einen 8KB-Typen handelt, ist 0x1Fxx eine Byteadresse im Bootbereich am Ende vom Flash und GCC will eine Wortadresse, also ist dann 0x1Fxx/2 richtig.
Hi der GCC arbeitet AFAIK immer mit Byteadressen. Das was man da sieht ist ein rjmp dessen negatives Argument dafür sorgt das es zu einem Unterlauf kommt was der Dissasembler mit dieser riesigen Adresse beantwortet. Was wirklich im Chip passiert ist ein Wrap-Around so das der rcall dann irgendwo am oberen Ende des Flash endet. Ich fürchte aber das sich Kurt irgendwie verrant hat und den Wald vor lauter Bäumen nicht mehr sieht. Desshalb meine Frage an den OP: Was soll das ganze werden? Matthias
Ich steh jetzt vor dem gleichen Problem: #define APP_FLASH_SIZE 0x1800 typedef uint8_t (*CAN_FUNCTION)( CAN_MSG * ); #define rcvCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 2 ))( X ) ) #define sndCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 4 ))( X ) ) sndCan( msg ); daraus entsteht folgendes Listing: d0: d5 db rcall .-2134 ; 0xfffff87c wenn ich nun aber nachrechne ( 0xd0 - 2134 + 0x2000 ) ergibt das nicht APP_FLASH_SIZE + 4 sondern APP_FLASH_SIZE + 4 + 0x78. 0x78 ist zufälliger weise die Größe der .vectors und .init sections. Wie sag' ich dem GCC jetzt das die Adresse eine absolute Adresse ist und nicht eine im .text Segment?
Jörg? Könntest Du vielleicht die Struct of Function Pointers Lösung etwas präzisieren (Beispiel)?
Das Beispiel ist ein wenig komplex, habe ich gerade nicht (getestet) zur Hand. Im Prinzip ungefähr so: statt des normalen Startups setzt du an den Anfang des Bootloaders eine Sprungtabelle:
1 | rjmp bootload ; reset vector |
2 | rjmp thisfunc |
3 | rjmp thatfunc |
4 | rjmp whatever |
Die daraus assemblierte Objektdatei wird beim Linken als erstes angegeben. Für die normale Applikation links du die folgende Datei mit hinzu, um auf diese Sprungtabelle zuzugreifen:
1 | #define bl_offset 0x1800 |
2 | #define sizeof_rjmp 2 |
3 | |
4 | .equ bootload, bl_offset + 0 |
5 | .equ thisfunc, bl_offset + 1 * sizeof_rjmp |
6 | .equ thatfunc, bl_offset + 2 * sizeof_rjmp |
7 | .equ whatever, bl_offset + 3 * sizeof_rjmp |
Wie geschrieben, ist alles ungetestet, aber sollte dir zumindest die Idee bringen.
Im Prinzip benutzt man auf diese Weise ja unbenutzte Bootloader-IR-Einsprünge. Am Schönsten wäre es, wenn man ein Makro ähnlich wie SIGNAL oder INTERRUPT schreiben würde, welches den Vektor automatisch setzt, aber einen normalen Funktionsaufruf statt eines IRs generiert. Auf diese Weise kommt man auch weniger mit vom Bootloader benutzten Interrupts in die Quere. Gruß, Stefan
Nochmal zu Byte- und Wortadressen: Es stimmt schon, dass GCC und insbesondere die Binutils mit Byteadressen rechnen. Die Maschine allerdings sieht das anders und daher muss man höllisch aufpassen wer da grad rechnet und ob er weiss womit er rechnet. GCC erlaubt von Haus aus keine Rechnung mit Codeadressen und kennt auch keine numerischen Konstanten von Codeadressen (mit diesem Problem fing der Thread ja an). Wenn man nun per type-cast eine numerische Konstante in einen Funktionszeiger umwandelt, dann findet keine Umrechnung zwischen Byte- und Wortadresse statt und die angegebene Zahl wird direkt als (Wort-) Adresse verwendet. Im Beispiel am Anfang dieses Threads wird also die Wortadresse 0x1f14 angesprungen. Bei einem Mega8 existiert diese Adresse überhaupt nicht. Korrekt wäre also "... = 0x1f14/2" gewesen. Analog muss auch beim Problem von m4444x in Worten gerechnet werden: #define APP_FLASH_SIZE (0x1800/2) typedef uint8_t (*CAN_FUNCTION)( CAN_MSG * ); #define rcvCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 1 ))( X ) ) #define sndCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 2 ))( X ) )
Kann man so etwas micht über den Linker machen? Also eine Funktion deklarieren (aber nicht definieren) und beim Linken dann die Adresse dieser Funktion per Option setzen. So, oder so ähnlich müsste das gehen.
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.