Hallo
Ich bin mittlerweile bei der Realisierung meines LCD- Menüs, schon so
weit, dass ich das Einstellmenü entwerfe.
Hier arbeite ich mit einem großen Struct, das im EEPROM abgelegt wird,
und wo die Daten der Menüeinstellungen gespeichert sind. Da das EEPROM
aber nur eine begrenzte Anzahl an Schreibzugriffen pro Speicherzelle
zulässt, kopiere ich dieses Struct beim Programmstart in seine exakte
Entsprechung im SRAM, also in eine globale Variable mit der Funktion
eeprom_read_block. Da das Menü auch Info- elemente enthält, die
teilweise im sekundentakt aktualisiert werden müssen, wäre es ungünstig,
jede Sekunde diese Daten in das EEPROM zu schreiben. Diese Daten werden
dann vorerst ins RAM geschrieben. Nach einer einstellbaren Zeit (ca.
5min) werden diese Daten dann ins EEPROM gesichert. So bleibt die Anzahl
der Schreibzugriffe auf das EEPROM begrenzt.
Doch damit ich nicht zuweit abschreife hier mein Problem
Das ist ein Auszug aus der Routine für das Einstellungsmenü. mem ist ein
void* Pointer auf den entsprechenden Datenwert des Structs im SRAM.
Das Auslesen der Daten (mode_display) funktioniert auch einwandfrei.
Möchte ich nun aber den Datenwert erhöhen oder erniedrigen und dann
wieder an die Pointer Position zurückspeichern funktioniert das nicht.
Rufe ich dann nämlich die Funktion rekursiv wieder auf nur jetzt mit dem
modus mode_display, dann wird am Display wieder der ursprüngliche
unveränderte Wert angezeigt.
1
ptr=(uint32_t*)(&u32Value);
Diese Codezeile hab ich schon mehrfach überprüft, hab aber meiner
Meinung nach keine Widersprüchlichkeiten entdeckt.
Weis vll. jemand von euch Rat?
Schon mal vielen Dank dafür
MfG
gcc anfänger schrieb:
1) ist der rekrusive AUfruf mehr als zweifelhaft. Die verfranst dich da
unter Umständen in immer tiefere Aufrufhierarchien
2) Kein Wunder.
Du änderst ja nicht den Wert in deiner Struktur selber, sondern eine
Kopie davon, die du funktionslokal in der Variablen u32Value hältst.
Die kannst du ändern bis du schwarz wirst, deswegen ändert sich
ja in der Struktur nichts.
> Da das EEPROM> aber nur eine begrenzte Anzahl an Schreibzugriffen pro Speicherzelle> zulässt, kopiere ich dieses Struct beim Programmstart in seine exakte> Entsprechung im SRAM, also in eine globale Variable mit der Funktion> eeprom_read_block. Da das Menü auch Info- elemente enthält, die> teilweise im sekundentakt aktualisiert werden müssen, wäre es ungünstig,> jede Sekunde diese Daten in das EEPROM zu schreiben.
Das klingt nach einem Designfehler.
Wozu Info-Elemente ins EEPROM übertragen? Kostet nur Speicherplatz.
Erstmal eine Nebenbemerkung:
Wenn ptr tatsächlich ein (uint32_t*) sein soll, dann deklariere ihn
nicht als (void *). Ich sehe keinen Grund dafür.
Aber der Hauptpunkt ist der:
Mit
1
...
2
void*ptr...
3
...
4
uint32_tu32Value;
5
...
6
ptr=(uint32_t*)(&u32Value);
7
...
veränderst Du den Zeiger selbst aber nicht den Inhalt, nicht das worauf
er zeigt.
Wenn Du den Inhalt Verändern willst:
Grrrr schrieb:> Erstmal eine Nebenbemerkung:> Wenn ptr tatsächlich ein (uint32_t*) sein soll, dann deklariere ihn> nicht als (void *). Ich sehe keinen Grund dafür.
er wird neben dt_time noch andere Datentypen haben, die dann eben nicht
uint32_t sind
Karl heinz Buchegger schrieb:> er wird neben dt_time noch andere Datentypen haben, die dann eben nicht> uint32_t sind
Wahrscheinlich. Habe auch erst hinterher gesehen, das das eigentlich nur
als Code-"Ausschnitt" Sinn macht.
Habe bis heute morgen gebastelt und wollte beim Kaffee mal reingucken.
Wohl noch nicht genug Kaffee. ;-)
Wobei ich noch nicht wirklich davon überzeugt bin, dass ein void-Pointer
im Speziellen bzw. ein Pointer überhaupt an dieser Stelle eine gute Idee
ist.
Oftmals ist es einfacher, an dieser Stelle im Menü ganz einfach einen
uint16_t (oder einen uint32_t) vorzusehen, anstatt sich da einen
zusätzlichen Pointer zu installieren, der dann auf massgeschneiderte
Datenfelder zeigt. Die zusätzlichen Pointer kosten mehr Speicherplatz,
als wie wenn man einen uint16_t ganz einfach in einem uint32_t speichert
und ein paar Bytes nicht benutzt.
Will man keinen uint32_t als generelle Speicherfläche nutzen, dann geht
man auf einen uint16_t als größtes gemeinsames Vielfaches und sieht zu,
dass man größere Daten anders speichert. Man muss ja nicht Uhrzeit und
Datum in einem einzigen uint32_t ablegen, sondern kann ja auch Zeit und
Datum getrennt in jeweils einen eigenen uint16_t ablegen.
universeller ist es natürlich mit dem mem-Pointer.
Nur kann man sich auf einem µC zuviel Universalität oftmals aus
Resourcengründen nicht leisten.
Hallo
Vielen dank für die vielen Antworten in so kurzer Zeit!!
Karl heinz Buchegger schrieb:> ist der rekrusive AUfruf mehr als zweifelhaft. Die verfranst dich da> in immer tiefere Aufrufhierarchien, die dann niemals wieder> aufgelöst werden
Die Rekursion ist hier nur als schnelle Testmethode zu verstehen. Ich
weis das hab ich vergessen zu erwähnen. In der Endversion wird die
Funktion von einer anderen Funktion aufgerufen.
Karl heinz Buchegger schrieb:> Das klingt nach einem Designfehler.> Wozu Info-Elemente ins EEPROM übertragen? Kostet nur Speicherplatz.
Sehe ich nicht so. Es geht darum Drehzahlwerte und Geschwindigkeiten zu
erfassen und dann noch aus diesen Werten z.B. Durchschnittswerte
Höchstwerte Mindestwerte usw. zu bilden.
Grrrr schrieb:> Wahrscheinlich. Habe auch erst hinterher gesehen, das das eigentlich nur> als Code-"Ausschnitt" Sinn macht.> Habe bis heute morgen gebastelt und wollte beim Kaffee mal reingucken.> Wohl noch nicht genug Kaffee. ;-)
Hab bis zu 12 verschiedene Datentypen, mit denen ich unterschiedlich
verfahren muss.
gcc anfänger schrieb:> Grrrr schrieb:>> Wahrscheinlich. Habe auch erst hinterher gesehen, das das eigentlich nur>> als Code-"Ausschnitt" Sinn macht.>> Habe bis heute morgen gebastelt und wollte beim Kaffee mal reingucken.>> Wohl noch nicht genug Kaffee. ;-)>> Hab bis zu 12 verschiedene Datentypen, mit denen ich unterschiedlich> verfahren muss.
Is ja gut.
Karl heinz Buchegger schrieb:> Oftmals ist es einfacher, an dieser Stelle im Menü ganz einfach einen> uint16_t (oder einen uint32_t) vorzusehen, anstatt sich da einen> zusätzlichen Pointer zu installieren, der dann auf massgeschneiderte> Datenfelder zeigt. Die zusätzlichen Pointer kosten mehr Speicherplatz,> als wie wenn man einen uint16_t ganz einfach in einem uint32_t speichert> und ein paar Bytes nicht benutzt.> Will man keinen uint32_t als generelle Speicherfläche nutzen, dann geht> man auf einen uint16_t als größtes gemeinsames Vielfaches und sieht zu,> dass man größere Daten anders speichert. Man muss ja nicht Uhrzeit und> Datum in einem einzigen uint32_t ablegen, sondern kann ja auch Zeit und> Datum getrennt in jeweils einen eigenen uint16_t ablegen.>> universeller ist es natürlich mit dem mem-Pointer.> Nur kann man sich auf einem µC zuviel Universalität oftmals aus> Resourcengründen nicht leisten.
Hab noch ca 50% Platz auf meinem MEGA32. Und die anderen 50% werden
nicht nur von der Menüstruktur belegt :) Die braucht so ca. 20%. Sie Ist
aber auch nicht unbedingt die kleinste.
gcc anfänger schrieb:> Sehe ich nicht so. Es geht darum Drehzahlwerte und Geschwindigkeiten zu> erfassen und dann noch aus diesen Werten z.B. Durchschnittswerte> Höchstwerte Mindestwerte usw. zu bilden.
und warum müssen die ins EEPROM übertragen werden?
(Ein Maximalwert wird zwar sekündlich aktualisiert, sprich
nachgerechnet, abr nach kurzer Zeit hat der doch seinen Höchststand
erreicht und wird doch meistens nur noch ganz selten höher, wenn er
schon im EEPROM überdauern muss)
Aber ist egal. Das musst sowieso du entscheiden bzw. so machen wie du es
für richtig hältst.
Karl heinz Buchegger schrieb:> und warum müssen die ins EEPROM übertragen werden?
Ganz einfach.
Die Drehzahlwerte werden hin hintergrund von der Hardware+ AVR
mitgeloggt. Es geht um ein Hobbyprojekt einer Motorrad Cockpitanzeige.
Der Benutzer sieht am Display nur einen Balken der die Drehzahl anzeigt,
da dies der wichtigste Wert ist. Wenn der Benutzer nun genaueres über
die während der Fahrt gesammelten Daten wissen will, dann geht er ins
Menü und die gesammelten Daten wie Motorlaufzeit, max. Drehzahl,
durchschnittliche Drehzahl etc. werden ihm in den verschiedenen
auswählbaren Menüpunkten angezeigt. Damit ich diese Werte eben auch
nachdem der Strom abgeschaltet wurde anzeigen kann, sozusagen als
Logbuch, müssen die ins EEPROM.
MfG
// bei dt_time wissen wir, dass der Pointer auf einen uint32_t zeigt
13
uint32_t*dtTimePtr=(uint32_t*)ptr;
14
15
switch(modus)
16
{
17
casemode_display:
18
GLCD_Int(*dtTimePtr,0);
19
break;
20
21
casemode_incdec:
22
*dtTimePtr+=step;
23
GLCD_Int(*dtTimePtr,0);
24
// oder: einstellen( value, mode_display );
25
break;
26
}
27
}
28
break;
29
}
30
}
Funktioniert einwandfrei! Danke!
Jetzt hab ich nur noch eine Frage:
Gibt es auch eine Möglichkeit die gesamte Wertzuweisung in einer Zeile
unterzubringen. Also ich meine, dass man nicht zuerst einen extra
uint32_t* Pointer deklarieren muss, den man dann in der Funktion dann
verwendet. Es wäre nämlich eine elegante Lösung wenn das ginge.
MfG
gcc anfänger schrieb:> Gibt es auch eine Möglichkeit die gesamte Wertzuweisung in einer Zeile> unterzubringen.
Natürlich.
> Also ich meine, dass man nicht zuerst einen extra> uint32_t* Pointer deklarieren muss, den man dann in der Funktion dann> verwendet.
Vertrau deinem Compiler. Nur weil du eine Variable schreibst, heist das
noch lange nicht, das der Optimizer sie nicht wieder rauswirft, wenn er
draufkommt, das er sie nicht braucht
> Es wäre nämlich eine elegante Lösung wenn das ginge.
Das wäre nicht wirklich elegant.
Du würdest den einen Cast über den Code verstreuen. Und fehleranfälliger
gegenüber Änderungen wirds ausserdem.
1
casedt_time:
2
{
3
switch(modus)
4
{
5
casemode_display:
6
GLCD_Int(*(uint32_t*)ptr,0);
7
break;
8
9
casemode_incdec:
10
*(uint32_t*)ptr+=step;
11
GLCD_Int(*(uint32_t*)ptr,0);
12
// oder: einstellen( value, mode_display );
13
break;
14
}
15
}
Aber wie gesagt: Vertrau deinem Compiler.
Der hat schon langst geschnallt, dass dtTimePtr im Grunde dasselbe wie
ptr ist und sich nur im Datentyp unterscheidet. Da sich das auch nicht
ändert kann er in diesem Codeabschnitt intern so tun als ob ptr ein
uint32_t Pointer gewesen wäre und den direkt benutzen anstelle einer
zusätzlichen Pointervariablen.