GCC C, falsche Umrechnung der ADC-Werte

Gast #5156723
Lesenswert?

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
Gast #5156724
Lesenswert?

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.
#5156818
Lesenswert?

@ 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.
#5157427
Lesenswert?

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
#5157800
Lesenswert?

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
#5157815
Lesenswert?

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.
#5157825
Lesenswert?

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
#5157838
Lesenswert?

@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.
Gast #5157841
Lesenswert?

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.
#5158299
Lesenswert?

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
#5158322
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren