mikrocontroller.net

Forum: Compiler & IDEs Funktionspointer


Autor: Kurt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tippfehler. Richtig:
typedef unsigned char (*myfcnptr)(unsigned char);
myfcnptr fu_pointer = (myfcnptr)0x1f14;

Autor: Kurt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Kurt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann wirf das C-Lehrbuch weg.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ü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.

Autor: Kurt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: m4444x (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: m4444x (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg? Könntest Du vielleicht die Struct of Function Pointers Lösung 
etwas präzisieren (Beispiel)?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
rjmp   bootload  ; reset vector
rjmp   thisfunc
rjmp   thatfunc
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:
#define bl_offset 0x1800
#define sizeof_rjmp 2

.equ bootload,  bl_offset + 0
.equ thisfunc,  bl_offset + 1 * sizeof_rjmp
.equ thatfunc,  bl_offset + 2 * sizeof_rjmp
.equ whatever,  bl_offset + 3 * sizeof_rjmp

Wie geschrieben, ist alles ungetestet, aber sollte dir zumindest die
Idee bringen.

Autor: _sk_ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 ) )

Autor: Unbekannter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.