Hallo, habe Probleme mit der Berechnung der Spannung aus dem ADC-Wert und zwar wie folgt: int Berechne(void) { uint32_t volt; uint16_t adc1val; adc1val = getadc(1); // hole Akkuspg (ADCW) volt = ((488*adc1val)/100); // berechne Akkuspannung in mV } An VRef liegt AVCC (5V) an. Damit berechnet sich aus einem Wandlerwert von 212 ein Spgsergebnis von 488*212/100 = 1034,56 als int also 1034mV. Tatsächlich bekomme ich einen Wert von 364mV angezeigt. Verändere ich die Zeile auf volt= 5*adc1val, dann funktioniert die Berechnung korrekt mit Ergebnis (1060mV). Wenn ich beim Simulieren mir den Rechenweg im Disassembler ansehe, dann wird dort nur mit 2 Byte gerechnet und nicht mit 4Byte (uint32_t), wobei das Ergebnis nach der Teilung durch 100 ja auch tatsächlich mit uint16_t auskommt. Was mache ich falsch?? rechnet AVR-GCC grundsätzlich nicht mit mehr als 2Byte? Gruß Frewer
Frewer schrieb: > rechnet AVR-GCC grundsätzlich nicht mit mehr als > 2Byte? wenn nicht vorgegeben ist, rechnet er als int (also bei dir 16bit) > Was mache ich falsch??
1 | int Berechne(void) |
2 | {
|
3 | uint32_t volt; |
4 | uint16_t adc1val; |
5 | |
6 | adc1val = getadc(1); // hole Akkuspg (ADCW) |
7 | volt = ((488*(uint32_t)adc1val)/100); // berechne Akkuspannung in mV |
8 | }
|
so sollte es gehen.
Der Compiler rechnet mit Integer Werten, wenn du ihm nichts anderes explizit vorgibst. 488*adcval ergibt dann einen Überlauf. Abhilfe: Mit long rechnen. Also (488L * adcval) / 100
Georg G. schrieb: > Abhilfe: Mit long rechnen. Also (488L * adcval) / 100 Wenn du bei unsigned long bleiben willst: (488UL * adcval) / 100
@ Frewer (Gast) >von 212 ein Spgsergebnis von 488*212/100 = 1034,56 als int also 1034mV. 625*212/128 = 1035 ist ein wenig schneller, denn /128 kann der Compiler meistens als 7 Bit Rechtsschieben umsetzen. https://www.mikrocontroller.net/articles/Festkommaarithmetik#Die_L.C3.B6sung
Falk B. schrieb: > 625*212/128 = 1035 > > ist ein wenig schneller, denn /128 kann der Compiler meistens als 7 Bit > Rechtsschieben umsetzen. könnte man nicht gleich 1250*212/256 = 1035 machen, dann muss gar nichts geschoben werden?
Einfacher für den µC:
1 | millivolt = (1250L * adc) / 256; // 5000 / 1024 * 256 = 1250 |
Haha, wir hatten den gleichen Gedankengang. Oder 1251, dann zeigt er beim Anlegen von 5V 4,999 statt 4,995 an.
... und wenn man sich über die benötigte/ erzielbare Genauigkeit Gedanken macht (vor allen Dingen die 'mV' ausschließlich für eine Anzeige verwendet), könnte man vielleicht auch innerhalb der 16Bit bleiben.
@ Ralf G. (ralg) >... und wenn man sich über die benötigte/ erzielbare Genauigkeit >Gedanken macht (vor allen Dingen die 'mV' ausschließlich für eine >Anzeige verwendet), könnte man vielleicht auch innerhalb der 16Bit >bleiben. Naja, das wir schon eher schwierig, vor allem wenn man das Ganze noch kalibrieren will. Bei einem 10 Bit ADC-Ergebnis bleiben nur 6 Bit für den Umrechnungsfaktor, das ist eine Auflösung von ~1,5%. Da kann man auch gleich nur mit 8 oder gar 7 Bit vom ADC rechnen, dann bleibt mehr für den Faktor und es stimmt wieder.
Wie oft wird das gerechnet, dass ein paar ms störend sind?
Hallo, vielen Dank für die vielfache Hilfe. Insbesondere die Thematik des erneuten Deklarierens von (uint32_t)adc1val bei der Berechnung habe ich so in der einschlägigen Literatur noch nicht gesehen. Der Wert in adc1val ist ja stets kleiner als 2Byte, daher habe ich nie daran gedacht, dass für die Rechnung der Compiler adc1val als Zwischenspeicher benutzt. Jetzt ist das klar. Deshalb nochmals vielen Dank für die Beratung!!!!! Frewer
@Werner Freytag (frewer)
>erneuten Deklarierens von (uint32_t)adc1val bei der Berechnung habe ich
Das nennt man einen "cast", also nur ein zeitweises Umdeklarieren auf
einen neuen Variablentyp.
Werner F. schrieb: > dass für die Rechnung der Compiler adc1val als Zwischenspeicher > benutzt das macht er auch nicht. Aber er verwendet den Datentype von adc1val. Sonst hätte ja adc1val danach einen andere Wert.
Werner F. schrieb: > dass für die Rechnung der Compiler adc1val als Zwischenspeicher > benutzt. Das tut er auch nicht. Nur hängt die Breite einer Berechnung in C nicht vom Ergebnis ab, sondern nur von der Breite des grössten der beiden Operanden, mindestens aber "int". Folglich ist int*int ein int.
Hallo, also das erste Problem ist gelöst. Mit dem neuen Wissen habe ich dann weitergearbeitet und hänge nun wieder an so einem Problem "integer overflow". Es soll die Akkukapazität ermittelt werden, indem alle Sek der Strom durch einen 10 Ohm Entlade-Widerstand gemessen wird. Die Berechnung ist (5V/1024)*(1000/(60*60)) = 25/18432 (Wandlung in mV, dann sek in h) und das Ganze dann noch /10 Ohm. Wie sieht der Programmteil aus: #define widvalue (resistor*18432) // mit R = 10 Ohm uint32_t kapazitaet; // alle Sek wird adc1val addiert uint32_t strom; strom = (((uint32_t)kapazitaet * 25)/(uint32_t)widvalue); Für diese Zeile meldet der Compiler stets ein integer Overflow. Warum? Gruß Frewer
Werner F. schrieb: > Für diese Zeile meldet der Compiler stets ein integer Overflow. > Warum? zeige mal die echte Fehlermeldung, ein Overflow kann der Compiler eigentlich nicht melden.
Werner F. schrieb: > Für diese Zeile meldet der Compiler stets ein integer Overflow. > Warum? Wie groß ist 'widevalue'? ... ... Richtig!
Das wird die Ursache sein:
> #define widvalue (resistor*18432)
wie ist resistor definiert?
vermutlich hift:
1 | #define widvalue (resistor*18432l)
|
Wie ist resistor definiert? Denn erst das Ergebnis von (resistor*18432) wird nach uint32_t gecastet. aber da ist es schon zu spät #define widvalue (resistor*18432L) // achte auf das L nach der Konstanten!
Peter II schrieb: > #define widvalue (resistor*18432l) Wer das so schreibt stellt sich selbst ein Bein. Besser: #define widvalue (resistor*18432L)
:
Bearbeitet durch User
Werner F. schrieb: > #define widvalue (resistor*18432) // mit R = 10 Ohm > uint32_t kapazitaet; // alle Sek wird adc1val addiert > uint32_t strom; > > strom = (((uint32_t)kapazitaet * 25)/(uint32_t)widvalue); kapazitaet ist schon ein uint32_t. Das nochmal in den Typ zu casten, den es schon hat, bringt nix. > Für diese Zeile meldet der Compiler stets ein integer Overflow. > Warum? Von welchem Typ ist denn resistor? Ich vermute, dass er nur 16 bit hat. Dann gibt es bei der Multiplikation 10*18432 einen Überlauf.
Die Rechenregeln (Klammer- vor Punkt- vor Strichrechnung) sollten die bekannt sein. Der C-Compiler zerlegt einen Term nach den Regeln in Teilausdrücke und berechnet sie jeweils mit der Genauigkeit der beteiligten Werte (aber mindestens als int). zuerst wird ((uint32_t)kapazitaet * 25) berechnet und dann (uint32_t)(resistor*18432) Diese beiden Terme sind unabhängig voneinander. Erst danch wird geteilt. Beim letzten Ausdruck kommt der Fehler: resistor ist ein int 18432l ist ein int -> int*int = int Dann folgt erst der cast und aus dem int wird ein uint32_t
A. K. schrieb: > Wer das so schreibt stellt sich selbst ein Bein. Dirk B. schrieb: > 18432l ist ein int q.e.d. :-)
:
Bearbeitet durch User
@Werner Freytag (frewer) >der Strom durch einen 10 Ohm Entlade-Widerstand gemessen wird. Die >Berechnung ist (5V/1024)*(1000/(60*60)) = 25/18432 (Wandlung in mV, dann >sek in h) und das Ganze dann noch /10 Ohm. >Wie sieht der Programmteil aus: >#define widvalue (resistor*18432) // mit R = 10 Ohm >uint32_t kapazitaet; // alle Sek wird adc1val addiert >uint32_t strom; > strom = (((uint32_t)kapazitaet * 25)/(uint32_t)widvalue); Jaja, das liebe Chaos. Im Physikunterricht hätte man dir die Ohren langegzogen! Denn zu jeder Variable gehört auch eine Einheit! Welche Einheit hat deine Variable Kapazität? Ah? mAh mAs? Sowas gehört UNBEDINGT in die Kommentare, penible Menschen packen das sogar in den Variablennamen, wie z.b. unsigned long Kapazitaet_mAs Wenn man die Kapazität in mAs mißt und speichert, kommt man mit 32 Bit VERDAMMT weit, denn ~4e9 mAs sind 1,1e6 mAh. Eine große Monozelle (D) hat um die 20000 mAh, ein mittlerer Autoakku ca. 50000 mAh. Ergo 32 Bit mit mAs reichen.
Falk B. schrieb: > Wenn man die Kapazität in mAs mißt und speichert die Frage ist ob man das überhaupt in einer "für Menschen gemachten" Einheit speichern muss. Für die CPU ist es viel einfacher ein ADC * Takt zu rechnen. Also überhaupt nicht vorher umrechnen. Für eine Anzeige kann man noch immer für den Menschen umrechnen.
Bist du sicher, dass du die geplante Rechnung haben musst ? Fuer eine Anzeige in mV ja, aber fuer eine Regelung nicht.
Peter II schrieb: > Für eine Anzeige kann man noch immer für den Menschen umrechnen. Wo wir dann aber wieder bei dem obigen Problem sind. Wofür man es umrechnet, ist schließlich egal, wenn es darum geht, dass die Umrechnung aufgrund von Überläufen nicht richtig funktioniert.
An Falk Brunner: OK, OK, habe zwar nicht Physik studiert aber immerhin Elektronik. Allerdings zu Zeiten, da man noch mit Zuse und Lochkarten gelernt hat. Nun zu den Fragen: 1. Die tatsächliche Warnung: "../Entlader.c:217: warning: integer overflow in expression" 2. widvalue ist eine Konstante definiert zu #define widvalue (resistor*18432) // mit R (resistor) = 10 Ohm also widvalue = 184320. Gehe davon aus, dass der Compiler dieser Konstanten dann auch mindestens 3 Byte spendiert. 3. Dirk B. schrieb: > #define widvalue (resistor*18432L) // achte auf das L nach der > Konstanten! Das habe ich ergänzt und die Warnung ist weg. Jetzt bin ich gespannt, ob alles zusammen funktioniert. Merci Frewer
Werner F. schrieb: > 2. widvalue ist eine Konstante definiert zu > #define widvalue (resistor*18432) // mit R (resistor) = 10 Ohm > also widvalue = 184320. > Gehe davon aus, dass der Compiler dieser Konstanten dann auch mindestens > 3 Byte spendiert. Nein, so funktioniert es nicht. widvalue ist das Ergebnis einer Berechnung. Und bei jeder Berechnung gilt, dass der Typ von den Typen der Operanden abhängt. resistor ist ein int, 18432 ist ein int, also wird auch das Ergebnis der Multiplikation der beiden in einen int gespeichert. Ob die Werte konstant sind oder nicht, spielt dabei keine Rolle.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.