Hi, ich hoffe mir kann hier jemand weiterhelfen, verstehe die Rechnung
des unten stehenen Codes nicht.
Angenommen mein ADC liefert mir einen Wert von 20.
dann sieht die Rechnung doch so aus:
x=4.8876*20; -> 97,752 (x kann keine Kommazahlen da int, also bleibt 97,
oder?)
vorkomma = x/1000 -> 97/1000 = 0,97 (... schon wieder eine Kommazahl,
wird daraus nun eine 0 oder eine 1?)
Ich denke weiter brauch ich nicht rechnen, ich komm einfach nicht drauf
wie hier mit int. Typen gerechnet wird?!
Gleich vorweg, in der praxis läuft es! :) Ich würde nur gerne verstehen
wie und warum?! :)
1
#include<avr/io.h>
2
#include<stdio.h>
3
#include<inttypes.h>
4
5
#define F_CPU 1000000 // CPU-Frequenz in Hz
6
#include<util/delay.h>
7
8
#include"messen.h"
9
#include"lcd-routines.h"
10
11
12
intmain(void){
13
intx=0,vorkomma,nachkomma;
14
charmesswertstring[20];
15
16
DDRB|=_BV(PB0);PORTB&=~_BV(PB0);// PB0 ist Status-LED Ausgang
Matthias M. schrieb:> Angenommen mein ADC liefert mir einen Wert von 20.>> dann sieht die Rechnung doch so aus:>> x=4.8876*20; -> 97,752 (x kann keine Kommazahlen da int, also bleibt 97,> oder?)
woher sollen wir denn wissen was 4.8876 ist?
Wenn du 0.3 Volt haben willst, dann denke einfach ein milliVolt. Dann
sind es 300mV. Und schon passt es in eine Int zahl.
Hallo,
das passiert hier schon. Hier wird in mV gerechnet:
5V / 1023 = 0,00488... (Annahme: 5V - Referenzspannung)
Das ist im Code 4,8876
Hoffe es ist jetzt etwas klarer geworden.
Gruß
BT
Ich gehe mal davon aus, dass MESSWERT() einen int zurückliefert.
Dein Compiler macht aus dem 4.8876 wahrscheinlich einfach eine 4.
Danach landet in vorkomma je nach messwert ein 250stel des Messwertes
(Nachkommastellen werden abgeschnitten).
Messwert 20 --> x = 80 --> vorkomma = 0, nachkomma = 8
Messwert 250 --> x = 1000 --> vorkomma = 1, nachkomma = 0
Messwert 345 --> x = 1380 --> vorkomma = 1, nachkomma = 38
Sorry, die 4.88... werden doch nicht zum int gecastet... also wird da
böserweise sogar eine Float-Multiplikation gemacht.
Also:
Messwert 20 --> x = 97 --> vorkomma = 0, nachkomma = 9
...
Martin schrieb:> Dein Compiler macht aus dem 4.8876 wahrscheinlich einfach eine 4.
Ok das dacht ich mir eben dass da was nicht stimmt, ich muss auch sagen
dass die Messergebnisse schon etwas ungenau sind.
sollte ich x dann besser zum Datentyp double machen?
Matthias M. schrieb:> sollte ich x dann besser zum Datentyp double machen?
Nein, du sollst einfach in mV rechnen und bei int bleiben. Dann ist
alles ausreichend genau und trotzdem schön flott und der code bleibt
klein.
Eumel schrieb:> Nein, du sollst einfach in mV rechnen und bei int bleiben. Dann ist> alles ausreichend genau und trotzdem schön flott und der code bleibt> klein.
Hmm aber sobald ich mit 4,8876 multipliziere verwende ich doch eine
Kommazahl und kann mit int nicht rechnen. Oder sollte ich mit 48876
multiplizieren? Dann wäre aber doch der Beispielcode falsch, oder?
Matthias M. schrieb:> Hmm aber sobald ich mit 4,8876 multipliziere verwende ich doch eine> Kommazahl und kann mit int nicht rechnen. Oder sollte ich mit 48876> multiplizieren? Dann wäre aber doch der Beispielcode falsch, oder?
Dann schieb das Komma halt noch ein paar Stellen nach rechts bis es dir
genau genug ist:
http://www.mikrocontroller.net/articles/Festkommaarithmetik
Matthias M. schrieb:> Hmm aber sobald ich mit 4,8876 multipliziere verwende ich doch eine> Kommazahl und kann mit int nicht rechnen. Oder sollte ich mit 48876> multiplizieren? Dann wäre aber doch der Beispielcode falsch, oder?
Das ist er sowieso, weil die 4.8876 an sich schon falsch sind.
Wo kommt die Zahl den eigentlich her?
Sie entsteht aus 5 / 1024
Warum 5?
Weil deine Referenzspannung 5V ist.
Und warum 1024?
Weil der ADC den Bereich 0 bis Referenzspannung (die 5V) in 1024 Stufen
(Bereiche) auflöst.
Ein derartiger Bereich ist daher 5/1024 oder eben 0.00488828125 V groß.
Die Zahl weicht deshalb ab, weil der Originalautor nicht mit 1024,
sondern mit 1023 gerechnet hat. Was aber eigentlich nicht ganz richtig
ist (allerdings auch keinen großen Unterschied macht).
ANstatt
1
x = 0.0048828 * Messwert * 1000.0;
hindert dich nichts und niemand, stattdessen
1
x = 4.8828 * Messwert;
zu schreiben. (Die 1000 kommen daher, weil du ja das Ergebnis in
Millivolt haben willst und nicht in Volt)
Es hindert dich allerdings auch nichts und niemand, anstatt der
0.0048828 den Ausdruck zu schreiben, aus dem der Wert entstanden ist
1
x = 5 / 1024 * Messwert * 1000;
Allerdings sollte man ihn etwas umformen, damit die DIvision zum Schluss
gemacht wird und nicht zwischendurch der Nachkommaanteil "verloren geht"
1
x = 5 * Messwert * 1000 / 1024;
Jetzt kann man noch die 5 und die 1000 zusammenfassen und landet bei
1
x = 5000 * Messwert / 1024;
und hat damit einen Ausdruck, der komplett ohne Floating Point
Arithmetik auskommt. Einfach nur indem man ein wenig darüber nachdenkt,
wo die Zahlen eigentlich herkommen und indem man ein wenig umformt.
Ein (potentielles) Problem gibt es noch, dem man sich widmen muss. Und
das ist ein potentieller Overflow. Wird im Zahlenraum int gerechnet (16
Bit), dann darf kein Ergebnis, auch kein Zwischenergebnis größer als
32767 sein. Rechnet man unsigned (also ohne Vorzeichen), dann liegt die
Grenze bei 65535.
Also sehen wir uns das mal näher an.
Einen Overflow kann es nur bei der Multiplikation geben, denn nur dort
kommen ja größere Ergebnisse raus.
1
5000 * Messwert
Wie groß kann Messwert maximal werden?
Messwert kann Werte im Bereich 0 bis 1023 annehmen. Größer geht nicht.
Die größte Zahl die da also entstehen kann, lautet daher
1
5000 * 1023
und das ist ausgerechnet 5115000, welches wiederrum zu groß für ein 16
Bit Ergebnis ist. Man könnte jetzt natürlich den Faktor 1000
entsprechend kleiner machen und so von Millivolt auf Hundertselvolt oder
gar Zehntelvolt gehen. Man kann aber auch einfach dafür sorgen, dass
diese Berechnung nicht in 16 Bit sondern in 32 Bit gemacht wird. Dann
passt es wieder und es entsteht kein Overflow
Und so landen wir bei
sowieso schon sprintf benutzt, dann kannst du dir (und dem Rechner) auch
eine Menge Arbeit sparen, indem du das benutzt, was dir sprintf an
Formatiermöglichkeiten anbietet. Es ist ein bischen witzlos, das ganze
aus Einzelteilen am LCD zusammenzusetzen und sich dann auch noch selbst
um die notwendigen führenden 0-en kümmern zu müssen, wenn sprintf das
alles auch mit dem richtigen Formatierstring ganz alleine kann.
@Karl Heinz Buchegger: DANKE!!! Schade dass das so nicht auch im
Tutorial steht, echt super schön erklärt!
Eine Frage bleibt mir allerdings, Variable x (als int.) wird ja bei der
Rechnerei zwarngsläuftig eine Kommazahl. Das Komma wird dann einfach
verworfen, richtig?
Sprich bei Messert 450 des ADC ergibt sich für x = 2197,265625. Werden
die Nachkommastellen dann einfach ignoriert oder passiert da noch
irgendwas anderes? Wäre es nicht besser das direkt zu verhindern dass
erst gar keine Nachkommastellen entstehen, z.b. durch eine Rundung oder
mach ich mir da zu viele Gedanken?
Matthias M. schrieb:> @Karl Heinz Buchegger: DANKE!!! Schade dass das so nicht auch im> Tutorial steht, echt super schön erklärt!>> Eine Frage bleibt mir allerdings, Variable x (als int.) wird ja bei der> Rechnerei zwarngsläuftig eine Kommazahl.
Nein.
Eine Variable hat einen Datentyp. Und der ändert sich nicht.
In deinem Original
1
x=4.8876*MESSWERT(0);
hast du erst mal eine Zuweisung.
Und die besteht aus 2 Teilen:
Dem Teil links vom =
und dem Teil rechts vom =
der Teil rechts vom =, der arithmetische Ausdruck, wird ausgewertet und
kommt mit einem Ergebnis hoch. Dieses Ergebnis besteht aus 2 Teilen:
EInem Zahlenwert und einem Datentyp.
Der Ausdruck
1
4.8876*MESSWERT(0)
ergibt irgendeinen Zahlenwert (je nach Messwert), aber er ist auf jeden
Fall vom Datentyp 'double'. Das alles hat noch nichts mit dem x zu tun.
Das kommt erst jetzt ins Spiel, wenn dann die Zuweisung tatsächlich
durchgeführt wird. Dann stehen rund um das = die Datentypen
1
int = double
und bei einer derartigen Zuweisung werden die Nachkommastellen des
double einfach abgeschnitten und verworfen.
und damit bekommt dann x (der int auf der linken Seite der Zuweisung)
dieses bereinigte Ergebnis zugewiesen.
> Wäre es nicht besser das direkt zu verhindern dass erst gar> keine Nachkommastellen entstehen, z.b. durch eine Rundung> oder mach ich mir da zu viele Gedanken?
Du kannst natürlich runden wenn du willst. Allerdings bezweifle ich,
dass deine Referenzspannung genau genug ist, bzw. das ADC Ergebnis genau
genug ist, dass die Millivolt-Stelle überhaupt stimmt. Ob da also im x
jetzt 2197 oder 2198 steht, ist höchst wahrscheinlich Jacke wie Hose,
denn deine Spannung war mit einiger Sicherheit sowieso nicht 2.198V