PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Zeiger auf eine Funktion im Flash.



izaseba
22.09.2006, 21:08
Hallo,
irgendwie verstehe ich das jetzt mit den Pointern auf Flashspeicher nicht :-s

Als Beispiel hier ein Miniprogramm in C:



#include<stdio.h>

void zeige(void) {

printf("Hallo, hier bin ich\n");
}

void zeige2(void) {
printf("und hier noch einmal\n");
}

int main(void) {
typedef void (*funktion) (void);

funktion feld[] = {&zeige,&zeige2};

feld[0]();
feld[1]();
return 0;
}


Es hat nicht viel Sinn, soll einfach nur deutlich machen, wo mein Problem liegt.

Auf dem PC klappt es ja Prima, auf dem AVR hol ich mir ja irgendwelche RAM Adressen #-o .

Ich weiß daß es an der Harvard Architektur und an den getrennten RAM/FLASH/EEPROM Adressräumen liegt, es ist auch kein Problem Daten zu lesen, nur halt mit Zeigern krieg ich das nicht hin ](*,)
Aus der Doku zu avr-gcc werde ich auch nicht schlauer...


Könnte mir mal jemand unter die Arme greifen und mir mal sagen wie ich das Miniprogramm in AVR implementiere ?

Gruß Sebastian

JonnyP
23.09.2006, 12:13
Die Daten im Flash sind 16 Bit, ein Character aber nur 8 Bit. Du mußt die Pointer adresse * 2 nehmen.

izaseba
23.09.2006, 19:35
Hallo JonnyP,

würden wir hier von AVR Pointern sprechen, würde ich Dir recht geben, es geht sich aber um die C Zeiger und die haben damit nichts zu tun.

Trotzdem danke für die Antwort, ich komme schon langsam selber auf die Lösung :-)

PicNick
23.09.2006, 20:00
Ich hab da ein beispiel, da läuft das so:


void HeartbtAct(UNIT* pUnit, unsigned char Cmd, unsigned short Param)
{
}
// ---------------------------------------------------
UNIT* HeartbtBuild(UNIT* pUnit, unsigned char UnitClass, unsigned char UnitIdent)
{
pUnit->iVect = (int)HeartbtAct; //SETZEN VECTOR
}

.....

später dann:

union {void (*vVec)(UNIT* pUnit, unsigned char Cmd, unsigned short Param);
int iVec;
} Vec;

UNIT* pUnit = (UNIT*)pMsg->iUnit;
Vec.iVec = pUnit->iVect; // HOLEN VECTOR

(*Vec.vVec)(pUnit, pMsg->bCommand, pMsg->wParam);
// und aufrufen



Sieht wüst aus, aber du siehst, eigentlich brauch ich da nix tricksen

Ich hoff', Du kannst Dir das ausdeutschen ?

SprinterSB
24.09.2006, 16:49
typedef void (*function_t) (void);

extern void zeige (void);
extern void zeige2 (void);

function_t feld[] = {zeige, zeige2};

void caller (int addr, function_t zeige3)
{

feld[0]();
feld[1]();
zeige3();
((void(*)(void)) addr) ();
}




Könnte mir mal jemand unter die Arme greifen und mir mal sagen wie ich das Miniprogramm in AVR implementiere?

Gruß Sebastian

Geht genauso.

Obiger Code assemblier avr-gcc so:

.file "foo.c"
.arch atmega8
__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__tmp_reg__ = 0
__zero_reg__ = 1
.global __do_copy_data
.global __do_clear_bss
.global feld
.data
.type feld, @object
.size feld, 4
feld:
.word pm(zeige)
.word pm(zeige2)
.text
.global caller
.type caller, @function
caller:
/* prologue: frame size=0 */
push r14
push r15
push r16
push r17
/* prologue end (size=4) */
movw r14,r24
movw r16,r22
lds r30,feld
lds r31,(feld)+1
icall
lds r30,feld+2
lds r31,(feld+2)+1
icall
movw r30,r16
icall
movw r30,r14
icall
/* epilogue: frame size=0 */
pop r17
pop r16
pop r15
pop r14
ret
/* epilogue end (size=5) */
/* function caller size 25 (16) */
.size caller, .-caller
/* File "adc.c": code 25 = 0x0019 ( 16), prologues 4, epilogues 5 */

