Hallo,
ich bin als blutiger Anfänger auf der Suche nach einem Pointer-Problem.
ICh würde mich freuen, wenn mir jemand weiterhelfen könnte.
Kurz zu meinem Problem.... Mir ist aufgefallen, wenn ich in der Funktion
"sendeJoyTastenToDisplay" die Arraylängen der Variablen "deleteTasten",
"noTasten", "taste1", "taste2", ... "taste56" z.b. mit 40 angebe erhalte
ich ein flakerndes Bild an dem angeschlossenen Display (siehe Bild im
Anhang). Lasse ich die Längen weg funktioniert alles. Mit der Eingabe
der Länge verschiebe ich ja eigentlich nur die Speicherbereiche - was
auf ein Pointer-Problem deutet. Trotz dieser schönen Erkentnisse kann
ich mir auf das Ganze leider keinen Reim bilden.
Ich hoffe ihr könnt mir weiterhelfen
Vielen Dank!
pal ... schrieb:> Anhang). Lasse ich die Längen weg funktioniert alles. Mit der Eingabe> der Länge verschiebe ich ja eigentlich nur die Speicherbereiche
mit der Angabe der Länge verbrauchst du vor allen Dingen erst mal
unendlich viel Speicher für nichts.
Sicher dass du nicht den verfügbaren Speicher überläufst?
* Lass den Compiler die Stringlänge zählen.
der macht das zuverlässiger als du und dimensioniert die Arrays
so lang wie benötigt
* das abschliessende \0 Zeichen hängt der Compiler für dich an
ein String-Literal an. Das brauchst du nicht selber machen
* Höchst wahrscheinlich brauchst du die Arrays in dieser Funktion
überhaupt nicht. Solange die Send Funktion an den Daten nichts
ändert, kannst du der auch getrost den String direkt übergeben
sendeDaten(DELETE_TASTE);// Bereich löschen und mit Hintergrundfarbe füllen
39
sendeDaten(TASTE_1_6);// Tasten 1 und 6 betätigt
40
oldKey=JOY_TASTE_16;
41
}
42
43
if(key2==1&&oldKey!=JOY_TASTE_26){
44
sendeDaten(DELETE_TASTE);
45
sendeDaten(TASTE_2_6);
46
oldKey=JOY_TASTE_26;
47
48
aktZustandMesser=1;
49
messer=1;
50
oldZustandMesser=0;
51
}
52
53
...
Wenn das dann immer noch zuviel Speicher verbraucht, müsste man darüber
nachdenken, die Strings ins Flash zu verfrachten(*), damit sie kein SRAM
verbrauchen.
(*) bzw. eigentlich nur im Flash zu haben. Denn im Moment braucht jeder
String Platz im Flash UND im SRAM.
Hallo Karl Heinz,
super... danke für deine ausführliche Beschreibung. Das hab selbst ich
verstanden :-)
stimmt... die ganzen Variablen kann ich mir sparen - es funktioniert so,
habe es gerade ausprobiert.
Danke!
Ich denke nicht, dass ich momentan noch zuviel Speicher brauch. Sobald
ich compeliere bekomme ich diese Anzeige:
1
Program: 3668 bytes (22.4% Full)
2
(.text + .data + .bootloader)
3
4
Data: 626 bytes (61.1% Full)
5
(.data + .bss + .noinit)
Das sollte doch noch genügend sein? Das Optimization Level steht auf
"Os".
Der verwendete Controller ist ein ATmega16.
Mein Problem ist, dass mein Programm schon deutlich ausführlicher (in
Sachen Quellcode) war und irgendwann genau dieses Problem wieder
aufgetaucht ist.
Dann habe ich es wieder "weg optimiert" z.B. durch ein "const" vor den
Variablen. Danach habe ich am Programm weiter geschrieben und irgendwann
tauchte das Ganze wieder auf. Will jetzt einfach sichergehen, dass es
kein Pointer-Problem oder sonst einen Denkfehler hab.
mfg pal
pal ... schrieb:> Ich denke nicht, dass ich momentan noch zuviel Speicher brauch. Sobald> ich compeliere bekomme ich diese Anzeige:>>
1
> Program: 3668 bytes (22.4% Full)
2
> (.text + .data + .bootloader)
3
>
4
> Data: 626 bytes (61.1% Full)
5
> (.data + .bss + .noinit)
6
>
>> Das sollte doch noch genügend sein? Das Optimization Level steht auf> "Os".> Der verwendete Controller ist ein ATmega16.
reicht erst mal. 61% lässt noch genug Reserve für den Stack und lokale
Variablen (solange du es nicht wieder übertreibst).
Bei dir war das ja so:
Die Strings stehen erst mal im Flash und verbrauchen dort Speicher.
(denn irgendwo müssen die ja beim Programmstart mal herkommen. Der SRAM
merkt sich ja nichts wenn man den Strom abdreht)
Von dort werden die String-Literale beim Hochfahren des Programms ins
SRAM umkopiert. Wenn ich jetzt mal alle Strings über den Daumen
zusammenzähle, dann sind das rund 300 Bytes.
Jetzt versuchst du in der Funktion noch 13 Arrays a 40 Bytes anzulegen,
macht 520 Bytes
d.h. alleine diese beiden Posten verbrauchen beim Aufruf dieser Funktion
820 Bytes. Du hast aber nur 1024 Bytes SRAM! Jetzt noch ein paar lokale
Variablen, ein bischen was am Return-Stack, noch ein paar globale
Variablen und die 1024 sind voll.
-> auch wenn des Softwerkers Herz schmerzt: keine übermässig grossen
funktionslokale Variablen! Ein paar uint8_t oder int sind ok (für
Variablen die man für Schleifensteuerung oder dergleichen braucht), 1
oder 2 Arrays mit 10 oder 20 Elementen ist auch ok. Aber mehr solls
nicht sein.
Das Problem: Alles was du hast um dich über den Speicherverbrauch zu
informieren, ist diese Zusammenfassung. Da tauchen aber lokale Variablen
nicht auf! Wie denn auch, die werden ja erst erzeugt, wenn die Funktion
aufgerufen wird. D.h. aber auch: benutzt du lokale Variablen für große
Sachen, dann ist die Zusammenfassung nutzlos, weil du nicht weißt
wieviel Speicher dann zur Laufzeit noch zusätzlich verbraucht wird. Du
musst dir also bei dieser Statistik eine Reserve lassen, die du in den
meisten Fällen einfach schätzen wirst. Bei vielen und großen Arrays
verschätzt man sich aber über den Speicherverbrauch ganz schnell.
Hallo Karl Heinz,
das war mir alles gar nicht so bewusst. Für den Speicherverbrauch habe
ich mir immer die 22.4% betrachtet.
Werde wahrscheinlich deinen Ratschlag mit den defines berücksichtigen.
Das wird wohl noch etwas Platz einsparen. Ich werde erstmal wieder
versuchen mit deinen Infos weiter zu programmieren und dich/euch über
meinen Erfolg oder Misserfolg auf dem laufenden halten :-)
Vielen Dank auch noch mal!
pal ... schrieb:> Hallo Karl Heinz,>> das war mir alles gar nicht so bewusst. Für den Speicherverbrauch habe> ich mir immer die 22.4% betrachtet.
Das ist der Teil, den dein Programm-Code im Flash verbraucht.
Aber dein Programm besteht ja nicht nur aus Code, sondern es braucht ja
auch Variablen. Die liegen im SRAM und in der Statistik taucht das als
"data" auf.
pal ... schrieb:> ok....> ich denke der Controller ist dann für meine Anwendung deutlich zu klein.
:-)
Wenns eng wird, kriegen wir da noch mal 300 Bytes (für die konstanten
Strings) frei.
'Wissen wie's geht' ist immer besser als 'Hau drauf und hinter mir die
Sintflut'
super, freut mich :-)
werde noch mal an meinem Programm weiter machen und melde mich wieder.
Wir müssen sicherlich noch einige Bytes befreien :-)
danke!!!
Hallo,
soooo... ich denke jetzt ist es an der Zeit das Ein oder Andere Byte
einzusparen. Die Strings hatte ich schon einmal durch defines ersetzt -
hat aber nichts gebracht.
Ich hoffe ihr könnt mir wieder dabei helfen. Der Teil des Codes, der am
meisten Speicher frisst habe ich mal online gestellt.
Zur Info... Nach dem compelieren zeigt die Statistik folgende Werte für
meinen ATmega16
Ich nehm jetzt nur willkürlich eine FUnktion heraus
1
voidsendeJoyBewToDispl(conststructJOYSTICK*j){
2
3
if(j->direction==ypos){
4
if(oldDirection!=JOY_VOR){
5
sendeDaten("#RL200,30,350,120,#UI255,35,2,");// Bereich löschen und mit Hintergrundfarbe füllen - Bild für Joystick vor anzeigen
6
oldDirection=JOY_VOR;
7
}
8
}
9
elseif(j->direction==yneg){
10
if(oldDirection!=JOY_ZURUECK){
11
sendeDaten("#RL200,30,350,120,#UI255,35,3,");
12
oldDirection=JOY_ZURUECK;
13
}
14
}
15
elseif(j->direction==xneg){
16
if(oldDirection!=JOY_LINKS){
17
sendeDaten("#RL200,30,350,120,#UI255,35,5,");
18
oldDirection=JOY_LINKS;
19
}
20
}
21
elseif(j->direction==xpos){
22
if(oldDirection!=JOY_RECHTS){
23
sendeDaten("#RL200,30,350,120,#UI255,35,4,");
24
oldDirection=JOY_RECHTS;
25
}
26
}
27
else{
28
if(oldDirection!=JOY_NULL){
29
sendeDaten("#RL200,30,350,120,");
30
oldDirection=JOY_NULL;
31
}
32
}
33
}
was fällt dir auf, wenn du die Strings vergleichst?
Also mir fällt auf, das das im Prinzip immer die gleichen Strings sind,
nur hinten ist eine 'Zahl' anders. Du legst aber für dieses 1 Byte
Unterschied jeweils einen kompletten String mit 30 Bytes an. 5 Strings
hast du, alle 5 fangen vollkommen identisch an und bei den restlichen 4,
gehts dann auch identisch weiter.
Das kann man doch sicher auch so umgestalten, dass diese Tatsache
ausgenutzt wird und man den kompletten String in 3 Teilstrings zerlegt,
deren Zusammensetzung dann die gewünschten Strings ergibt.
sendeDaten("2,");// Bereich löschen und mit Hintergrundfarbe füllen - Bild für Joystick vor anzeigen
12
oldDirection=JOY_VOR;
13
}
14
}
15
elseif(j->direction==yneg){
16
if(oldDirection!=JOY_ZURUECK){
17
sendeDaten(ClearJoyArea);
18
sendeDaten(SetJoyArea);
19
sendeDaten("3,");
20
oldDirection=JOY_ZURUECK;
21
}
22
}
23
elseif(j->direction==xneg){
24
if(oldDirection!=JOY_LINKS){
25
sendeDaten(ClearJoyArea);
26
sendeDaten(SetJoyArea);
27
sendeDaten("5,");
28
oldDirection=JOY_LINKS;
29
}
30
}
31
elseif(j->direction==xpos){
32
if(oldDirection!=JOY_RECHTS){
33
sendeDaten(ClearJoyArea);
34
sendeDaten(SetJoyArea);
35
sendeDaten("4,");
36
oldDirection=JOY_RECHTS;
37
}
38
}
39
else{
40
if(oldDirection!=JOY_NULL){
41
sendeDaten(ClearJoyArea);
42
oldDirection=JOY_NULL;
43
}
44
}
45
}
mal rechnen, wie jetzt die Bilanz aussieht.
Vorher hattest du
4 Strings a 31 Bytes .... 124 Bytes
1 String 19 Bytes .... 19 Bytes
------------
143 Bytes
Jetzt hast du
1 String 19 Bytes 19
1 String 11 Bytes 11
4 String a 4 Bytes 16
-----------
46 Bytes
Fazit: Fast 100 Bytes eingespart.
Schau dir deine Funktion sendeMaschAuswToDispl an. Praktisch alle
Ausgaben fangen mit #MN an. Das kann man durchaus auch vorziehen und nur
eine einzige Ausgabe von #MN machen lassen an die dann hinten nur noch
die Nummer angehängt wird.
Und jetzt hab ich mir tatsächlich nur die allerersten beiden Funktionen
angesehen, die ich in deinem Code gesehen habe. Da schlummern sicherlich
noch andere Schätze.
Edit: Yep
In sendeJoyTastenToDispl fangen ebenfalls alle Strings mit einer
identischen Sequenz an bzw. haben signifikant große gleiche Teile.
Zur Not macht man sich dann auch schon mal Spezialfunktionen, die einem
das Senden eines Strings in mehreren Happen erlauben. Die "#MN" Sache
müsste man zb ein wenig anders angehen. Man kann ja zb eine
Spezialfunktion machen, die immer zuerst mal "#MN" sendet und dann den
übergebenen String noch hinten nach absetzt. Oder FUnktionen
'BeginSendString', 'SendString', 'EndSendString', wenn da irgendwelche
Spezialsachen vor und nach dem Senden eines Strings notwendig sind.
Hallo Karl Heinz,
danke für deine Antwort. Leider kann ich die Strings nicht so
auseinander ziehen.
Du hast mich dabei allerdings auf die Idee gebracht, dass ich den Code
folgendermaßen änderen könnte:
Zur Not macht man sich dann auch schon mal Spezialfunktionen, die einem
das Senden eines Strings in mehreren Happen erlauben. Die "#MN" Sache
müsste man zb ein wenig anders angehen. Man kann ja zb eine
Spezialfunktion machen, die immer zuerst mal "#MN" sendet und dann den
übergebenen String noch hinten nach absetzt. Oder FUnktionen
'BeginSendString', 'SendString', 'EndSendString', wenn da irgendwelche
Spezialsachen vor und nach dem Senden eines Strings notwendig sind.
Ich bin beeindruckt was etwas hin und her kopieren von strings
bewirkt....
Im Anhang sind noch mal die Änderungen.
Hast du sonst noch eine Idee für Optimierungsbedarf?
Zur Info....
1
Device: atmega16
2
3
Program: 5028 bytes (30.7% Full)
4
(.text + .data + .bootloader)
5
6
Data: 305 bytes (29.8% Full)
7
(.data + .bss + .noinit)
8
9
EEPROM: 2 bytes (0.4% Full)
10
(.eeprom)
568 Byte geschrumpft....
Nachtrag.....
Die Funktion "sendeMaschAuswToDispl" werde ich natürlich auch noch
ändern...
Das ist böse. Du allokierst einen Speicher mit N Bytes und hängst dann
noch 2 Bytes dran. Das ergibt nicht nur einen satten Overflow, sondern
ClearJoyArea ist danach wegen str_n_cat auch nicht mehr terminiert.
Abgesehen davon passt das "const" dazu auch nicht mehr.
Wenn, dann brauchst Du einen Zwischenspeicher:
char Buf[MINDESTENS_SOVIEL_WIE_DER_STRING_INSG_LANG_WERDEN_KANN + 1];
strcpy (Buf, ClearJoyArea);
strcat(ClearJoyArea, ",2");
sendeDaten(Buf);
>> ist (was ich so gesehen habe) in allen Fällen immer gleich.> Heißer Kandidat für eine Funktion.
str_n_cat() ist hierfür nicht geeignet. Er zerschießt sich damit
sämtliche String-Terminierungen. Wenn, dann strcat() ohne Längenangabe.
pal ... schrieb:> danke für deine Antwort. Leider kann ich die Strings nicht so> auseinander ziehen.
Anderer Vorschlag.
Es ist ja nicht in Stein gemeisselt, dass du nur eine Send Funktion für
Strings haben darfst und die nur 1 String ausgeben darf.
Es spricht ja nichts dagegen, dass du dir zb eine SendeString2 FUnktion
machst, die 2 Strings bekommt und die beiden hintereinander ausgibt.
und die dann das richtige macht, ganz ohne umkopieren oder sonstigen
Aufwand (und nicht zu vergessen: potentielle Fehlerquellen!)
Und was für 2 geht, geht auch für 3
(und die sendDaten sieht dann so aus
1
uint8_tsendeDaten(constchar*data)
2
{
3
sendeDaten2(data,NULL);
4
}
)
Es gibt (fast) immer Mittel und Wege etwas zu vereinfachen. Und wenn man
sich seine Werkzeuge entsprechend zurechtschnitzen muss, dann muss man
sich eben manchmal seine Werkzeuge zurechtschnitzen, um an anderen
Stellen einfacher arbeiten zu können. Es gibt immer 2 Ansatzpunkte:
Entweder beim Aufrufer einer Funktion die Dinge vereinfachen
Oder die Funktionen selbst so zu gestalten, dass sich die Aufrufe
vereinfachen. Manchmal ist es in Summe simpler, ein wenig Aufwand in die
Funktionen zu stecken, wenn dadurch quer durch den restlichen Code ein
Haufen Code wegfällt.
hi,
SORRY!!!!
Ich hab eben den falschen Qullcode gepostet. Sorry.... Der alte
Quellcode war nicht wirklich gut!
Jetzt ist aber im Anhang der neue...
Sorry für eure Anstrengungen - danke
pal ... schrieb:> Ich hab eben den falschen Qullcode gepostet. Sorry.... Der alte> Quellcode war nicht wirklich gut!
Der ist auch nicht besser, z.B. hier:
1
charmaschEro[]="#MN15";
2
strncat(maschEro,"1,",2);
2 Fehler:
maschEro ist ein String mit Speicherlänge 5 + 1 = 6.
strncat() hängt da ohne Terminierung noch 2 Bytes dran.
Daraus ergeben sich 2 Bugs, die in Deinem Programm zu Fehlverhalten
führen können:
1. Der nachfolgende String maschBin wird überschrieben wegen
Bufferoverflow.
2. Der String maschEro ist nicht mehr mit '\0' terminiert.
Du brauchst für Deine Aktionen einen Zwischenpuffer, siehe mein Posting
Beitrag "Re: Pointer suche"
und statt strncat() musst Du strcat() benutzen, damit die Terminierung
erhalten bleibt.
Warum ignorierst Du meine bisherigen Angaben zu Deinem Code?
Hallo Frank.
sorry, das wollte ich nicht! War eine Kurzschlussreaktion auf meinen
falsch geposteten Code.
Du hast natürlich recht! Werde den Code natürlich ändern.
Aber danke schon einmal!
Und sorry noch mal....
pal ... schrieb:> Hallo Frank.>> sorry, das wollte ich nicht! War eine Kurzschlussreaktion auf meinen> falsch geposteten Code.>> Du hast natürlich recht! Werde den Code natürlich ändern.
Zieh auch mal ins Kalkül die Strings überhaupt nicht beim Aufrufer
zusammenzusetzen. Das muss nicht unbedingt sein, und damit hast du dann
schon eine Fehlerquelle weniger.
Wenn ich deinen Code so überfliege, dann gibt es viele Stellen die von
einer Funktion profitieren würden, die einen String ausgeben können, den
man der Funktion in 2 Teilen vorsetzt.
Beitrag "Re: Pointer suche"
Nur um das klar zu stellen:
Nicht das Frank hier nicht recht hat. Mit Strings umgehen zu können, und
zwar korrekt umgehen zu können, muss jeder C-Programmierer. Da führt auf
lange Sicht kein Weg daran vorbei.
Nur um Moment kannst du dich nochmal davor drücken, wenn du dir ein paar
Hilfsfunktionen baust, weil String Manipulation im Moment noch nicht
unabwendbar notwendig ist. Ganz im Gegenteil gibt es eine Möglichkeit,
wie man das Programm sogar um einiges einfacher kriegt, wenn man keine
Stringmanipulation macht.
pal ... schrieb:> Ja klar, das wollte ich auch damit nicht so sagen. :-)> Ich werde natürlich versuchen an geeigneter Stelle Franks Informationen> zu brücksichtigen
Kombiniere meine Tipps besser direkt mit denen von Karl Heinz. Er hat
nämlich völlig recht: es ist Unsinn, die Strings an N Stellen immer
wieder zusammenzusetzen. Das kostet nur unnötig viel Code.
Genau dafür gibt es Funktionen, in denen man sich immer wiederholende
Vorgänge zusammenfasst. Also schreib eine Funktion, die direkt 2 Strings
bekommt, diese beiden zu einem zusammensetzt und dann die eigentliche
Funktion aufruft, die Du benutzen willst.
Hallo,
danke noch mal für eure Kritik und guten Ratschläge. Ich denke, dass ich
jetzt alles entsprechend umgesetzt habe.
Allerdings denke ich, dass dort immer noch Optimierungsbedarf besteht.
Denn betrachtet man sich beeispielsweise die Funktion
"sendeDrehzToLeistmod" braucht diese enorm viel Speicherplatz. Ich
denke, dass es am casten liegt - hätte aber noch keine Idee es anders zu
lösen
Danke für eure Hilfe
mfg pal
pal ... schrieb:> "sendeDrehzToLeistmod" braucht diese enorm viel Speicherplatz. Ich> denke, dass es am casten liegt - hätte aber noch keine Idee es anders zu> lösen
Solange du da float Arithmetik benutzt, darfst du dich über den
Speicherverbrauch nicht wundern.
Brauchst du überhaupt float? Wozu?
Da kommt ein Wert raus, den du ins PWM Register schiebst. Wer braucht da
Nachkommastellen?
Wenn man die Berechnung
drehzRed = minDrehz - (((minDrehz - maxDrehz) / 100.0) * aktDrehz);
ein wenig umstellt, kann man das ohne Genauigkeitsverlust auch mit
INteger-Arithmetik berechnen (Faustregel: Divisionen will man immer
soweit wie möglich 'nach rechts' schieben, d.h. so spät wie möglich
machen)
Und fällt dann die Floating Point Arithmetik weg, dann fällt auch der
dazu notwendige Code weg. Und das ist nicht gerade wenig.
i2c_start_wait(DISPLAY_WRITE);// setze Adresse vom Display und Schreib-Modus
15
16
i2c_write(DC1);// sende DC1
17
18
len1=strlen(data1);
19
len2=strlen(data2);
20
len3=strlen(data3);
21
i2c_write(len1+len2+len3);// sende Datenlänge
AUtsch.
Ich hab extra in der Vorlage hier eine Absicherung gemacht. Die war
nicht einfach so aus einer Lust und Laune heraus. Du DARFST strlen NICHT
mit einem NULL Pointer aufrufen! Du tust es aber
1
uint8_tsendeDaten(constchardata[]){
2
returnsendeDaten3Str(data,NULL,NULL);
3
}
Gewöhn dir an: Wann immer ein Pointer im Spiel ist, dann muss die erste
Frage lauten - kann der NULL sein?
Und im Zweifelsfall ist immer davon auszugehen, dass der Pointer NULL
sein kann.
pal ... schrieb:> Denn betrachtet man sich beeispielsweise die Funktion> "sendeDrehzToLeistmod" braucht diese enorm viel Speicherplatz.
Du musst von den floats weg und alles in Integern machen.
Mache erst die Multiplikation mit aktDrehz und dann erst die Division
durch 100. Alles mit Integern. Dann kommst Du für msg->bytes.pwmVal auf
denselben Wert.
Muss sendeDrehzToLeistmod() überhaupt einen float zurückliefern? Ich
glaube, der PWM-Wert reicht.
Gruß,
Frank
EDIT: Karl Heinz war schneller :-)
> Gewöhn dir an: Wann immer ein Pointer im Spiel ist, dann muss die erste> Frage lauten - kann der NULL sein?> Und im Zweifelsfall ist immer davon auszugehen, dass der Pointer NULL> sein kann.
PS: Hier in der Funktion #IST# data1 ein Pointer, auch wenn das data[]
in der Argumentliste etwas anderes suggeriert. Zwischen
besteht kein Unterschied. Die [] Schreibweise ist nur syntaktischer
Zucker. Tatsächlich #SIND# das Pointer, die in einer anderen Syntax
versteckt wurden. Und da das durch die [] Schreibweise mehr schlecht als
recht verschleiert wird, wird meistens von dieser Schreibweise
abgeraten.
Gewöhn dich an Pointeroperationen. Je früher desto besser. Es gibt
keinen Grund sich (auf dieser Ebene) davor zu fürchten.
hallo,
ok, ich gebe mich wieder ran und versuch es umzusetzen.
Sorry Karl Heinz, mir ist nicht aufgefallen das du den NULL-Pointer
überprüft hast.
Ich habe nur auf die while-Schleifen verzichtet. Arbeite lieber mit
for's.
pal ... schrieb:> Ich habe nur auf die while-Schleifen verzichtet. Arbeite lieber mit> for's.
Ist zum jetzigen Zeitpunkt noch ok. Aber irgendwann wirst du den Schritt
machen müssen. Auf dieser Ebene sind Pointer noch leicht zu beherrschen
und Stringoperationen laufen sowieso immer noch dem gleichen Muster ab.
Hallo,
ich habe mir noch mal eure Ratschläge zu Herzen genommen....
Im Anhang habe ich noch mal die geänderten Quellen. Ich hoffe ihr habt
wieder gute Kritik. DANKE!!!
Nachtrag....
Die Statistik zeigt nun folgendes....
Programmiertaktik:
Schau deine Header Files durch.
Welche der #define werden nur in EINEM C-File benutzt und werden auch in
Zukunft nur in diesem einen C-File benutzt werden?
Diese #define gehören nicht ins Header File, sondern ins jeweilige
C-File. Du musst das Header File aus Sicht eines 'Verwenders' sehen. Im
Header File sind nur die Dinge, die einen Verwender interessieren, die
ER benötigt. Werden #define nur in einem Code File verwendet, dann
sollen die auch dort bleiben.
ZB die
1
#define JOY_NULL 0 // Joystick in Nullposition
2
#define JOY_VOR 2 // Joystickbewegung nach vor
3
#define JOY_ZURUECK 3 // Joystickbewegung nach zurück
4
#define JOY_RECHTS 4 // Joystickbewegung nach rechts
5
#define JOY_LINKS 5 // Joystickbewegung nach links
6
7
#define JOY_TASTE_UNBET 0 // Joystick - keine Taste betätigt
#define JOY_TASTE_16 16 // Joystick Taste 1 und 6 betätigt
15
#define JOY_TASTE_26 26 // Joystick Taste 2 und 6 betätigt
16
#define JOY_TASTE_36 36 // Joystick Taste 3 und 6 betätigt
17
#define JOY_TASTE_46 46 // Joystick Taste 4 und 6 betätigt
18
#define JOY_TASTE_56 56 // Joystick Taste 5 und 6 betätigt
Interessieren die ausserhalb display.c irgend jemanden? Muss jemand
wissen, dass
a) es diese Konstanten gibt?
b) welche Werte sie haben?
Ich denke nicht. Ergo gehören diese Konstanten nicht ins Header File,
sondern nach display.c
(und so gibts noch andere #define, die zumindest zweifelhaft sind).
Ansonsten: Schau selber deinen Code durch! Zb hier
1
uint8_tleseDaten(){
2
chardataS[]={'S'};
Wozu brauchst du da ein Array?
Schreibweisen:
mehrere Leerzeilen hintereinander erhöhen nicht die Übersicht, sie
ziehen den Code nur in die Länge. Wenn du im Code einen optischen
Schnitt machen willst, dass hier eine neue Funktion anfängt, dann reicht
1 Leerzeile durchaus. Wenn dir etwas so wichtig war, dass du es mit
mehreren Leerzeilen abtrennen musst, dann ist es wohl auch wichtig
genug, dass es sich einen Kommentar verdient hat.
oldDirection hat also ganz offensichtlich etwas mit den #define
1
#define JOY_NULL 0 // Joystick in Nullposition
2
#define JOY_VOR 2 // Joystickbewegung nach vor
3
#define JOY_ZURUECK 3 // Joystickbewegung nach zurück
4
#define JOY_RECHTS 4 // Joystickbewegung nach rechts
5
#define JOY_LINKS 5 // Joystickbewegung nach links
6
...
zu tun.
Nur: Es gibt kein #define, welches einer 255 einen Wert geben würde. 255
ist für diese Variable gar nicht erlaubt. Warum initialisierst du dann
diese Variable damit? Nimm einen Wert der erlaubt ist und am besten
benutzt du da gleich einen deiner #define-ten Werte dafür
1
staticuint8_toldDirection=JOY_NULL;
Schau deine Kommentare durch. Welche sind 0-Kommentare? Das sind
Kommentare, die genau das gleiche aussagen wie das, was ohnehin schon im
Code steht. Erzählt mir ein Kommentar etwas neues, was ich nicht im Code
sehe? Wenn nicht, dann weg mit dem Kommentar - denn im besten Fall
erfahre ich durch Lesen des Kommentars nichts neues, im schlimmsten Fall
ist er falsch.
Das zb
1
_delay_us(100);// kurze Pause
ist ein 0-Kommentar. Das mit _delay_us kurz gewartet wird, sehe ich im
Code auch. Was mich aber interessieren würde: Warum gerade 100us? Steckt
da was dahinter oder ist das relativ willkürlich? Nur leider: Genau das
erzählt mir der Kommentar nicht. Der erzählt mir nur Sachen, die ich
beim Lesen des Codes auch sehe.
Das hier
1
i2c_write(DC1);// sende DC1
ist ein 0-Kommentar.
Generell ist es immer verdächtig, wenn man versucht ist, rechts an so
gut wie alle Anweisungen einen Kommentar zu setzen. Das sind meistens
0-Kommentare. In deiner send Funktion wäre zb in einem Kommentar zu
dokumentieren, was da eigentlich genau in welcher Reihenfolge gesendet
wird. Aber als Blockkommentar vor dem ganzen Code!
1
...
2
// Sendet einen String an das Gerät
3
// Gesendet wird
4
// Kommando (== DC1)
5
// ANzahl der Zeichen
6
// die Zeichen selber
7
// Prüfsumme
8
//
9
// DIe Prüfsumme errechnet sich aus der arithmetischen Summe aller Bytes
10
// (inklusive der Länge und dem Kommandobyte), wobei nur das Lower-Byte
11
// gesendet wird
12
...
Das zb. ist ein Kommentar, der mich weiterbringt. WEnn ich den COde
lese, dann kann ich die Information aus dem Kommentar verwenden um den
Programmfluss zu verfolgen. Die ganzen Einzelkommentare brauch ich dann
gar nicht mehr. Denn die erzählen mir erstens sowieso nicht viel und
zweitens ist die Absicht im Code damit nicht erkennbar (und drittens
sind die des öfteren tatsächlich falsch)
Generell: Kommentiere nicht das WIE, sondern das WARUM
WIE eine Operation durchgeführt wird, steht im Code. Der Kommentar muss
mir Info liefern WARUM etwas gemacht wird.
ja mit den Kommentaren geben ich dir auch vollkommen recht. Die sind
teilweise nutzlos. Mit deinem Beitrag hast du mir aber schon eine gute
Vorlage geliefet. Die Kommentare oberhalb der Funktion wollte ich
eigentlich erst später machen. Aber du hast schon recht - man sollte
solche Dinge eigentlich nicht aufschieben.
Was würdest du noch zum Code sagen - soweit ok?
Mit der Statistik liege ich jetzt bei 27,7%.
Ich habe jetzt allerdings noch manchmal das Problem, dass wenn ich
Controller und Display starte (d.h. Spannung ein schalte) bekomme ich
nicht immer eine Aktion am Display angezeigt. Sobald ich das Display
dann wieder resette funktioniert es wieder. Hast du eine Idee was das
sein kann?
Werde mich aber jetzt doch an die Kommentare geben :-)
sollte überarbeitet werden. Die ist für die Funktionalität zu lang und
zu unübersichtlich. Der Aufruf von setEEAndId(id); erfolgt in allen
Fällen und kann daher aus dem ganzen switch-case raus. (Ich hätte ihn
fast übersehen, als ich nachverfolgen wollte, wie und wo du wieder ins
EEPROM schreibst)
In dem Fall
ja das gewollt, dass bei der ID_HEIM nicht ins EEPROM geschrieben wird.
Mir ist auch aufgefallen, dass die Funktion deutlich zu lange und
unübersichtlich ist. Mir ist aber nicht ganz klar wie ich diese
vereinfachen soll
In der Funktion "void sendeMaschAuswToDispl(uint8_t id)" kann man
"setEEAndId(id)" nicht aus dem case zeihen, weil noch andere IDs
auftauchen könnten und diese nicht ins EEPROM geschrieben werden sollen
Ich würde die FUnktion so schreiben.
(Achtung: Ich handle mir damit eine Abhängigkeit zu den Zahlenwerten im
#define ein. Da du die aber im Nachhinein sowieso nur schlecht ändern
kannst, würde ich das akzeptieren)
Da du deine Werte nicht fortlaufend vergeben hast, gibt es leider keine
Möglichkeit, das alles noch mehr Datengesteuert zu machen, so dass man
den Code noch kompakter kriegt.
Karl Heinz Buchegger schrieb:> Da du deine Werte nicht fortlaufend vergeben hast, gibt es leider keine> Möglichkeit, das alles noch mehr Datengesteuert zu machen, so dass man> den Code noch kompakter kriegt.
Egal.
Probieren wir einfach mal, wo uns das hinführt
1
structidSel
2
{
3
uint8_tid_;
4
uint8_tnum_;
5
}
6
7
// Übersetzungstabelle für Error-Ids zu Codes die zum Display
8
// zu schicken sind
9
structidSelidSelError[]=
10
{
11
{ID_EINS_CANEPRUNER_ERO,1),
12
{ID_EINS_LAUBHEFTER_ERO,2),
13
{ID_EINS_LAUBSCHNEIDER_ERO,3),
14
{ID_EINS_LAUBSAUGER_ERO,4),
15
{ID_EINS_ENTBLAETTERER_ERO,5),
16
{ID_ZWEIS_LAUBSCHNEIDER_ERO,6),
17
{ID_ZWEIS_LAUBSAUGER_ERO,7),
18
{ID_ZWEIS_ENTBLAETTERER_ERO,8),
19
};
20
21
structidSelidSelBin[]=
22
{
23
{ID_EINS_VORSCHNEIDER_BIN,1),
24
{ID_EINS_ENTLAUBER_BIN,2),
25
{ID_EINS_LAUBSCHNEIDER_BIN,3),
26
{ID_ZWEIS_ENTLAUBER_BIN,4),
27
{ID_ZWEIS_LAUBSCHNEIDER_BIN,5),
28
};
29
30
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
31
32
voidsendeMaschAuswToDispl(uint8_tid){
33
34
constcharmaschEro[]="#MN15";
35
constcharmaschBin[]="#MN16";
36
37
if(id==oldMachineSelection)
38
return;
39
40
if(id==ID_HEIM){
41
sendeDrehzToDispl(ID_BETRIEB_READY);
42
return;
43
}
44
45
// ist es ein Error Code?
46
for(i=0;i<ARRAY_SIZE(idSelError);i++){
47
if(idSelError[i].id_==id){
48
setEEAndId(id);
49
SendNumString(maschEro,idSelError[i].num_);
50
return;
51
}
52
}
53
54
// Nope, kein Error Code. Bin Code?
55
for(i=0;i<ARRAY_SIZE(idSelBin);i++){
56
if(idSelBin[i].id_==id){
57
setEEAndId(id);
58
SendNumString(maschEro,idSelBin[i].num_);
59
return;
60
}
61
}
62
}
Ist nicht wirklich kürzer. Vorteil ist höchstens, dass man die Codes
erweitern kann indem man einfach zum jeweiligen Array zufügt.
Karl Heinz Buchegger schrieb:> (Achtung: Ich handle mir damit eine Abhängigkeit zu den Zahlenwerten im> #define ein. Da du die aber im Nachhinein sowieso nur schlecht ändern> kannst, würde ich das akzeptieren)>
Da hab ich aber ordentlich geschlampt und Fehler eingebaut :-)
Ich denke, ich würde die Version nehmen. Dadurch dass zwischen Error-Id
und Bin-Id unterschieden wird, wird die tabellengesteuerte Variante
nicht wirklich kürzer oder einfacher. Und auf die Variante, diese Info
auch noch mit in die Tabelle aufzunehmen kann ich mich (noch) nicht
erwärmen.
So wie hier, mit der Erkennung der jeweiligen 'Gruppe' und
zusammenstoppeln des zu sendenden Codes aus der Gruppenposition, das
scheint mir ein guter Kompromiss zu sein. Vielleicht noch eine kleine
Prise Makro-Magie, aber mehr würde ich nicht tun.
So eine ähnlich Idee hatte ich auch schon...
Noch habe ich die Möglichkeit die IDs näher zusammen zurücken. Ich denke
damit können wir den Code noch vereinfachen.
Ich gebe mich einfach mal dran und poste ihn schließlich wieder....
Danke für deine/eure Hilfe!!!
pal ... schrieb:> So eine ähnlich Idee hatte ich auch schon...>> Noch habe ich die Möglichkeit die IDs näher zusammen zurücken. Ich denke> damit können wir den Code noch vereinfachen.
Wichtig ist vor allen Dingen, dass sie fortlaufend sind!
Alle xxx_ERO Codes mit aufsteigender Nummerierung. Alle xxx_BIN Codes
mit aufsteigender Nummerierung.
Dann geht:
Werde es gleich mal probieren und meinen Miss-/Erfolg posten :-)
Ich habe den Code jetzt so geändert, dass alle empfangenen ID identisch
mit den zu sendenden String sind und die IDs zusammen liegen Das
vereinfach das Ganze noch ein wenig.
mfg
pal ... schrieb:> void sendeMaschAuswToDispl(uint8_t id) {> char buffer[3];> const char masch[] = "#RL5,5,150,160,#UI10,10,";
Huch, wo kommt denn jetzt auf einmal dieser String her.
Vorher wars doch noch
* für Error Codes #MN15
* für Bin Codes #MN16
Ja, ich habe mir der Teil im Display angesehen und denke, dass es so
funktionieren könnte :-)
Werde es gleich noch prüfen.
Dank dir!!! Melde mich aber noch mal :-)
Momentan funktioniert gar nichts mehr bei mir....
Ich bin mir ziemlich sicher, dass die Software funktioniert. Betrachte
ich mir das Display, welche über I2C angesteuert wird leuchtet lediglich
die SCL Leuchte (rot) - SCA ist dunkel.
Was mich noch wundert, dass ich plötzlich durch
1
uint8_teeMaschEEMEM=0;
einen Fehler bekomme (siehe Anhang). Sobald ich dieses durch
> Ich bin mir ziemlich sicher, dass die Software funktioniert.
Das sind keine guten Voraussetzungen. 'ziemlich sicher' ist zu wenig.
Manchmal passiert es schon, dass man zuviele Änderungen auf einmal macht
und dabei Fehler einbaut.
Hatte wohl jeder schon mal und irgendwann ist der Punkt erreicht an dem
nur noch eines bleibt:
Zurück zur letzten Version, die ganz sicher noch funktioniert hat und
dieselben Änderungen nochmal NEU machen. Aber diesmal: in kleineren
Änderungseinheiten arbeiten! Und nach jeder Änderung ausgiebig testen!
Egal wie banal die Änderung auch ausgesehen haben mag - denn irgendwas
übersieht man immer.
Die Einstellung "Ich mache keinen dummen Fehler" ist die falsche. Jeder
macht dumme Fehler. Und diese muss man so früh wie möglich detektieren.
Daher:
Immer in kleinen Einheiten arbeiten.
Keine ungetesteten Codemonster produzieren.
Sich immer überlegen, wie man sich möglichst schnell wieder in einen
Codestand manövrieren kann, den man testen kann - selbst wenn noch nicht
alle geplanten Änderungen durchgezogen sind oder Einzelfunktionen
gefaked werden müssen.
Keine Fehler vor sich herschieben. Logikfehler beheben sich
normalerweise nicht selbst. Schiebt man die Fehlersuche und Behebung zu
lange vor sich her, wird die Sache nicht einfacher sondern schwieriger.
Die bist Codeproduzent und Controller in Personalunion und musst dich
selber kontrollieren und an die Leine legen.
das #include <eeprom.h> ist im HeaderFile
Ja eigentlich bin ich auch für die kleinen Einheiten, aber ich habe
eigentlich nur die Funktion "sendeMaschAuswToDispl" vereinfacht.
Warum aber jetzt plötzlich mein Bus nichts mehr macht ist für mich
unbegreiflich
Das Problem saß vor dem Rechner :-)
Ich hatte noch einen Fehler in der Funktion "leseDaten".
Die Funktion "sendeMaschAuswToDispl" habe ich ebenfalls noch deutlich
vereinfacht.
@Karl Heinz & Frank: Ich bedanke mich noch mal ganz herzlich für eure
Hilfen!!!
Wie funktioniert das in einem Forum, darf ich euch als Dank eine
Bewertungen abgeben? (Ebay ähnlich)
pal ... schrieb:> Wie funktioniert das in einem Forum, darf ich euch als Dank eine> Bewertungen abgeben? (Ebay ähnlich)
Schick dem Karl Heinz ein virtuelles Bierchen von mir, ich schulde ihm
noch eines ;-)
> sprintf(buffer, "%d", aktDrehz);
Funktioniert das auf deinem Display mit dem Überschreiben, wenn die
Drehzahl abnimmt? Wenn also die Drehzahl von 10 auf 9 abfällt, steht
dann auch tatsächlich "9" auf dem Display und nicht "90"?
Persönlich finde ich es immer eine gute Idee, wenn Zahlenangaben auf
Displays nicht hin und her hüpfen. Das bringt so eine Unruhe ins Display
und ist auch gerade in den Bereichen des Wechsels der Stellenanzahl
ermüdend abzulesen. Die Lösung: die Einerstelle der Zahlen ist immer an
der gleichen Position. ALso nicht
10 |
9 |
8 | Zeit
11 v
...
sondern
10
9
8
11
....
sprintf macht dir das leicht, weil du ihm nur sagen musst, dass die
Ausgabe mindestens 2 stellig sein soll.
sprintf(buffer, "%2d", aktDrehz);
sprintf füllt dann eigenständig links mit Leerzeichen auf. Damit ist
sichergestellt, dass die Einerstelle im String immer an der gleichen
Position ist.
(Und nebenbei löst das dann auch gleich noch das Überschreibproblem)
Will man mit führenden 0-en auffüllen lassen, dann eben
sprintf(buffer, "%02d", aktDrehz);
Hallo Karl Heinz,
ja das funktioniert und die Stellen springen nicht hin und her. Das
passt schon...
Aber danke für den Hinweis...
@Karl Heinz: Im Anhang dein virtuelles Bierchen :-)