Hallo zusammen,
Ich stehe mal wieder auf dem Schlauch!
Ich möchte ein kleines Menü auf einem 4*20 LCD programmieren mit einer
C-Strukur. Habe mich an diesem Beispiel gehalten, wie es hier auch schon
öfter diskutiert wurde:
http://projects.higaski.at/videotutorials/videotutorial1_lcd_menus/videotutorial1_lcd_menus.html
Um das zu verstehen und das zu nutzen habe ich das ganze nun mit der
MikroC IDE geschrieben und versucht, die Feinheiten anzupassen,
wie z.B. die unterschiedlichen Routinenbenamungen MPLAB/MikroC.
Soweit so gut, aber auf dem Ziel - PIC läuft es nicht.
Jetzt beim Fehlersuchen fehlt mir das Grundverständnis von C-Strukturen.
Hier mal der Code und meine Fragen - kann mir mal bitte jemand einen
Schupps in die richtige Richtung geben?
Hier wird die const Struktur definiert mit dem Namen MenuEntry:
1
typedefconststructMenuStructure// Struktur defnieren
sowie die Arrays für das Menü, welche dann die Eigenschaften
beschreiben:
1
MenuEntrymenu[]=
2
{
3
{menu_000,11,0,0,0,0},// 0
4
{menu_001,11,1,2,12,0},// 1
5
{menu_002,11,1,3,21,0},// 2
6
};
Dann gibt es eine Routine, die die Zeichen aus dem ROM(?) auf dem
Display darstellen soll, anhand von Tastendrücken.
Das mit der Menübedienung ist jetzt erstmal noch nicht das Problem,
sondern die Funktion und der Ablauf des gesamten Konstrukts mit der
Pointerübergabe an sich, da bin ich mir unklar.
Hier die Routine eingekürzt ohne Fehlerbehandlung:
lcd_offset=strlen(*lcd_zeichen);// Länge der Zeichenkette
7
8
for(lcd_i=lcd_offset;lcd_i;lcd_i--)
9
{
10
Lcd_Chr_Cp(*lcd_zeichen);
11
lcd_zeichen++;
12
}
13
}
Diese wird dann aufgerufen in der MAIN mit:
1
LCDWriteStringROM(0,0,*menu[1].text,0);
..... SOOOOOO!
Jetzt meine Kernfragen:
- Kann mir mal "einfach" jemand erklären, wie die
Pointer - Parameterübergabe aus der MAIN (*menu[1].text)
in die Struktur funktioniert und wie schlussendlich die Daten fließen?
- Wie funkioniert es mit dem const char *text;?
- Woher weiß das System, welchen Menüeintrag es auswählen soll?
- Wird hiermit -> void ( *fp )( void );
dann eine Funktion aufgerufen, die den Namen aus dem Menü haben muss?
- Ziel wäre es erstmal, provisorisch in der MAIN diese Funktion
aufzurufen
LCDWriteStringROM(0,0,*menu[1].text,0);
Was passiert dann?
Welcher Text sollte dann auf dem Display erscheinen;
meiner Meinung nach doch " Options1" oder?
- Kann mein Compiler vielleicht keine Const Strukturen, compiliert
aber ohne meckern.
- liegen die Const´s im ROM oder RAM?
Mit der Bitte um Einnordung ;-)
Display, Taktfrequenzen, PIC, Flashen usw. können wir alles
ausschließen, funktioniert!
Quelle: siehe o.g. Link
Gruß Calle
C. L. schrieb:> Diese wird dann aufgerufen in der MAIN mit:>
1
>LCDWriteStringROM(0,0,*menu[1].text,0);
2
>
>
Der Stern muss weg. Der hat da nichts zu suchen.
menu[1].text ist bereits ein Pointer.
> Jetzt meine Kernfragen:> - Kann mir mal "einfach" jemand erklären, wie die> Pointer - Parameterübergabe aus der MAIN (*menu[1].text)> in die Struktur funktioniert und wie schlussendlich die Daten fließen?
Wenn du dir den Stern wegdenkst, hast du dann immer noch Probleme?
>> - Wie funkioniert es mit dem const char *text;?
Ist ein Pointer wie jeder andere. Das const besagt lediglich, dass die
Funktion nicht verusuchen wird das, worauf der Pointer zeigt, zu
verändern. Vor Einführung des const in die Sprache war das eine reine
Absprache unter Entwicklern. Nach Einführung des const in die Sprache,
ermöglichte dieses const die Einhaltung dieser Absprache auch formal zu
prüfen. -> Mehr Sicherheit, dass sich Funktkionen auch tatsächlich an
Zusagen halten, die sie in ihren Argumentlisten versprechen.
> - Woher weiß das System, welchen Menüeintrag es auswählen soll?
steht doch da: menu[1]
> - Wird hiermit -> void ( *fp )( void );> dann eine Funktion aufgerufen, die den Namen aus dem Menü haben muss?
Ja. Das ist ein Funktionspointer.
> - Ziel wäre es erstmal, provisorisch in der MAIN diese Funktion> aufzurufen> LCDWriteStringROM(0,0,*menu[1].text,0);
Stern weg.
> Was passiert dann?
aus dem Array menu
1
menu
wird das Array Element mit dem Index 1 ausgewählt
1
menu[1]
dieses Array Element ist aber ein Struktur Objekt, laut Definition. Es
hat also mehrere Member. Unter anderem einen Member, der mit text
bezeichnet ist. Dieser Member ist ein Pointer auf einen Text.
Also wird mit
1
menu[1].text
der Inhalt dieses Pointers (die Adresse, unter der der Text zu finden
ist) ausgelesen und diese Adresse an die Funktion übergeben, die dann
ihr Ding macht.
> Welcher Text sollte dann auf dem Display erscheinen;> meiner Meinung nach doch " Options1" oder?
Ganz genau
> - Kann mein Compiler vielleicht keine Const Strukturen, compiliert> aber ohne meckern.
Unwahrscheinlich. Wenn der Compiler mit const nichts anfangen könnte
(weil zu alt), dann würde es eine Fehlermeldung geben.
> - liegen die Const´s im ROM oder RAM?
Kommt auf den Compiler an.
Die Sprache C hat dazu nicht viel zu sagen. Das const bedeutet erst mal
nur, dass du als Programmierer festlegst, dass sich dieser Wert niemals
ändern wird. Bzw. in einer Funktionsschnittstelle eben, dass die
Funktion auch nicht versuchen wird, ihn zu ändern.
Das ist erst mal die Grundvoraussetzung, das etwas ins ROM verschoben
werden kann. Die Sprachdefinition hat dazu allerdings nichts zu sagen,
die kennt kein RAM oder ROM, sondern für die gibt es einfach nur
Speicher.
Einige Compiler treiben das allerdings soweit, dass sie alles was const
markiert ist, ins ROM verschieben. Wenn dann die Zugriffe ins ROM auch
noch anders compiliert werden müssen, als die ins RAM, ist es
lebenswichtig, das man hier absolut sauber const-Korrekt arbeitet. Ob
dein Compiler das so macht oder nicht, findest du in der Compilerdoku.
Nimm erst mal den Stern raus. Wundert mich, dass du das so überhaupt
durch den COmpiler gebracht hast. Eigentlich hätte es da einen saftigen
Datentyp Fehler geben müssen.
lcd_offset=strlen(*lcd_zeichen);// Länge der Zeichenkette
ist der Stern auch Quatsch.
Ich kapiers nicht. Wie hast du das alles durch den Compiler gebracht?
Das sind Pointer-Grundlagen-Fehler! Wenn dir dein Compiler das wirklich
durchgehen lässt, dann wechsle den Compiler, denn dann taugt der nichts.
Karl Heinz schrieb:> Ich kapiers nicht. Wie hast du das alles durch den Compiler gebracht?> Das sind Pointer-Grundlagen-Fehler! Wenn dir dein Compiler das wirklich> durchgehen lässt, dann wechsle den Compiler, denn dann taugt der nichts.
Jetzt mal davon abgesehen, dass der ganze Funktionsaufruf zu strlen
Quatsch ist, denn den braucht in Wirklichkeit keiner. Wozu soll es gut
sein, dass strlen erst mal Zeichen zählt indem es den String abklappert,
nur damit du dann in deiner Funktion im Prinzip noch mal dasselbe
machst, nur komplizierter mit einer eigenem Zähler und for-Schleife. Das
'Abklappern' bis zum Stringende kannst du auch selbst machen und gleich
schreiben:
und gut ists. Was anderes macht strlen auch nicht, nur dass es eben
nicht das Zeichen ausgibt, sondern einen Zähler erhöht und die so
ermittelte Zahl an Zeichen vor dem '\0' zurückgibt.
Clemens schrieb:> LCDWriteStringROM(0,0,*menu[1].text,0);> *menu[1].text sieht seltasam aus.> LCDWriteStringROM(0,0,&menu[1].text,0); ??
Auch wenn es manchmal harmlos ist, bei einem Funktionsargument einen &
vor ein Array zu schreiben, weil die Compiler diesen Fehler ignorieren:
Hier wäre das ein fataler Fehler. Die Funktion würde dann nicht die
Adresse des Strings kriegen, sondern die Adresse des Pointers in dem
steht, wo der String zu finden ist.
Ganz abgesehen davon, dass dann auch die Datentypen nicht stimmen
würden. Der Datentyp von &menu[1].text wäre ein 'const char **', während
die Funktion einen 'const char *' haben will.
Es hilft nichts. Die Sache mit Pointern, Sternen und
Adress_of_Operatoren muss man als C Programmierer beherrschen. Sonst
wird das nichts. Sterne und & nach Lust und Laune zu verteilen, bis der
Compiler ruhig ist, führt nur ins Desaster.
Karl Heinz schrieb:> Der Stern muss weg. Der hat da nichts zu suchen.> menu[1].text ist bereits ein Pointer.
Das war erstmal der Schlüssel zu Erfolg!! Jetzt tut sich was auf dem
Zielsystem!
Karl Heinz schrieb:> lcd_offset = strlen(*lcd_zeichen); // Länge der Zeichenkette
Diese Ermittlung macht Probleme, ohne Stern geht nicht, da string
übergeben werden muss. Zudem ist das Ergebnis nicht plausibel.
Habe das einfach gegen Deine Routine ersetzt und siehe da, fluppt
einwandfrei!
Soweit habe ich erstmal das Etappenziel erreicht.
Jetzt werde ich mal sehen, das ich die eingentlichen
Steuerungsfunktionen schreibe. Wenn es wieder hakt, dann melde ich mich
nochmal!
Wie schnell man Hilfe bekommen kann ist echt erstaunlich!
DD = Dickes Dankeschön bis hier her mal an Euch!!
Carsten
C. L. schrieb:>> Karl Heinz schrieb:>> lcd_offset = strlen(*lcd_zeichen); // Länge der Zeichenkette>> Diese Ermittlung macht Probleme, ohne Stern geht nicht, da string> übergeben werden muss.
Sorry. aber das ist Blödsinn.
Wenn lcd_zeichen ein const char* ist, dann hat es bereits den richtigen
Datentyp um direkt an strlen übergeben zu werden.
Was maximal sein kann, das ist diese automatische RAM/ROM Sache, so dass
strlen für einen String im ROM die falsche Funktion ist. Bei PIC bzw.
PIC-Compilern bin ich mir da immer etwas unsicher, ob das auch heute
noch so ist, bzw. bei welchen PIC-Typen bzw. Compilern das noch so ist.
Aber prinzipiell ist
1
voidfoo(constchar*str)
2
{
3
size_zi=strlen(str);
4
}
absolut korrekt. Ein Stern wäre hier völlig falsch.
> da string übergeben werden muss.
Einen string kannst du prinzipiell nicht übergeben. Was du übergeben
kannst, das ist die Startadresse des Strings. Genau die steht aber in
der lokalen Variablen lcd_zeichen drinn. Deren Inhalt (da es ja eine
Pointer-Variable ist) ist genau die Startadresse des Strings, die diese
Funktion von ihrem Aufrufer gekriegt hat. *lcd_Zeichen wäre gar kein
Pointer mehr, sondern der erste Buchstabe des Strings. Und den wirst du
kaum an strlen übergeben können.
... ja, ich habe das mit der Funktion strlen(*lcd_zeichen); jetzt
verworfen und habe mich weiter um die Menüsteuerung gekümmert.
Jetzt läuft fast alles gut und ich kann quasi im Menü hin und her
browsen.
Wenn ich jetzt die aufzurufene Funktion eintrage, meckert der Compiler!
Hier sind die Menüstrings und die Entry´s programmiert:
const char menu_000[] = " [Hauptmenue] "; // 0
const char menu_001[] = " Options1 "; // 1
const char menu_002[] = " Options2 "; // 2
const char menu_003[] = " Options3 "; // 3
const char menu_004[] = " Options4 "; // 4
const char menu_005[] = " Options5 "; // 5
const char menu_006[] = " start "; // 6
const char menu_007[] = " Options6 "; // 7
const char menu_008[] = " Options7 "; // 8
const char menu_009[] = " Options8 "; // 9
const char menu_010[] = " return "; // 10
MenuEntry menu[] =
{
{menu_001, 11, 1, 2, 12, 0},
{menu_002, 11, 1, 3, 21, 0},
{menu_003, 11, 2, 4, 30, 0},
{menu_004, 11, 3, 5, 4, 0},
{menu_005, 11, 4, 6, 5, 0},
{menu_006, 11, 5, 7, 6, start},
{menu_007, 11, 6, 8, 7, 0},
{menu_008, 11, 7, 9, 8, 0},
{menu_009, 11, 8, 10, 9, 0},
{menu_010, 11, 9, 10, 10, 0}, // 10
}
Die Funktionen werden mit dem ENTER Button dann aufgerufen via
if (ImpulsEnter)
{ //wenn 6, dann start aufrufen
if (menu[selected].fp != 0) menu[selected].fp();
selected = menu[selected].enter;
}
Kann mir da noch mal jemand helfen?
Angemeckert wird die Zeile {menu_006, 11, 5, 7, 6, start},
Es muss an dem Aufruf der Routine liegen.
Diese sieht zu Test so aus:
void start (void)
{
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Out(1,1,"Aufgehts !!");
DELAY_MS(500);
}
Mit der Bitte um Hilfe.
Das wäre dann das letzte was ich jetzt noch benötige.
Gruß und schönen Abend!
Carsten
C. L. schrieb:> Wenn ich jetzt die aufzurufene Funktion eintrage, meckert der Compiler!
'meckert' ist keine vernünftige Fehlerbeschreibung.
Da steht sicher nicht
1
Error 345, Line 68: Ich meckere jetzt
sondern eine ordentliche Fehlermeldung, die du auch lesen und verstehen
solltest.
Ich kann mir schon vorstellen, was das Problem ist. Aber du sollst auch
selbst ein wenig nachdenken. Zumal es hier wieder um eine grundsätzliche
Eigenheit der Sprache bzw. des Compilers geht, der sich eben nicht kreuz
und quer aus deinem Programmtext die Einzelteile zusammensucht sondern
den Text von oben nach unten liest. Und das hat nun mal Konsequenzen,
die in dem Satz zusammengefasst werden können "Verwendet werden kann nur
das, was bereits bekannt ist. Insbesondere muss mindestens eine
Deklaration vor einer Verwendung im Quelltext auftauchen"
Hallo,
hier mal die Fehler mit Bild...
...ja, hätte ich auch eher machen können, sry.
Der Compiler sagt "undeclared identifier in expression"
dieser Ausdruck wundert mich trotzdem, weil die Funktion doch existiert.
Karl Heinz schrieb:> Ich kann mir schon vorstellen, was das Problem ist. Aber du sollst auch> selbst ein wenig nachdenken. Zumal es hier wieder um eine grundsätzliche> Eigenheit der Sprache bzw. des Compilers geht, der sich eben nicht kreuz> und quer aus deinem Programmtext die Einzelteile zusammensucht sondern> den Text von oben nach unten liest. Und das hat nun mal Konsequenzen,> die in dem Satz zusammengefasst werden können "Verwendet werden kann nur> das, was bereits bekannt ist. Insbesondere muss mindestens eine> Deklaration vor einer Verwendung im Quelltext auftauchen"
Hmmm. Nachdenken mache ich ja... nur das Durchblicken fehlt noch!
Aus meiner Sicht ist doch alles richtig und in der Compilerdoku steht da
nichts klares oder artverwandtes drin. Im Netz stehen mit anderen
Compilern genau die prinzipgleichen Anweisungen drin. Habe auch schon
alles mögliche probiert, wie Aufruf mit Klammern usw. aber nichts ist
zielführend.
Dein Satz war Gold Wert.
> Zumal es hier wieder um eine grundsätzliche> Eigenheit der Sprache bzw. des Compilers geht, der sich eben nicht kreuz> und quer aus deinem Programmtext die Einzelteile zusammensucht sondern> den Text von oben nach unten liest.
Der Compiler kann nur kennen, was er schon durchforstet hat!
Also die Zielanwendung funktioniert, wie ich es möchte.
Danke Karl Heinz - und auch die anderen ;-}
Carsten
C. L. schrieb:> Der Compiler sagt "undeclared identifier in expression"
Der Compiler sagt "undeclared identifier "start" in expression"
hast du den Parameter start deklariert und definiert?
Ich kaue ja auch nicht gerne Lösungen vor, aber das Erste, was mir hier
einfällt:
Du hast die Funktion unterhalb deiner Main-Funktion definiert, (oder in
welcher auch immer das Menü gesteuert wird) und oberhalb keinen
Funktionsprototypen.
Das kann ich aber nicht genau sagen, da du nur Codeschnipsel zeigst...
Erwarten würde ich sowas:
1
voidstart(void);
2
3
intmain(void)
4
{
5
start();// das funktioniert nur, wenn vorher "start" bekannt ist
Hi!
Lösungen vorkauen ist immer so eine Sache!
Am besten lernt man natürlich, wenn man in die richtige Richtung
gestoßen wird. Aber hinhalten oder den falschen Ton an den Tag zu legen
ist auch nicht korrekt. Gut, das es hier nicht so war.
Zum Thema:
Ja genau das war ja auch das Problem.
Die Routine war zwar definiert aber dem Compiler an der Aufrufposition
noch nicht bekannt.
Die Routine "start" ist zwar vor der MAIN aber hinter der eingentlichen
Menüroutine, die die Funktion start aufruft.
Kurz umgeschoben und es hat auf anhieb funktioniert.
Danke
Carsten