Moin moin! In C ist es ja möglich Funktionen über Pointer aufzurufen. Ist dies auch auf einem AVR möglich? (Genauer: kann ich diese Adresse irgendwie in den Programmspeicher ablegen?) Mein konkretes Problem ist folgendes: Ich habe eine Funktion die ein Menü erzeugt - nun möchte ich beim anwählen eines Menüpunkts bestimmte Funktionen ausführen - Momentan mach ich das indem ich ne dicke Case Abfrage zu jedem Menüpunkt mache... Schöner wär natürlich wenn ich ein Zeigerarray in dem Programmspeicher ablegen könnte... Danke schonmal
Hallo, fehlt mir wie üblich die C-Kenntnis. Muß aber gehen, da der AVR IJMP und ICALL kann. in menunr ist die Nummer des Menüs (array-index) .cseg ldi XH,high(2*menutab) ldi XL,low(2*menutab) lsl menunr ; *2 wegen Wordlänge der Adresse add XL,menunr clr menunr ; um an eine 0 zu kommen... adc XH,menunr ; jetzt zeigt x auf die gewünschte Adresse in der Tabelle lpm ZH,X+ ; jetzt die Adresse nach Z lpm ZL,X ijmp ; und zur Routine springen springen oder icall ; wenn es als Subroutine aufgerufen werden soll .dseg menuetab: .dw funktion1 .dw funktion2 .dw funktion3 usw. wie das in C geht? noch keine Ahnung... ;) Gruß aus Berlin Micahel
Zeigerarray als const geht, aber was ich bisher gesehen war das der Compiler LDS oder LDD Befehle erzeugt, die Sprungziele also aus dem RAM holt (das vorher mit den Konstanten initialisiert wird). Wenn die Sprungtabelle im PROGMEM liegt wird der gleiche Code erzeugt aber die RAM Werte nicht richtig geladen - der Sprung geht an die Adresse 0. Hier ist ein Beispiel wie ich eine Zuordnung von Tasten zu Funktionen realisiert habe:
1 | void Btn0Click(void) |
2 | {
|
3 | PORTC &= ~_BV(0); |
4 | pos=middlePos; |
5 | }
|
6 | |
7 | void Btn1Click(void) |
8 | {
|
9 | PORTC &= ~_BV(0); |
10 | pos+=deltaPos; |
11 | }
|
12 | |
13 | void Btn2Click(void) |
14 | {
|
15 | PORTC |= _BV(0); |
16 | pos-=deltaPos; |
17 | }
|
18 | |
19 | typedef struct |
20 | {
|
21 | short bitnr; |
22 | void (*fnAction)(void); |
23 | } ACTION; |
24 | |
25 | const ACTION ActionTab[] = |
26 | {
|
27 | {2, Btn0Click}, |
28 | {3, Btn1Click}, |
29 | {5, Btn2Click}, |
30 | {0, NULL} |
31 | };
|
32 | |
33 | void CheckIO(void) |
34 | {
|
35 | ACTION* p; |
36 | p=ActionTab; |
37 | while(p->fnAction != NULL) |
38 | {
|
39 | if ((PINB ^ PB_old) & PB_old & _BV(p->bitnr)) |
40 | p->fnAction(); |
41 | p++; |
42 | }
|
43 | PB_old=PINB; |
44 | }
|
Und wenn das Ganze ins Flash bzw. PROGMEM soll, dann kann man nicht direkt über den pointer lesen, sondern muß pgm_read_byte bzw. pgm_read_word bemühen und den Funktionspointer lokal umkopieren: const ACTION ActionTab[] PROGMEM = { {2, Btn0Click}, {3, Btn1Click}, {5, Btn2Click}, {0, NULL} }; void CheckIO(void) { const ACTION* p; p=ActionTab; void (*fp)(void); while(fp = (void *) pgm_read_word(&p->fnAction) != NULL) { if ((PINB ^ PB_old) & PB_old & _BV(pgm_read_byte(&p->bitnr)) fp(); p++; } PB_old=PINB; }
Wirf mal einen kleinen Blick auf folgenden Beispiel: So ergeben sich Function-Calls mit ICALL(Z) statt mit CALL: __________________________________________________________ #include <avr/io.h> void function_A(void) { unsigned char i=4; while(i--) { asm volatile("nop"); } } // get address pointer of function_A void (*function_B)(void) = (void *)(function_A); // define a function-pointer to a fixed FLASH address void (*function_C)(void) = (void *)(0x0F00); // WORD-Address !!! int main(void) { function_A(); // => CALL function_B(); // => ICALL(Z=function_A) function_C(); // => ICALL(Z=0x0F00) while(1); } __________________________________________________________ Auf diese Art kannst Du natürlich auch beliebeige Struks aufbauen. Die Pointer werden allerdings zur Laufzeit ins RAM-kopiert!
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.