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
intBerechne(void)
2
{
3
uint32_tvolt;
4
uint16_tadc1val;
5
6
adc1val=getadc(1);// hole Akkuspg (ADCW)
7
volt=((488*(uint32_t)adc1val)/100);// berechne Akkuspannung in mV
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
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?
... 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.
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.
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!
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
@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.
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.
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang