Datum:
Hallo, ich habe folgendes Problem. Bei einer Stringformatierung verwende ich zum ermitteln der Nachkommastellen den Modulo-Operator. Das funktioniert auch soweit ganz prima mit uint16_t- Werten nur sobald ich das gleiche mit uint32_t-Werten mache erhalte ich vom Modulo immer eine Null :-( Beispiel wo es nicht funktioniert:
class Application : public Controller
{
protected: uint16_t analogwert;
protected: uint32_t spannung;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onWork()
{
// leer
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
//Volt=analogwert*VRef/Maximalwert
spannung=analogwert*5000/1023;
text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000);
konsole.sendString(text);
}
} app;
|
und hier das gleiche mit uint16_t was absolut prima läuft:
class Application : public Controller
{
protected: uint16_t analogwert, spannung;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onWork()
{
// leer
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
//Volt=analogwert*VRef/Maximalwert
spannung=analogwert*50/1023;
text.format("Spannung = %d,%d Volt \n",spannung/10,spannung%10);
konsole.sendString(text);
}
} app;
|
was mache ich falsch? :/ Grüße Jahat
Datum:
> spannung=analogwert*5000/1023;
Sobald dein analogwert größer als 6 ist, gibt das einen satten Überlauf.
6 * 5000 -> 30000 passt gerade noch in einen 16 Bit int
7 * 5000 -> 35000 passt nicht mehr in einen 16 Bit int
Datum:
Jahat Iblis schrieb: > text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000); bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist bei einem Integer eigentlich nicht sinnvoll. Ich würde da jetzt %03d schreiben.
Datum:
> spannung=analogwert*5000/1023; ist ausserdem falsch. /1024 ist richtig. Beitrag "Re: Berechnung zur Auflösung ADC - wie richtig?" Der Überlauf ist ein klassischer Fehler, siehe http://www.mikrocontroller.net/articles/Festkommaa... MfG Falk
Datum:
Karl Heinz Buchegger schrieb: >> spannung=analogwert*5000/1023; > > Sobald dein analogwert größer als 6 ist, gibt das einen satten Überlauf. > 6 * 5000 -> 30000 passt gerade noch in einen 16 Bit int > 7 * 5000 -> 35000 passt nicht mehr in einen 16 Bit int naja aber das Attribut spannung ist ja 32 bit und da sollte das Ergebnis locker rein passen ... ich schau mir noch mal den Artikel zu den Festkommazahlen an... danke erst mal. Klaus T. schrieb: >> text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000); > > bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist > bei einem Integer eigentlich nicht sinnvoll. Ich würde da jetzt %03d > schreiben. jup ... war ein Irrtum vom Amt sollte natürlich %03d heißen... ist ja keine Gleitkommazahl ;-) Gruß Jahat
Datum:
Jahat Iblis schrieb: > naja aber das Attribut spannung ist ja 32 bit und da sollte das Ergebnis > locker rein passen ... Standarddenkfehler. In a=b*c wird b*c unabhängig vom Typ von a berechnet.
Datum:
Klaus T. schrieb: > bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist > bei einem Integer eigentlich nicht sinnvoll. Doch. > Ich würde da jetzt %03d schreiben. Hat effektiv die gleiche Wirkung.
Datum:
Jahat Iblis schrieb: > .. > protected: String text; > .. > text.format("Spannung = %d,%d Volt \n",spannung/10,spannung%10); In welcher String-Klasse gibt es den die Methode format?
Datum:
ok, das mit dem Überlauf war mir schon irgendwie klar deshalb hab ich ja das Ergebnis auf 32Bit dimensioniert. Da aber offensichtlich auch kein 16Bit-Wert in der Rechnung selber sein darf hab ich jetzt mal alle Werte als 32 Bit deklariert und Kontrollausgaben der Zwischenergebnisse gemacht... es schaut aus als wenn Modulo bei uint32_t nicht funktioniert:
class Application : public Controller
{
protected: uint32_t analogwert;
protected: uint32_t spannung;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
text.format("Analogwert = %d \n",analogwert);
konsole.sendString(text);
spannung=analogwert*5000/1024;
text.format("Millivolt = %d \n",spannung);
konsole.sendString(text);
text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000);
konsole.sendString(text);
}
} app;
|
Ausgabe:
Analogwert = 1022 Millivolt = 4995 Spannung = 4,000 Analogwert = 914 Millivolt = 4467 Spannung = 4,000 Volt Analogwert = 790 Millivolt = 3861 Spannung = 3,000 Volt Analogwert = 666 Millivolt = 3255 Spannung = 3,000 ... usw. |
oder an welcher Stelle bin ich zu dusselig? In der Regel sitzt der Fehler ja immer vor der Tastatur :( Grüße Jahat Ach so der String ist irgenwie bei SiSy mit drin, bin dabei mich durch das AVR-C++ Tutorial zu kämpfen.
Datum:
@ Jahat Iblis (jaib) >das Ergebnis auf 32Bit dimensioniert. reicht nicht. > Da aber offensichtlich auch kein >16Bit-Wert in der Rechnung selber sein darf Doch, aber mindestens EINER muss 32 Bit sein, bei größeren Formeln, die der COmpiler häppchenweise bearbeitet ggf. mehrere! Im Zweifelsfall alle Eingangsvariablen in der Formel auf 32 Bit casten. >gemacht... es schaut aus als wenn Modulo bei uint32_t nicht >funktioniert: Glaub ich nicht. >oder an welcher Stelle bin ich zu dusselig? In der Regel sitzt der >Fehler ja immer vor der Tastatur :(
text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000); |
Ich glaub hier kann es knirschen, denn %d bezieht sich auf einen Integer, nicht long int. Eher so.
text.format("Spannung = %d,%03d Volt \n",spannung/1000,(uint16_t)(spannung%1000)); |
MfG Falk
Datum:
alles 32bit und casting beim format auf 16bit hab ich schon probiert... geht nicht und die folgende Zeile gibt ja den 32bit wert korrekt aus.. Jahat Iblis schrieb: > text.format("Millivolt = %d \n",spannung); also funktioniert %d mit uint32_t. wieso sollten 32 bit nicht reichen? 1023 * 5000 = 5115000 = 0x4E0C78 ... größer wird da ja nix übrigens so funktioniert es... aber eben Modulo auf 16 Bit
class Application : public Controller
{
protected: uint32_t analogwert;
protected: uint32_t rechnung;
protected: uint16_t ausgabe;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
rechnung=analogwert*5000/1023;
ausgabe=(uint16_t)rechnung;
text.format("Spannung = %d,%03d Volt \n",ausgabe/1000,ausgabe%1000);
konsole.sendString(text);
}
} app;
|
Das muss ich irgendwie erst mal verdauen... hab aber keinen plan wie ich jetzt mal feststelle ob Modulo mit uint32_t geht :/ Gruß Jahat
Datum:
Wenn Spannung ein uint32_t ist, dann kann es hier
text.format("Spannung = %d,%03d Volt \n",spannung/1000
nicht %d im Format String heissen.
%d ist für int. Du hast aber keinen int, du hast mit 32 Bit auf deinem
System offensichtlich einen long. Also muss es zumindest %ld heissen.
(UNd das das ganze eigentlich unsigned ist, lass ich jetzt mal mit einem
Augenzwinkern unter den Tisch fallen)
> jetzt mal feststelle ob Modulo mit uint32_t geht :/
Du bist immer noch am falschen Dampfer.
Gewöhn dir dieses Beschuldigen von Systemkomponenten ab. Zu 99% sitzt
das Problem immer vor den Bildschirm und es ist DEIN Programm, welches
fehlerhaft ist.
Datum:
hm... ok... wenn nicht %d was dann? :/ das Ergebnis der Operation passt definitiv in 16 Bit und hat so oder so nimmer ein Vorzeichen, die Division funktioniert problemlos so wie ich es gebaut hab ... :/ übrigens geh ich strikt davon aus, dass ICH was falsch mache nur versteh ich es noch nicht was :/ ... es scheint dann wohl doch beim format zu liegen der folgende Code
class Application : public Controller
{
protected: uint32_t wert;
protected: String text;
protected: Uart konsole;
public: void onStart()
{
konsole.config(9600);
wert=0xFFFFFFFF;
}
public: void onWork()
{
text.format("32bit Wert = %u / %d / %X \n",wert,wert,wert);
konsole.sendString(text);
waitMs(100);
wert--;
}
} app;
|
bringt dieses Ergebnis als Ausgabe:
32bit Wert = 65535 / -1 / FFFF 32bit Wert = 65534 / -1 / FFFE 32bit Wert = 65533 / -1 / FFFD 32bit Wert = 65532 / -1 / FFFC 32bit Wert = 65531 / -1 / FFFB 32bit Wert = 65530 / -1 / FFFA 32bit Wert = 65529 / -1 / FFF9 32bit Wert = 65528 / -1 / FFF8 32bit Wert = 65527 / -1 / FFF7 32bit Wert = 65526 / -1 / FFF6 32bit Wert = 65525 / -1 / FFF5 32bit Wert = 65524 / -1 / FFF4 32bit Wert = 65523 / -1 / FFF3 32bit Wert = 65522 / -1 / FFF2 32bit Wert = 65521 / -1 / FFF1 32bit Wert = 65520 / -1 / FFF0 32bit Wert = 65519 / -1 / FFEF 32bit Wert = 65518 / -1 / FFEE 32bit Wert = 65517 / -1 / FFED usw. |
ich muss dir also recht geben Karl Heinz die Formatanweisungen %u %d und %x beziehen sich auf 16Bit Werte ... jetzt muss ich nur noch mals das Modulo checken... Gruß Jahat
Datum:
OK... danke dir Karl Heinz Buchegger schrieb: > Gewöhn dir dieses Beschuldigen von Systemkomponenten ab. Kopf waschen hat geholfen ... jetzt funtzt es tadelos:
class Application : public Controller
{
protected: uint32_t analogwert, spannung;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onWork()
{
// leer
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
spannung=analogwert*5000/1024;
text.format("Spannung = %ld,%03ld Volt \n",spannung/1000,spannung%1000);
konsole.sendString(text);
}
} app;
|
und ich habs sogar begriffen :) Danke Jahat
Datum:
@Jahat Iblis (jaib) Es reicht, die Rechung in 32 Bit zu machen, die Variablen können 16 Bit bleiben.
class Application : public Controller
{
protected: uint16_t analogwert, spannung;
protected: AnalogDevice poti;
protected: Uart konsole;
protected: String text;
public: void onStart()
{
poti.config(0);
konsole.config(9600);
}
public: void onWork()
{
// leer
}
public: void onTimer100ms()
{
analogwert=poti.getValue10();
spannung=(uint32_t)analogwert*5000L/1024L;
text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000);
konsole.sendString(text);
}
} app;
|
MFG Falk
Datum:
Falk Brunner schrieb: > spannung=(uint32_t)analogwert*5000L/1024L; Ist zwar technisch egal, aber schöner sieht es so aus: spannung=(uint32_t)(analogwert*5000L)/1024L;
Datum:
Jahat Iblis schrieb: > und ich habs sogar begriffen :) OK. Du hast begriffen was die Ursache war. Aber hast du auch begriffen, wie die Ausgabe die du gesehen hast, zu Stande gekommen ist? Ich geh da jetzt mal näher drauf ein, weil diese Analyse sehr erhellend ist und verdeutlicht, warum es so wichtig ist, dass die Formatieranweisungen IMMER zum Datentyp des angegebenen Ausdrucks passen müssen. * printf ist eine variadische Funktion. Deine .format ist da auch nicht anders. Kann sie auch gar nicht sein. * Das wiederrum bedeutet, dass sich der Compiler darauf verlassen muss, dass die aufgerufene Funktion die Argumente so vom Stack holt, wie sie der Aufrufer hinaufschreibt. * Als variadische Funktion hat printf (und .format) keine Möglichkeit festzustellen, was der Aufrufer auf den Stack gelegt hat. Der Mechanismus, der normalerweise da Ordnung reinbringt greift hier nicht. Das einzige was printf zur Verfügung hat, ist der Formatstring. * Der Compiler weiß, dass ein int 2 Byte hat und ein long 4 Bytes * Auch printf weiß, dass ein int 2 Bytes hat und ein long 4. Was passiert beim Aufrufer? Der Aufrufer legt die beiden numerischen Argumente auf den Stack. Diese sind 32 Bit Argumente. Also werden pro Argument 4 Bytes auf dem Stack abgelegt. Auf deinem Stack liegen also 8 Bytes (in einer spezifischen Reihenfolge, die hier wichtig ist und dafür sorgt, dass du die 0 gesehen hast uns hier aber nicht weiter interessieren soll.) A0 A1 A2 A3 B0 B1 B2 B3 (A ist die erste 32-Bit Zahl. B ist die andere 32-Bit Zahl. Die Nummer ist jeweils die Bytenummer.) Das ist das was der Aufrufer an .format weitergibt. A0 A1 A2 A3 B0 B1 B2 B3 | v 0 1 2 3 4 5 6 7 .format weiß aber nichts von dieser Organisation. Alles was es hat ist der Formatstring und einen Stack auf dem 8 Bytes liegen. Den Formatstring zerlegt es sich erst mal. Dabei stösst es auf das %d. Da .format die Regeln kennt, weiß es, dass mit %d ein int verknüpft ist. int bedeutet: 2 Bytes. .format holt sich also die ersten beiden Bytes vom Stack und interpretiert diese als int. 0 1 2 3 4 5 6 7 | | v v A0 A1 dieser int wird entsprechend aufbereitet und ausgegeben. Danach geht es weiter im Format-String und .format findet die nächste Angabe: wieder ein %d. Also holt es sich die nächsten beiden Bytes (%d ist ja ein int) vom Stack und interpretiert diese wiederrum als int 0 1 2 3 4 5 6 7 | | v v B0 B1 Das wars. Im Formatstring sind keine weiteren %-Angaben mehr enthalten, woraus .format den Schluss zieht, dass es fertig ist und der Ausgabestring somit um die Argumente ergänzt wurde. Aber: Wenn du dir das ansiehst, dann wurden für die 2-te Zahl ganz andere Bytes zur Ausgabe herangezogen als der Aufrufer beabsichtigte! Der Aufrufer wollte die beiden im Formatstring angegebenen Argumente in ihrer Bytezerlegung so aufgefasst wissen A0 A1 A2 A3 B0 B1 B2 B3 +--------+ +--------+ durch den falschen Formatstring hat .format aber auf dem Stack das hier gesehen A0 A1 B0 B1 +--+ +--+ und damit die Bytes auf dem Stack völlig falsch den Argumenten zugeordnet. Und deshalb ist es wichtig, dass die Angaben im Formatstring mit den tatsächlichen Datentypen der angegebenen Ausdrücke übereinstimmt! Tut es das nicht, dann passieren solche Dinge.
Datum:
A. K. schrieb: > Ist zwar technisch egal, aber schöner sieht es so aus: > spannung=(uint32_t)(analogwert*5000L)/1024L; Besser: spannung = (analogwert * 5000UL) / 1024UL; oder spannung = ((uint32_t)analogwert * 5000) / 1024; Nach uint32_t (unsigned) zu casten und mit 5000L (signed) zu multiplizieren ist eher verwirrend als hilfreich.
Datum:
hm... ok das muss ich mir in Ruhe durch den Kopf gehen lassen. Ansonsten danke für die schnelle Hilfe und ich werde mich bestimmt bald mit meinem nächsten Problem hier melden :) Gruß Jahat
Datum:
A. K. schrieb: > Nach uint32_t (unsigned) zu casten und mit 5000L (signed) zu > multiplizieren ist eher verwirrend als hilfreich. Es ist allgemein keine gute Idee, signed und unsigned in einer Rechnung zu mischen. GCC bringt bei sowas auch gerne mal eine Warung.
Datum:
Jahat Iblis schrieb: > Jahat Iblis schrieb: >> text.format("Millivolt = %d \n",spannung); > > also funktioniert %d mit uint32_t. Nein! Versuche nicht, durch Trial-and-Error eine Sprache zu lernen und falsche Schlussfolgerungen zu ziehen wie "Code macht was er soll" => "Code ist korrekt". avr-gcc übergibt Argumente an varargs-Funktionen auf dem Stack, high-Byte zuerst. Dementsprechend werden die Argumente low-zuerst gelesen. Ein "%d" liest also nur 2 Bytes vom Stack, und zwar die low-Bytes und zeigt diese wie erwartet an. Es bleiben aber 2 nicht-gelesene high-Bytes. Das alles funktioniert nur deshalb und fliegt dir nicht um die Ohren, weil mit avr-gcc der Callee die Argumente über den Framepointer liest und nicht per POP. Folgt aber ein weiteres Argument nach dem uint32_t, hast du den Schamassel und suchst dir den Wolf, weil "uint32_t mit %d nachweislich funktioniert".
Datum:
Wenn man sicher und allgemein den richten Format-Specifier will, muß man für die (u)int*_t-Typen zu den entsprechend dafür definierten Makros greifen. Die sind nicht so wirklich schön, aber wenigstens in jedem Fall korrekt.
text.format("Millivolt = %"PRIu32" \n", spannung); |