Hallo!
Versuche gerade mit meinem ATmega32 per ADC Strom und Spannung zu
messen.
Dabei ist es zur Berechnung wegen der erforderlichen Genauigkeit
notwendig, daß ich eine 32bit Multiplikation durchführe.
Seltsamerweise ergibt diese Multiplikatione zweier uint32-Zahlen ein
negatives Ergebnis. Zuerst dachte ich an einen Überlauf, diesen kann ich
aber ausschließen. Hat jemand von euch eine Idee, woran das liegen kann?
MC: ATmega32
IDE: AVR-Studio 5
In Main:
1
uint32_tvoltage_source=0;// Variable für Quellenspannung
Ich habe mir folgende Variablen am LCD ausgeben lassen:
K_VOLTAGE = 52 (korekt)
adc_val = 900 (korrekt)
voltage_electrodes = -18736 ??? falsch ???
Nach meinem Wissen genügt es bei einer Rechenoperation eine Zahl auf
32bit zu casten, damit der Compiler alles mit 32bit rechnet (Artikel:
Festkommaarithmetik)
Da ich alle Konstanten zur Berechnung der Variable K_VOLTAGE mit L (Long
= 32bit) caste wird der ADC-Wert "adc_val" auch automatisch auf 32bit
vergrößert.
Die Multiplikation lautet dann uitn32 = uint32 * uint32
-> -> -18736 = 900 * 52
Wie kann dann das Ergebnis negativ werden?
Was mich weiters verwirrt ist, daß in den Artikeln bei den 8bit-MC int
immer als 16bit angenommen wird und long int mit 32bit.
Müßte bei einer 8bit-Plattform int nicht als 8bit gelten und long int
als 16bit?
Gruß Rick
Wie kommst du auf den negativen Wert?
Die Variable hat einen unsigned Datentyp, da kann garnichts negatives
drin stehen. Wenn man aber das Ergebnis von 900*50 als signed int16
betrachtet dann stimmt deine negative Zahl. Die Rechnung stimmt also.
Ich würde also ehr sagen dass du den Wert falsch ausliest.
Rick M. schrieb:> Müßte bei einer 8bit-Plattform int nicht als 8bit gelten und long int> als 16bit?
Nein, denn der C Standard schreib vor, dass ein int mindesten 16 Bit
groß ein muss.
Michael Köhler schrieb:> Die Multiplikation zweier 32bit-Zahlen ergibt eine 64bit-Zahl. Kanns> sein, dass dein Problem hier her kommt? ;)
900*52 liegt doch locker im 32bit-Bereich.
Hubu schrieb:> Die LCD-Ausgabefunktion erwartet offenbar eine signed int Variable als> Übergabeparameter. Dann kommt bei 900*52 genau der Wert raus.
Die Ausgabe am LCD mache ich wie folgt:
1
charstring[10];
2
3
lcd_gotoxy(0,3);
4
itoa(voltage_electrodes,string,10);
5
lcd_puts(string);
Für die Ausgabe am Display (4*20 Zeichen mit Standard HD44780
Controller) benutze ich die Bibliotheken von Perter Fleury.
Gruß Rick
War nur meine erste Vermutung dass der Fehler daher kommt und der
Compiler versucht zu optimieren. Der weiß ja nicht wie groß das Ergebnis
wird ;)
Durchaus aber auch möglich, dass itoa einen 16 bit Integer nur erwartet
und du gibst ihm nen 32 bit. Um das raus zu finden lasse itoa einfach
mal einen 32 bit Integer (z.B. 80.000) umformen und schau ihn dir an ;)
@gerd auch das ist stringgenommen nicht richtig, denn ltoa ist auch
signed. Und ultoa ist kein C-Standard.
Ich würde sprintf nehmen, das ist eh sauberer.
Hubu schrieb:> Michael Köhler scheint scih weder die Frage noch die Antworten der> anderen anzugucken bevor er seinen Qurk in die Runde wirft.
itoa erwartet eine int, wie lang aber int ist ist vom System abhängig
und kann nicht pauschal gesagt werden. Es kann 16 bit sein aber auch 32
bit.
Stefan Ernst schrieb:> Hubu schrieb:>> Stefan Ernst schrieb:>>> utoa>>>> FALSCH. Siehe oben.>> Und was genau meinst du mit "oben"?
Ach, schon klar, uint32_t.
Habe nicht damit gerechnet, dass er nicht nur signed/unsigned außer Acht
gelassen hat, sondern auch die Größe.
(Trotzdem war es nicht unbedingt nötig, mich anzuschreien)
Die Variable "voltage_electrodes" wird mit der Umwandlungs-Funktion
ultoa() jetzt korrekt angezeigt.
46800 = 900 * 52
Danke!
Die Displayausgabe dieser Spannung ist jedoch immer noch falsch.
Der Wert 46800 entspricht 46,8V.
Angezeicht wird jedoch 46,32V ?
1
vorkomma=voltage_electrodes/1000;// Spannung wird im mV ausgegeben
2
if(vorkomma<10)// wenn Vorkomma kleiner 10 Leerstelle einfügen
3
lcd_putc(' ');
4
utoa(vorkomma,string,10);// Elektrodenspg. in ASCII umwandeln
Rick M. schrieb:> Der Wert 46800 entspricht 46,8V.> Angezeicht wird jedoch 46,32V ?
von welchem Datentyp ist Deine Variable "nachkomma"?
Ich vermute uint8_t, und das wäre zu klein für 800.
Wenn man versucht, 800 in einen uint8_t zu packen, kommt 32 heraus.
J.-u. G. schrieb:> Rick M. schrieb:>> Der Wert 46800 entspricht 46,8V.>> Angezeicht wird jedoch 46,32V ?>> von welchem Datentyp ist Deine Variable "nachkomma"?>> Ich vermute uint8_t, und das wäre zu klein für 800.>> Wenn man versucht, 800 in einen uint8_t zu packen, kommt 32 heraus.
Bin gerade selbst draufgekommen, Du hast voll ins Schwarze getroffen
;-)Jetzt klappts
Vielen Dank für eure schnelle Hilfe!
Gruß Rick
Rick M. schrieb:> J.-u. G. schrieb:>> Rick M. schrieb:>>> Der Wert 46800 entspricht 46,8V.>>> Angezeicht wird jedoch 46,32V ?>>>> von welchem Datentyp ist Deine Variable "nachkomma"?>>>> Ich vermute uint8_t, und das wäre zu klein für 800.>>>> Wenn man versucht, 800 in einen uint8_t zu packen, kommt 32 heraus.>> Bin gerade selbst draufgekommen, Du hast voll ins Schwarze getroffen> ;-)Jetzt klappts>
Noch nicht wirklich.
Denn wenn deine Anzeige eigentlich 42,008 sein sollte, steht auf deiner
Anzeige 42,8
Du hast die führenden 0-en vergessen. Im Vorkommaanteil spielen die
keine Rolle. Im Nachkommaanteil sind die aber wichtig, weil sich dadurch
die Aussage des Ergebnisses verändert.
Und damit sind wir dann schon in einem Bereich, in dem man darüber
nachdenken kann sprintf einzusetzen. Auch mit dem Hintergedanken, dass
es dann ein leichtes ist die Ausgabe in eine bestimmte Feldbreite zu
packen, was wiederrum dem Benutzerkomfort zugute kommt, weil die Ausgabe
auf einem LCD dann nicht ständig hin und her wandert, sondern das Komma
immer an der gleichen Stelle stehen bleibt.
Sicher, sprintf hat einen nicht unwesentlichen Flash-Speicherverbrauch.
Wenn das aber keine Rolle spielt und man seine Features nutzen kann, ist
es eine gute Wahl. Sofern man gelernt hat, was man mit dem Format-String
alles anfangen kann.
Karl Heinz Buchegger schrieb:> Denn wenn deine Anzeige eigentlich 42,008 sein sollte, steht auf deiner> Anzeige 42,8
Daran hab ich bisher noch gar nicht gedacht, stimmt.
Hab bisher herumgetüftelt, wie ich mir ein Unterprogramm schreibe, daß
auf 1ne oder 2 Nachkommastellen begrenzt und korrekt rundet.
Wollte die Fkt. "my_round" vom Artikel "Festkommaarithmetik benutzen.
Hab sie mir für meine Bedürfnisse adaptiert, leider funktionierts nicht
richtig und ausserdem ist mir die Vorgehensweise zu komplex.
Für jede Rundung muß man Papier und Bleistift zur Hand nehmen und sich
erst mal anschauen welche Indexmarken man setzen muß.
Die Fkt. sprintf ist dann natürlich sehr komfortabel, aber ich dachte
für einen MC ist diese Fkt. nicht brauchbar, da sie viel zu viele
Resourcen (CPU + Flash)verbraucht.
Daß es aber anscheinend doch praktikabel ist, freut mich, da ich n
Problem weniger zum Lösen habe.
Ein vorher/nachher Vergelich zeigt mir ja den zusätzl. Speicherverbauch.
Gruß Rick
Soweit ich mich jetzt schlau gemacht habe bedeutet:
%4d: d steht für dezimal, ein i für integer oder speziell für mich ein u
für unsigned integer würde auch gehen.
Da ich maximal nur 2 Vorkomma-Stellen habe, kann ich auch %2d, %2i oder
%2u verwenden.
%03d: die führende Null bedeutet, daß anstelle von Leerstellen mit
Nullen aufgefüllt wird. Also anstelle von 32, 8 wird 32,008 ausgegeben.
Da ich bei der Spannung nur 1ne Nachkommastelle möchte reicht %1d bei 2
Nachkommastellen müsste ich dann %02d schreiben.
Richtig gerundet wird hier aber nicht, oder? Jedenfalls weiß ich nur
davon, wenn man mit dem Typ float arbeitet %.2f
Gruß Rick
Rick M. schrieb:> Nullen aufgefüllt wird. Also anstelle von 32, 8 wird 32,008 ausgegeben.> Da ich bei der Spannung nur 1ne Nachkommastelle möchte
Warum "modulierst" du dann durch 1000?
Da musst du dir noch was einfallen lassen.
> Richtig gerundet wird hier aber nicht, oder?
runden musst du selber.
Aber Vorsicht, dass machst du im Idealfall schon bei voltage_electrodes.
Deine eine Rundung in den Nachkommastellen, kann sich auch auf den
Vorkommaanteil auswirken :-)
429999 also 42,999
gerundet ergibt
430000 also 43,000
> da sie viel zu viele Resourcen (CPU + Flash)verbraucht.
definiere 'viel zu viel'
Für nicht verbrauchten Flash-Speicher kriegst du von Atmel kein Geld
zurück.
Karl Heinz Buchegger schrieb:> Warum "modulierst" du dann durch 1000?> Da musst du dir noch was einfallen lassen.
modulieren?
Hab meine Info von hier: (Flags)
http://home.htw-berlin.de/~junghans/cref/FUNCTIONS/format.html
Vielleicht hab ich da auch was falsch vertanden:
Zitat:" 0 Felder mit 0 ausfüllen (an Stelle von Leerzeichen). "
Karl Heinz Buchegger schrieb:> runden musst du selber.> Aber Vorsicht, dass machst du im Idealfall schon bei voltage_electrodes.> Deine eine Rundung in den Nachkommastellen, kann sich auch auf den> Vorkommaanteil auswirken :-)>> 429999 also 42,999>> gerundet ergibt>> 430000 also 43,000
Womit ich wieder am Anfang bin :-(
Hab mal ausprobiert, wieviel sprintf "schluckt".
Für diese 1ne Zeile werden 1440bytes mehr vergraucht. Ganz schön happig.
Gruß Rick
Übrigens ist das hier
#define K_VOLTAGE (VAREF * K / 1024) // Ergebnis: 52
voltage_electrodes = ( adc_val * K_VOLTAGE ); // 900*52
nicht wirklich schlau.,
Nach der Expansion steht da
adc_val * (2130L * 25L / 1024)
das bedeutet aber auch, dass der Klammerausdruck zuerst ausgewertet
wird.
Er ergibt 52.00195, also keine ganze Zahl, wird aber als ganze Zahl
berechnet. Und erst mit dieser ganzen Zahl wird dann dein adc_val
multipliziert. Du hast also in deiner Multiplikation einen maximalen
Fehler von 1023 (weil adc_val nicht größer werden kann) * 0.00195, oder
ausgerechnet 1.99
Multiplizierst du aber zuerst alles aus und dividierst erst ganz zum
Schluss, dann hast du diesen Fehler nicht.
Du möchtest das also so berechnen
1
adc_val * 2130L * 25L / 1024
Divisionen immer so weit wie möglich in der Berechnung nach hinten
schieben. Einzige Ausnahme: Wenn Überlauf droht.
Inwiefern dich dieser Fehler jetzt stört, musst du entscheiden. Ich
halte es auch ein wenig sinnfrei, Millivolt auszurechnen, wenn du dann
sowieso nur Zehntelvolt ausgibst.
Ich hab das so gemacht, damit der MC nicht immer 2 Multiplikationen und
eine Division durchführen muß, sondern nur eine Multiplikation.
Steh da momentan auf n Schlauch, da der Fehler zwischen 52 und 52,00195
bei 0,2% liegt.
Hab mich da an den Artikel "Festkommaarithmetik" gehalten.
Stichwort "Korrekturfaktor" Ich rechne ja in mV!
Der maximale Fehler beträgt also 2mV und das ist akzeptabel.
Aus demselben Grund multilpiziere ich bei der Strommessung auch mit
1000000 und rechne in uA.
Gruß Rick