Hallo,
mein Name ist Florian und ich beschäftige mich seit etwa 1.5 Monaten mit
dem Programmieren von Mikrocontrollern. Auslöser hierfür war ein
Programmierkurs an der Uni un der darauffolgende Wunsch das ganze
praktisch anzuwenden.
Inzwischen arbeite ich an folgendem Projekt: Mit einem ATmega8, soll die
Infrarotschnittstelle eines Stromzählers ausgelesen werden, der Zähler
sendet etwa alle Sekunde ein Datenpaket das Informationen zu den
Zählerständen enthält. Einige Beispielprotokolle befinden sich im
Anhang.
Der Controller soll nun aus der Zeitdifferenz zwischen 2 Paketen und der
Differenz der Zählerstände den aktuellen Verbrauch ausrechnen und auf 4
7-Segmentanzeigen anzeigen.
Die Anzeige wird mit folgender ISR gelöst:
1
ISR(TIMER0_OVF_vect){
2
PORTC=0;
3
show(display[pos]);
4
PORTC|=(1<<pos);
5
if(pos==0&&einspeisen==1)
6
PORTD&=~(1<<7);
7
if(pos==1&&powered==1)
8
PORTD&=~(1<<7);
9
pos++;
10
if(pos>3)
11
pos=0;
12
}
Die Variable pos ist am Anfang 0, die ISR schaltet nun zuerst PORTC auf
0, über ihn ist der + der Anzeigen mithilfe von Transistoren geschaltet.
Anschließend schaltet die Funktion show() die gewünschten Segmente für
die anzuzeigende Zahl, welche Zahl angezeigt werden soll entnimmt sie
einem Array.
Die show()-Funktion sieht wie folgt aus:
1
voidshow(intnum){
2
if(num==0)
3
PORTD=0b11000000;
4
else
5
if(num==1)
6
PORTD=0b11111001;
7
else
8
if(num==2)
9
PORTD=0b10100100;
10
else
11
if(num==3)
12
PORTD=0b10110000;
13
else
14
if(num==4)
15
PORTD=0b10011001;
16
else
17
if(num==5)
18
PORTD=0b10010010;
19
else
20
if(num==6)
21
PORTD=0b10000010;
22
else
23
if(num==7)
24
PORTD=0b11111000;
25
else
26
if(num==8)
27
PORTD=0b10000000;
28
else
29
if(num==9)
30
PORTD=0b10010000;
31
else
32
if(num==10)
33
PORTD=0b00000000;
34
else
35
PORTD=0b10000110;
36
}
Überprüft also nacheinander was angezeigt werden soll. Hierbei steht 10
für alles an, dies dient einem Displaycheck beim Starten des uC. Die
else Anweisung nach if(num==10) schaltet das Segment auf E, dies wird
ausgelöst wenn eine Zahl größer 10 angezeigt werden soll.
Das Array display[] wird über folgenden Code befüllt:
1
for(i=0;i<4;i++){
2
display[i]=(int)verkaufendiff%10;
3
verkaufendiff/=10;
4
}
Hierbei ist Verkaufendiff ein vorher berechneter Wert des Datentyps
float.
Meiner Meinung nach sollte nun in jedem der 4 Einträge von display[]
eine Zahl zwischen 0 und 9 stehen, da %10 alles bis auf die letzte
Stelle fressen sollte.
Trotzdem zeigen die 7-Segment-Anzeigen sehr oft E an, sie sollen also
etwas größer als 10 darstellen. Sollte eigentlich nicht möglich sein, da
im Array ja nur Werte kleiner 10 stehen sollen.
Die ISR kann meiner Meinung nach auch nicht auf anderen Teile des Arrays
zugreifen außer den Index Nummern 0, 1, 2 und 3, da bei einem Wert von 4
auf 0 resettet wird.
Ich bin relativ ratlos was hier das Problem ist. Im Anhang befindet sich
neben den Beispielprotokollen noch der Komplette Quellcode falls das
jemandem weiterhilft.
Gruß Florian
Leider nicht, der Zähler gibt nur die kurze Version des Protokolls aus
und die sieht wie die angehängte EHz.txt aus.
Wenn es eine möglichkeit gibt das Protokoll auf Erweitert zu stellen
nehm ich das natürlich gerne, hab in die Richtung aber noch nichts
gehört.
> for(i=0;i<4;i++){> display[i]=(int)verkaufendiff%10;> verkaufendiff/=10;> }
wenn verkaufendiff wirklich ein float ist, dann lass dir den zuvor
einmalig erst mal auf einen int umwandeln, ehe du dann daran gehst, den
int zu zerlegen.
Und mach dir doch um Gottes Willen dafür eine Funktion. In deinem Code
findet man doch nichts mehr!
1
voidtoDisplay(floatwert)
2
{
3
uint16_tiWert=(uint16_t)(wert+0.5);// Annahme: wert kann nur positiv sein
4
uint8_ti;
5
6
for(i=0;i<4;i++)
7
{
8
display[i]=iWert%10;
9
iWert/=10;
10
}
11
}
damit hast du den Teil geklärt.
Noch einfacher wird die Sache, wenn du deine ganze Show Funktion gleich
mal über die Klinge hüpfen lässt und in den display Variablen gleich die
Codes für die 7-Segmentanzeigen hinterlegst. Dann brauchst du nämlich in
der ISR nicht 100 mal in der Sekunde das immer gleiche tun und immer
wieder zur selben Zahl dieselben Codes raussuchen.
Aber bitte mit etwas Intelligenz! Die Codes kann man in einem Array
ablegen! Da muss man nicht mit einer elends langen if-else if Kette
arbeiten!
uint16_tiWert=(uint16_t)(wert+0.5);// Annahme: wert kann nur positiv sein
10
uint8_ti;
11
12
for(i=0;i<4;i++)
13
{
14
display[i]=SegCodes[iWert%10];
15
iWert/=10;
16
}
17
}
18
19
ISR(TIMER0_OVF_vect)
20
{
21
PORTC=0;
22
PORTD=display[pos];
23
PORTC|=(1<<pos);// das sollte man auch ersetzen. 1<<pos ist eine üble Operation
24
25
// Bit 7 dimmen, je nachdem ob einspeisen bzw powered 1 sind ergibt das
26
// unterschiedliche Dimm-Stufen
27
if(pos==0&&einspeisen==1)
28
PORTD&=~(1<<7);
29
if(pos==1&&powered==1)
30
PORTD&=~(1<<7);
31
32
pos++;
33
if(pos>3)
34
pos=0;
35
}
Und dann rufst du einfach nur die Funktion toDisplay auf, wenn du einen
Zahlenwert hast, der auszugeben ist.
1
...
2
3
if(einspeisen==1)
4
toDisplay(verkaufendiff);
5
else
6
toDisplay(kaufendiff);
7
...
Weiters: Datentypen!
Es bringt nichts, wenn du da mit Datentypen großzügig rummachst. Wenn
eine 8 Bit Variable ausreicht, dann mach die auch 8 Bit! Machst du die
größer, dann kannst du dir damit Probleme einhandeln (und ich denke,
genau das war eines deiner Probleme), weil ein int auf einem AVR nun mal
nicht von alleine atomar behandelbar ist.
Benutze die Bitnamen! Die stimmen dann mit den Namen im Datenblatt
überein! Bei 1<<0 muss man erst mal im Datenblatt bei der
Registerbeschreibung nachsehen, wie das Bit mit der Nummer 0 heißt um
dann im weiteren mit dem Namen im Datenblatt weiterzusuchen.
1
TCCR0|=(1<<CS01)|(1<<CS11);
2
TIMSK|=(1<<TOIE0);
da steht jetzt explizit: TOIE0
-T-imer -O-verflow -I-nterrupt -E-nable -0-
Dieses Bit ist also dafür zuständig, dass der Interrupt beim Timer
Overflow vom Timer 0 enabled (also "erlaubt") wird. Dieses ist die
Information, die einen Leser (auch dich selbst) interessiert! Was mich
hingegen überhaupt nicht interessiert, nicht interessieren soll, ist,
dass dieses das Bit 0 ist! Das ist eine Information, die ich nicht
brauche.
Die Bitnamen sind Kürzeln! Und wenn ich die kenne, bzw. den
systematischen Aufbau dieser Kürzel kenne, dann kann man aus dem
Bitnamen ganz leicht erkennen, was da eigentlich freigegeben wird.
Hier kann ich leicht erkennen, dass
TIMSK |= (1<<TOIE0);
und
ISR(TIMER0_OVF_vect)
zusammenpassen und das da kein Fehler passiert ist. In beiden Fällen ist
übereinstimmend vom Interrupt bei einem Overflow des Timers 0 die Rede.
Bei
TIMSK|=(1<<0);
muss ich hingegen erst mal im Datenblatt nachschlagen, ob da mit dem Bit
0 das richtige gemeint ist, wenn ich das überprüfen will.
ist IMHO unmittelbarer ersichtlich, was mit dieser Schleife bezweckt
wird und unter welchen Umständen sie beendet wird.
Wenn du das '!' in der Eingabe gar nicht brauchst, dann kannst du das
auch so machen
1
i=0;
2
log[i]=readchar();
3
while(log[i]!='!'){
4
i++;
5
log[i]=readchar();
6
}
oder in der C-typischen Zusammenfassung unter Ausnutzung der Tatsache,
dass auch eine Zuweisung ein Ergebnis hat (nämlich der Wert der
zugewiesen wurde)
1
i=0;
2
while((log[i]=readchar())!='!')
3
i++;
Man muss nicht alles auf Biegen und Brechen in eine for-Schleife
quetschen. Vor allen Dingen dann nicht, wenn die Schleife vom Kern her
eigentlich eine 'mache so lange wie', also eine while Schleife ist.
(Und im übrigen gehört es zum guten Ton, bei solchen Sachen
sicherzustellen, dass man auf keinen Fall das Array überlaufen kann.
Selbst dann nicht, wenn (aus welchem Grund auch immer) in der Eingabe
kein '!' auftaucht.
Danke erstmal für die Kritik/Verbesserungsvorschläge, habe direkt einige
Punkte umgesetzt, so ist das Array schreiben in eine eigene Funktion
gewandert, einige Ints sind Chars gewichen und beim Timer Interrupt
stehen jetzt die Bit-Namen. Außerdem habe ich entsprechende Sicherheiten
eingebaut damit die Arraygrenzen nicht überschritten werden.
Das Schwergewicht show() habe ich bewusst gelassen, um die selben
Ausgabeoptionen zu haben wie vorher, sprich wenn er etwas größer als 10
ausgeben soll, stellt er ein E dar. Wenn es endlich funktioniert wird es
natürlich ersetzt, bringt einiges an freiem Speicherplatz.
Das ganze kompiliert, auf den uC hochgeladen und ausprobiert ergibt
allerdings weiterhin das Problem, dass ich E's angezeigt kriege, also im
array zu große Werte stehen.
Florian T. schrieb:> Ausgabeoptionen zu haben wie vorher, sprich wenn er etwas größer als 10> ausgeben soll, stellt er ein E dar.
In
wert % 10
KANN nichts größeres als 9 rauskommen, wenn 'wert' ein positiver Integer
ist.
Florian T. schrieb:> Die show()-Funktion sieht wie folgt aus:
Kleiner Tipp nebenbei. Es gibt sowas wie eine switch-case-Anweisung in
C. Die solltest du dir mal anschauen...
Gruß
Marius
Persönlich denke ich ja, dass die komplette Messwertaufnahme in main()
so wie geschrieben, nicht besonders klug gemacht ist. Die ganze
Zeiterfassung ist unsinnig (da nicht genau. _delay_us abzählen bis in
Pin einen Pegel wechselt ist gerade bei nebenher laufenden Interrupts
alles andere als genau) und in die Aufnahme und Verarbeitung der Daten
vom UART hab ich auch kein Vertrauen. Du vielen float tun dann auch noch
ihr übriges.
Daher: Erst mal nur das Multiplexen testen!
tritt deinen 'langen' Multiplexteil in die Tonne, und verwende den
kurzen, wie ich ihn dir gezeigt habe.
Und dann ein main(), welches erst mal nur die 7-Seg Ansteuerung testet.
1
...
2
3
intmain()
4
{
5
floati;
6
7
DDRC=0b00001111;/*0, 1, 2, 3 als Ausgang, 5 als Eingang*/
8
DDRD=0b11111111;/*Komplett D auf Ausgang*/
9
PORTD=0b11111111;
10
11
TCCR0|=(1<<CS01)|(1<<CS11);
12
TIMSK|=(1<<TOIE0);
13
14
sei();
15
16
while(1)
17
{
18
for(i=0;i<10000;++i)
19
{
20
toDisplay(i);
21
_delay_ms(10);
22
}
23
}
24
}
das muss erst mal eine saubere Ansteuerung der 7-Seg ergeben. In 10
Sekunden rauschen da alle Zahlen von 0000 bis 9999 durch. Und dabei darf
es keine Fehler geben.
Und erst dann, wenn das getestet ist, geht es mit dem Rest weiter!
Wenn sich dabei dann Fehler in der Darstellung zeigen, dann liegt der
Fehler bei den frisch hinzugekommenen Dingen. Denn der Multiplexcode ist
bereits getestet und für gut befunden worden. Du darfst nicht Symptome
und Ursachen verwechseln.
Da Du pro Digit ein Byte speicherst, kannst Du dort auch gleich den
7-Segmentcode ablegen, statt der Ziffer.
Dann muß der Timerinterrupt das nicht ständig neu aufdröseln.
Und wenn keine negativen Werte benötigt werden, nimm unsigned.
Bei signed gibt es Seiteneffekte, große positive Zahlen können kleine
negative werden und umgekehrt.
Auch werden einige Rechnungen aufwendiger, durch die zusätzliche
Vorzeichenbehandlung.
Warum nimmst Du nicht die UART?
Schau Dir mal atof() an.
Oder sscanf().
Peter
Guten Morgen,
zuersteinmal weiterhin danke für das Feedback, so jetzt arbeiten wir mal
die Posts ab:
Zum Thema: Funktioniert das Multiplexen/%10;
Ja tut es, habe es vorher schon einmal so ausprobiert und gerade eben
noch einmal mit beiden Codevarianten, keinerlei Fehler.
Das in %10 keine Zahl größer 9 rauskommen kann ist mir bewusst. Deshalb
bin ich ja hier :)
Zu UART:
Habe ich mich bis heute noch nicht wirklich mit auseinander gesetzt. Mag
viel eleganter sein, aber das Readchar() funktioniert definitiv und
liest auch das Protokoll richtig (getestet mit LED's die an-/ausgehen
wenn er gleich bleibende Protokollabschnitte erkennt). Werde mich sicher
noch damit beschäftigen, aber da dieser Teil des Codes nicht das Problem
ist eher später.
Zu ungenaue Zeitmessung:
Ist mir voll bewusst, das soll und wird noch auf den Timer1 umgestellt,
kann den ja soweit ich das verstanden habe über das TCNT1 Register
auslesen.
Zu switch case:
Kenn ich, hatte ich, hab ich ersetzt um zu gucken ob da der Fehler
liegt. Und jetzt hat mich Karl Heinz auf eine viel elegantere Lösung
gestoßen die mir so erstmal nicht in den Kopf gekommen ist. Werde also
am Ende diese verwenden.
Fragen:
Das (1<<pos) böse ist hab ich schon im Programmierkurs gelernt, wie
würdet ihr das ersetzen?
Und welche Codeausschnitte kommen eurer Meinung nach noch für die
Angezeigten E's in Frage?
Gruß Florian
Florian T. schrieb:> Guten Morgen,> zuersteinmal weiterhin danke für das Feedback, so jetzt arbeiten wir mal> die Posts ab:>> Zum Thema: Funktioniert das Multiplexen/%10;> Ja tut es, habe es vorher schon einmal so ausprobiert und gerade eben> noch einmal mit beiden Codevarianten, keinerlei Fehler.> Das in %10 keine Zahl größer 9 rauskommen kann ist mir bewusst. Deshalb> bin ich ja hier :)
Also. Was sagt dir das?
Mir sagt es, dass der Fehler nicht im Multiplexteil liegt sondern in dem
... wie soll ich es nur nennen .... Programmteil in der Hauptschleife.
> Habe ich mich bis heute noch nicht wirklich mit auseinander gesetzt.
Dann wirds Zeit
> Mag> viel eleganter sein, aber das Readchar() funktioniert definitiv und> liest auch das Protokoll richtig (getestet mit LED's die an-/ausgehen> wenn er gleich bleibende Protokollabschnitte erkennt).
Das readchar stelle ich nicht in Frage.
Aber kein Mensch sagt, dass man erst mal die kompletten Zeichen in ein
riesen Array stopfen muss, nur damit man die Teile rauskriegt die man
haben will. Das kann man alles auch 'on-the-fly' machen.
Ein Datensatz beginnt damit, dass ein '/' reinkommt. Das ist dein
Startsignsal. Alles davor ist uninteressant.
Dann wird die Anzahl der '(' (und nur die!) mitgezählt.
Bei der 2.ten schaltet sich ein Modul dazu, welches das eingehende
Zeichen auf die erste Variable aufrechnet:
Für Vorkomma
Zahl = Zahl * 10 + ( Zeichen - '0' );
Für Nachkomma
Zahl = Zahl + ( Zeichen - '0' ) * Zahlenbasis;
Zahlenbasis /= 10;
und wenn die ')' daher kommt, dann schaltet sich dieser Programmteil
wieder ab.
Dasselbe wenn die 3-te '(' eintrudelt.
Wird irgendwann von der UART ein '!' gelesen, dann werden die gelesenen
Zahlen umgerechnet und zur Anzeige gebracht.
Kein Mensch muss dazu den kompletten String in einem 500-er Array
zwischenspeichern.
Es reicht völlig, wenn man das jeweils nächste Zeichen holt und anhand
der 'Umgebung' entscheidet, was damit zu tun ist.
> Das (1<<pos) böse ist hab ich schon im Programmierkurs gelernt, wie> würdet ihr das ersetzen?
entweder:
* weiteres Array, welches das auszugebende Byte liefert, wenn man
mittels
pos indiziert. Das wäre die allgemeine Lösung
* Da aber bei dir die Pins sowieso durchlaufend aufsteigend angeordnet
sind:
Mit einer Maske, die bei jedem Aufruf um 1 Stelle geschoben wird und
wenn pos auf 0 geht, wird auch die Maske wieder auf Ausgangsstellung
gesetzt.
1
uint8_tmultiPinMask=0x01;
2
3
ISR(TIMER0_OVF_vect)
4
{
5
PORTC=0;
6
PORTD=display[pos];
7
PORTC|=multiPinMask;
8
9
// Bit 7 dimmen, je nachdem ob einspeisen bzw powered 1 sind ergibt das
10
// unterschiedliche Dimm-Stufen
11
if(pos==0&&einspeisen==1)
12
PORTD&=~(1<<7);
13
if(pos==1&&powered==1)
14
PORTD&=~(1<<7);
15
16
pos++;
17
multiPinMask<<=1;
18
if(pos>3)
19
{
20
pos=0;
21
multiPinMask=0x01;
22
}
23
}
> Ist mir voll bewusst, das soll und wird noch auf den Timer1 umgestellt,
Du hast doch schon einen Timer laufen!
Den kannst du doch benutzen. Sagt doch keiner, dass ein Timer nur eine
Aufgabe machen darf.
Du weißt, in welchen Zeitabständen dir ISR aufgerufen wird. Wenn du dort
in der ISR also einfach die Anzahl der Aufrufe mitzählst, dann ist das
eine einfache Rechung, wie man eine vergangene Zeitdauer feststellt.
Liest du von der UART ein '\', dann liest du diesen Zeit-Zähler aus und
setzt ihn wieder auf 0.
> Und welche Codeausschnitte kommen eurer Meinung nach noch für die> Angezeigten E's in Frage?
der ganze Rest in main().
Irgendwo bügelst du dir den Speicher nieder.
Wirf es raus und bau das nochmal neu auf.
Aber achte diesmal ein bischen mehr auf Lesbarkeit!
Und schreib nicht alles in einem Zug durch, sondern teste immer wieder
Zwischenresultate! Mit zuviel Code auf einmal stehst du am Ende mit
einem Programm da, welches nicht funktioniert und du hast keine Ahnung,
wo das Problem liegt. Äh, den Zustand hast du ja jetzt auch schon.
Und die Sache mit den float solltest du dir in einer ruhigen Stunde
nochmal durch den Kopf gehen lassen. Das ist eine nicht unwesentliche
Belastung für den µC!
Anstelle von Gleitkomma und Euros kann man nämlich oft auch in Cent
rechnen und dafür mit ganzen Zahlen.
Ob man €2.80 plus €3.40 mittels GLeitkomma rechnet und €6.20
rausbekommt, oder ob man 280Cent + 340Cent rechnet und 620Cent
rausbekommt, ist vom Ergebnis her egal. Aber der Aufwand ist ein völlig
anderer.
Die UART hat den Vorteil, daß sie bis zu 3 Byte puffert. Du kannst also
im Hintergrund ständig einlesen.
Und den Sender kannst Du für Debugausgaben benutzen. Das ist
komfortabler als nur die 7-Segment.
Du kannst Dir dann Texte und Zahlenwerte ausgeben lassen.
Peter
Vermelde Erfolg, lag weder an den floats noch an irgend einem anderen
Codeausschnitt sondern am verwendeten Datentyp für das Display-Array.
Bei der von dir toDisplay genannten Funktion kommt es beim aufteilen der
Zahl bei verwendung von unsigned 16 Bit Integern zum Overflow, die
Verwendung von unsigned long Variablen also 32 Bit Integern hat das
Problem gelöst, keine E's mehr. Lohnt also ab und zu doch mit Datentypen
großzügig rumzumachen ;)
Es wurden also tatsächlich zu große Werte durch %10 in das Array
geschrieben, von char auf unsigned int zu gehen hat einfach nicht
gereicht.
Rausbekommen hab ich es letztendlich indem ich den ganzen Code auf
Standard-C umgeschrieben habe, sodass ich mit Eclipse anständig debuggen
konnte und siehe da, je nach verwendetem Datentyp steht in dem Array
totaler Schwachfug.
Ist zwar ziemlich blöd, dass ich jetzt um Zahlen von 0-9 zu speichern n
32 Bit Integer verwende, aber es funktioniert jetzt :)
Denke man kann das array auch wieder auf char umstellen, dafür musste
man in der toDisplay Funktion dann erst auf 32 Bit gehen und dann wenn
man die einzelne Zahl hat auf den Char/unsigned Char.
Fürs nächste mal hier posten merk ich mir dann noch:
genauer sagen was ich schon probiert hab, viele der angesprochenen
Sachen habe ich nämlich schon probiert/gehabt, dann aber zur Fehlersuche
ersetzt.
Der Code wurde auch nicht am Stück geschrieben sondern in kleinen
Häppchen, zuerst das Readchar(), was erstmal gar nicht funktionierte,
also musste n Quarz her, danach kam dann die Auswertung dazu und zum
Schluss das Problemkind/die Anzeige.
Nochmal danke an alle Beteiligten, auch wenn ich hier eher
Codeverbesserungen als Problemlösungen mitgenommen hab, sowas kann man
ja aber auch immer brauchen und verhindert hoffentlich Fehler in der
Richtung für spätere Projekte.
Gruß Florian
Edit: Hallo Peter,
ich habe deinen Post grade erst gesehen, hab mich eben etwas in den UART
eingelesen, ist schon ne feine Sache. Wenn ich das allerdings richtig
sehe frisst der PINs D0 und D1, weshalb ich dann meinen gesamten
Ausgabeteil übern Haufen werfen müsste, da ich die Segment Anzeigen auf
mehrere Ports verteilen müsste. Die Ausgabe wäre dann wohl nur noch
häßlich zu implementieren. Für weitere Projekte werd ich das wohl
nutzen, für das aktuelle wird mein readchar() herhalten.
> Zahl bei verwendung von unsigned 16 Bit Integern zum Overflow,> die Verwendung von unsigned long Variablen also 32 Bit Integern hat> das Problem gelöst, keine E's mehr. Lohnt also ab und zu doch mit> Datentypen großzügig rumzumachen ;)
Das klingt nach einer völlig falschen Diagnose.
Selbst wenn dein float zu gross ist, kann es nicht passieren, dass bei
irgendeinem uint16_t bei der Rechnung wert%10 etwas ausserhalb des
Bereichs 0 bis 9 rauskommt.
Egal was bei der Konvertierung
uint16_t iWert = (uint16_t)( wert + 0.5 ); // Annahme: wert kann nur
positiv sein
tatsächlich für ein Wert rauskommt, wird es immer so sein, dass du die 4
niederwertigsten Stellen dezimal mittels
for( i = 0; i < 4; i++ )
{
display[i] = SegCodes[ iWert % 10 ];
iWert /= 10;
}
absplitten kannst und kein einziges der Digits wird ausserhalb des
Bereichs 0 bis 9 sein.
Ob 4 Digits für die Darstellung der Zahl ausreichen ist eine andere
Geschichte. Aber die Digits sind auf jeden Fall im Wertebereich korrekt.
Es sei denn du lebst in einem anderen Universum, in dem eine andere
Mathematik gilt.
Zeig doch mal deinen Code, wie er jetzt aussieht. Die Vermutung steht,
dass du den Fehler nur kaschiert hast, aber nicht behoben.
> display[i]=(char)wert%10;
Das castet zuerst den Wert nach char und bestimmt erst dann den Rest.
> Ich bekomme aber: 123251
Wie kannst du eine 6-stellige Ausgabe bekommen, wenn du nur 4 Digits
hast?
Karl Heinz Buchegger schrieb:>> display[i]=(char)wert%10;>> Das castet zuerst den Wert nach char und bestimmt erst dann den Rest.
Lass den Cast weg!
Du brauchst ihn nicht und er ist dein Problem!
In Zukunft:
plain vanilla char verwendest du ausschliesslich nur dann, wenn du
Textverarbeitung machst!
Ansonsten benutzt du IMMER entweder
signed char wenn du einen kleinen Integer mit Vorzeichen
brauchst
unsigned char wenn du einen kleinen Integer ohne Vorzeichen
brauchst.
Und zwar IMMER!
Und am besten gewöhnst du dich daran, die Datentypen aus stdint.h zu
benutzen:
char für alles was Textverarbeitung ist
int8_t für einen kleinen Integer mit Vorzeichen
uint8_t für einen kleinen Integer ohne Vorzeichen
> Hieraus schließe ich, dass der Modulo Operator bei einem Overflow> nicht korrekt funktioniert
Deine Schlussfolgerung ist falsch.
Die richtige Schlussfolgerung ist:
auf deinem System ist ein char ein signed char
die Konvertierung von Wert in einen signed char ergab ein
negatives Ergebnis. Die Operation i%10 ergibt bei einem negativen
i ein negatives Ergebnis.
Dein vorsorglicher Cast war das Problem.
Du willst niemals Casten, wenn du nicht musst. Casts sind Waffen!
Warum hast du die Funktion nicht einfach so übernommen, wie ich sie dir
geschrieben habe? Buchstabe für Buchstabe?
Ich hab mir schon was dabei gedacht, als ich sie geschrieben habe.
Florian T. schrieb:> Nutze ich 32 bit Integer (in meinem Fall einfaches int, benutze Cygwin)
Beim AVR ist int nicht 32bit, sondern signed 16bit!
Und wie gesagt, signed hat Seiteneffekte, also nur dort einsetzten, wo
nötig!
Besonders lustig: (signed_var % 10) kann auch negativ werden.
Peter
Indem einige Einträge des Arrays nach der %10-Operation größer 10 sind.
Die einzelnen Array Einträge sind: 1, 2, 3, 251.
Bei der Behandlung der Einer-Stelle versagt also der Modulo Operator.
Wahlweise leben natürlich auch die CPU's meines PC's und Laptop's in
Welten mit anderer Mathematik.
Nehme ich als Startwert 6543.16789, erhalte ich als Ausgabe für die
einzelnen Stellen folgende Ausgabewerte: 6, 5, 252, 253.
Für 9500.16789 erhalte ich: 9, 5, 250, 6
Jeweils auf 2 verschiedenen Rechnern.
Im Anhang der aktuelle uC Code (einzige wirkliche Änderung die
Problemmäßig etwas geändert hat ist der Wechsel auf unsigned long für
display[])
Florian T. schrieb:> Bei der Behandlung der Einer-Stelle versagt also der Modulo Operator.
Vielleicht hast du es nur noch nicht gelesen.
Er versagt eben nicht. Er macht genau das, was er machen soll.
Nur deine Verwendung der Datentypen bzw. unnötiger Casts hat das Problem
hervorgerufen.
> Im Anhang der aktuelle uC Code (einzige wirkliche Änderung die> Problemmäßig etwas geändert hat ist der Wechsel auf unsigned long für> display[])
Hat sich ja mitlerweile geklärt.
Du hast bei der Übernahme der Funktion rumgepfuscht und dir eine Fehler
durch den Austausch der Datentypen eingehandelt.
-> Datentypen eben doch nicht auf die leichte Schulter nehmen, wie du
das momentan machst.
Florian T. schrieb:> Die einzelnen Array Einträge sind: 1, 2, 3, 251.> Bei der Behandlung der Einer-Stelle versagt also der Modulo Operator.
Nö.
Als char ist es nicht 251, sondern -5, der Modulo Operator arbeitet
völlig korrekt.
Dein Problem ist, char zu nehmen, was per default signed ist.
Peter
Okay Fehler IST der Typecast, war mir nicht bewusst, dass char und
unsigned char was anderes ist, hatte im Hinterkopf, dass beides von 0
bis 255 geht, ist ein Fehler der mir unter Garantie nicht nochmal
passieren wird.
Code gerade angepasst runter in Keller gerannt und ausprobiert: Keine
E's mehr. Aber zu große Zahlen, da bin ich mir aber sicher woher es
kommt: ungenaue Zeitmessung, teilen durch Wert kleiner 1, dadurch werden
die Zahlen größer.
Florian T. schrieb:> Okay Fehler IST der Typecast, war mir nicht bewusst, dass char und> unsigned char was anderes ist
Auch das stimmt so nicht.
Ob ein "char" als "signed char" oder als "unsigned char" angesehen
wird, entscheidet der Compiler bzw. der Compilerbauer!
Und oft genug kann man beim Aufruf des Compilers das auch noch
überstimmen.
Daher: Du kannst dich bei char weder darauf verlassen, dass er ein
Vorzeichen hat, noch kannst du dich darauf verlassen, dass er keines
hat.
Daher: "char" ausschliesslich und immer nur für Textverarbeitung
einsetzen! Also in Fällen, wo es keine Rolle spielt.
In allen anderen Fällen IMMER entweder "signed char" oder "unsigned
char". Oder eben int8_t bzw. uint8_t (welches konsequent benutzt die
bessere Alternative ist)
> bis 255 geht, ist ein Fehler der mir unter Garantie nicht nochmal> passieren wird.
So soll es sein.
Mach dir nichts draus. Das wird nicht der letzte Fehler bleiben und ja,
das gehört genauso zum Lernprozess dazu. Wichtig ist, dass der Fehler
korrekt diagnostiziert wird und du auch das 'Problem' verstehst. Denn
mit einer falschen Diagnose und einem dubiosen 'der rechnet falsch' ist
niemandem geholfen.
Lernprozess oh ja... saß 3 Stunden dran um rauszukriegen warum er die
Daten nicht anständig erkennt,
Grund war, dass ich diese if-Zeile haben wollte
1
if(log[i]=='('||log[i]==')')
aber diese hatte
1
if(log[i]=='('||')')
wodurch er den Klammern Counter jedes mal erhöht hat egal was in Log[i]
drinstand.
Frustresistenz ist genug vorhanden^^ Meistens hat man dann das Verlangen
sich ein großes Buch vor den Kopf zu schlagen weil es ein so primitiver
Fehler war.
Ich mach mich jetzt ans Verbessern der Zeitmessung und danach wird noch
eingebaut, dass der Ausgabewert über mehrere Protokolle gemittelt werden
soll.
Florian T. schrieb:> Lernprozess oh ja... saß 3 Stunden dran um rauszukriegen warum er die> Daten nicht anständig erkennt,> Grund war, dass ich diese if-Zeile haben wollte>
1
>if(log[i]=='('||log[i]==')')
2
>
>> aber diese hatte>
1
>if(log[i]=='('||')')
2
>
> wodurch er den Klammern Counter jedes mal erhöht hat egal was in Log[i]> drinstand.
Tipp:
Seit du 6 Jahre alt bist, hast du dein Gehirn darauf trainiert, dass
beim Lesen zwischen Wörtern ein Leerraum steht. Dein Gehirn ist darauf
konditioniert und findet diese Leerräume mitlerweile problemlos, so dass
du beim Lesen eben nicht ständig die Buchstaben absuchen musst, wo denn
ein Wort aufhört und wo das nächste anfängt. D.h. du konzentrierst dich
komplett und ausschliesslich auf die Bedeutung der Wörter. Das Zerlegen
der Wörter geschieht für dich völlig unbewusst im Hintergrund. Und
probiers doch mal aus,
umwievielschwererdieserSatzzulesenist,wennmanneinfachalleZwischenräumewe
glässt.
Es ist daher unklug, in einer Programmiersprache diesen Automatismus
dadurch lahmzulegen, indem man Zeichen auf Zeichen, knirsch an knirsch
schreibt. Selbst dann, wenn es dir die Programmiersprache erlaubt!
1
if(log[i]=='('||')')
Und hier fällt viel eher auf, dass da eben nicht 2 Vergleiche stehen,
die mit einem logischen Oder verknüpft sind.