izaseba
24.09.2006, 20:12
Hallo,
Danke erstmal.

Ich glaube, ich hab mich falsch ausgedrückt.

Sprinter, bei Deinem Beispiel klappt das, weil der Compiler die Adressen direkt einsetzen kann ? Kann das sein ?

Ich meine Hier:


feld[0]();
feld[1]();

Die Adressen stehen zu Kompilierzeit fest und müßen nicht im laufendem Betrieb geholt werden.
Ich glaube, wenn man eine Variable bei dem Arrays verwenden würde würde es nicht klappen ?

Aber nochmal ein Beispiel:

Ich habe ein paar Strings in Flash gelegt:


const char menuhauptstr[] PROGMEM = "Hauptmenu";
const char menukontstr[] PROGMEM = "Kontrast";
const char menuhellstr[] PROGMEM = "Helligkeit";
const char menufrage[] PROGMEM = "Speichern ? ";


dann eine Struktur :


struct MENU {
const char *Menu_Name;
uint8_t Entrie_up;
uint8_t Entrie_down;
uint8_t Entrie_right;
uint8_t Entrie_left;
void(*Menu_Funkt)(void);
};


Zum Schluß ein Array mit diesen Strukturen im Flash:


const struct MENU menu[] PROGMEM = {
{menuhauptstr,3,1,10,15,Draw_Main},
{menuhellstr,0,2,10,15,Draw_Hell},
{menukontstr,1,3,10,15,NULL},
{menufrage,2,0,1,0,NULL}
};


Will ich jetzt z.B. mit:


menu[i].Menu_Funkt();


Meine Funktion aufrufen, klappt das nicht.
Ich habe gelesen, daß man sich die Adresse zuerst mit (const char *)pgm_read_word(menu) holen muß, nur irgendwie krieg ich das nicht hin :-s

Ich hab es jetzt so gelöst, daß ich mein Array in RAM gelegt habe, dann geht alles wunderbar.

Ich glaub, entweder hab ich den Kapitel über Pointer bei Kernighan u. Ritchie oder die Harvald Architektur nicht verstanden.

Gruß Sebastian

SprinterSB
24.09.2006, 20:32
Dei Adresse muss nicht bekannt sein. Zwei Adressen in meinem Beispiel ergeben sich werst zur Laufzeit, andere zur Linkzeit (extern...). Woher die Adressen sind ist ja egal.



function_t func = (function_t) pgm_read_word (& menu[i].Menu_Funkt);
func();

oder


((function_t) pgm_read_word (& menu[i].Menu_Funkt))();


Das Problem mit der Harvard-Architektur und gcc ist, daß man Flash-Adressen (dito EEPROM) als Attribut angibt, und nicht wie es sein müsste als Qualifier (so wie const, volatile, unsigned, ...). Das macht vielerorts Probleme. U.a weiß gcc beim zugriff nicht, wie er darauf zugreifen soll. Etwa wenn ein Zeiger in eine Funktion run kommt: ist es RAM-Adresse? EEPROM? oder Flash? Deshalb muss man umständlich mit den pgm_read/write Zeug arbeiten.

Für andere Architekturen (linearere Adressraum) hätte man einfach sagen können
#define pgm_read_byte(addr) (*((uint8_t*) addr))
bzw
#define pgm_read_byte(addr) (* addr)
wenn addr ein Zeiger aud uint8_t ist.

Wenn du eine Struktur im Flash hast musst du eben die Komponenten per pgm_xxx holen/schreiben:

x = a.b; --> pgm_read_block (&x, &a.b, sizeof (a.b));
x = a->b; --> pgm_read_block (&x, &a->b, sizeof (a->b));

SprinterSB
24.09.2006, 20:43
Beachte auch, daß du für die Strings zwei Induirektionen via pgm_xxx drinne hast (die zeite wohl in deiner out-Routine, die nen Zeiger ins Flash bekommt.


void foo (uint8_t i)
{
((void(*)(void)) pgm_read_word (& menu[i].Menu_Funkt)) ();
}

ergibt übrigens


foo:
mov r30,r24
clr r31
ldi r24,3
1: lsl r30
rol r31
dec r24
brne 1b
subi r30,lo8(-(menu+6))
sbci r31,hi8(-(menu+6))
/* #APP */
lpm r24, Z+
lpm r25, Z
/* #NOAPP */
movw r30,r24
icall
ret

izaseba
24.09.2006, 20:49
Danke,
ich glaub es hat klick gemacht


function_t func = (function_t) pgm_read_word (& menu[i].Menu_Funkt);
func();


Ich habe hier immer auf (const char) gecastet warum auf immer :-s , jetzt hab ich wieder was zum probieren :-)

Gruß Sebastian

SprinterSB
25.09.2006, 17:38
Ich hab den entsprechenden Abschnitt im C-Tutorial mal ausgefüllt. Hätte nicht gedacht, daß jemand Funktionszeiger benutzt ;-)

https://www.roboternetz.de/wissen/index.php/C-Tutorial#Zeiger_auf_Funktionen

Ein Beispiel haz's auch in

https://www.roboternetz.de/wissen/index.php/Avr-gcc#Sprungtabelle

izaseba
25.09.2006, 19:59
Hätte nicht gedacht, daß jemand Funktionszeiger benutzt

Momentmal, ist es so exotisch ?

Funktionszeiger sind ganz fein bei z.B. einer Menuprogrammierung :-)

Ich hab mir ein Wolf gesucht und kaum was zum Thema gefunden :-(
Klar für C an sich steht es in jedem besserem Buch, man kann es aber kaum mit den pgm_read_xxx vergleichen.

Dank Deiner Hilfe klappt es mittlerweile sehr gut \:D/
Danke, daß Du es ins Wiki aufgenommen hast.

Es macht sich sicher sehr gut als Referenz und als Verweis für manche Fragesteller ...

Gruß Sebastian

ManniMammut
25.09.2006, 20:26
Funktionszeiger sind ganz fein bei z.B. einer Menuprogrammierung :-)


Rischtisch! Danke Sprinter :D

Felix G
25.09.2006, 21:38
Hätte nicht gedacht, daß jemand Funktionszeiger benutzt ;-)Also ich liebe Funktionszeiger :mrgreen:

Die kann man ja nicht nur für Menüs verwenden...
Ich z.B. nutze sie seit einiger Zeit fürs"Multitasking", wodurch meine Programme übersichtlicher und flexibler geworden sind.

Falls es Jemanden interessiert:
man nehme ein Array mit Funktionszeigern, und schreibe sämtliche aufzurufenden Funktionen da rein (von Hand in einer .h Datei und/oder dynamisch während der Laufzeit wobei letzteres etwas mehr Programmieraufwand ist). Im Hauptprogramm muss man dann nurnoch in einer Endlosschleife die Funktionen in diesem Array aufrufen.

Bei der dynamischen Variante kann man entweder das Array entsprechend groß wählen, oder alternativ eine verkettete Liste verwenden.


Gruß,
Felix

PicNick
26.09.2006, 07:38
Hätte nicht gedacht, daß jemand Funktionszeiger benutzt
Na, sag Du mir ?
Ein paar Beispiele bringt dir eh' der dreiäugige

SprinterSB
26.09.2006, 10:15
*g* na wenn hier so viele Funktionszeiger-Begeisterte sind, will ich mal ein kleines Quiz anhängen:

Was für eine Art von Objekt wird in folgender C-Zeile definiert?


int*(*(*foo)(int*(*[])(int*,int*)))(int*,int*);