Forum: Mikrocontroller und Digitale Elektronik Vergleich von float-Zahlen von ADC


von Martin (Gast)


Lesenswert?

Hallo zusammen,

ich möchte einen Strom über einen ADC Eingang des ATmega32 messen.
Dazu habe ich den Wert ausgelesen und in einer uint16_t Variable 
abgelegt.
Da der ADC ja nur 10Bit hat, liegen dort ja auch nur Werte zwischen 0 
und 1023.

Nun habe ich mir eine Funkion geschrieben, die mir den Strom in mA 
umrechnen soll, da ich den Strom im Programm vergleichen möchte.
1
float ADC2I_Melder (uint16_t adcvalue)
2
{
3
  return (adcvalue*0.00488*0.01);        //Umrechnung aus Auflösung ADC und Messwiderstannd=100Ohm, 0,1V pro mA
4
                
5
}

In der Main-Funkion habe ich dann folgende Auswertung.
1
if(stromMG1 > 0.2)
2
{
3
PORTB |= (1 << PB0);
4
}
5
else
6
{
7
PORTB &= ~(1 << PB0);
8
}

Nur leider klappt diese Auswertung nicht. Vergleiche ich die uint16_t 
Zahl direkt aus dem ADC, klappt die Auswertung in der Main einwandfrei. 
Daher muss der Fehler entweder in der Umrechnung sein, oder aber in bei 
der Auswertung.

Kann mir jemand einen Tipp geben, wo mein Fehler sein kann.

Vielen Dank.

Gruß Martin

von Udo S. (urschmitt)


Lesenswert?

Wenn ich mir die Zahlen so anschaue, dann kann der Wert maximal 1023 (10 
Bit max Value) * 0.01 * 0.00488 werden das ist bei mir 0.0499.

Wie soll das jemals größer als 0.2 werden?

von Ulrich (Gast)


Lesenswert?

Die Faktoren bei der Umrechnung sind falsch: Der erste Faktor gibt die 
Umrechnung in eine Spannung (in V). Um daraus dann den Strom im mA zu 
bekommen müsste man mit 10 Multiplizieren, nicht mit 0.01. So gibt das 
den Strom in A.

von Udo S. (urschmitt)


Lesenswert?

Das in float umzurechnen ist sowiso Blödsinn, kostet nur Platz und viel 
Zeit. Eine Kommazahl braucht nur der menschliche Anwender wenn er es 
angezeigt bekommt.

von Karl H. (kbuchegg)


Lesenswert?

Martin schrieb:

> Nun habe ich mir eine Funkion geschrieben, die mir den Strom in mA
> umrechnen soll, da ich den Strom im Programm vergleichen möchte.

Wenn du nur vergleichen musst, dann ist es vernünftiger den 
Vergleichswert in ADC Einheiten umzurechnen und nicht den ADC Wert in 
Milliampere. Denn typischerweise vergleichst du viel öfter als das sich 
der Vergleichswert ändert.

: Bearbeitet durch User
von Martin (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für die schnellen Antworten. Ich hab tatsächlich immer in A 
gerechnet. Keine Ahnung, was mich da geritten hat.

Ich möchte zukünftig die Werte auch über ein LCD ausgeben, daher wäre 
eine Gleitpunktzahl schon ganz ansehnlich.

Gruß Martin

von Bitflüsterer (Gast)


Lesenswert?

@ Martin

Schau Dir mal in diesem Forum einige Threads zum Thema Ausgabe von 
Fliesskommazahlen an.

Der allgemeine Teno ist, dass die oft auftretende Vermutung, man müsse 
in Gleitkomma rechnen, weil man Gleitkomma ausgeben will, letztlich 
falsch ist. Die Verfahren dazu wurden hier schon oft besprochen. Das 
findest Du leicht.

Der tiefere Grund für die Vermeidung von Gleitkommazahlen ist, dass der 
AVR recht viel Zeit und Code benötigt, um Berechnungen damit 
auszuführen. Tatsächlich ist es in der Mehrzahl der Fälle auch nicht 
wirklich nötig, mit Gleitkomma zu rechnen.

Wie Dir vielleicht noch aus der Schule bekannt ist, kann man die 
Ergebnisse aller nicht-transzendenten Funktionen (d.h. was nicht etwa 
trigonometrische Zahlen, Wurzeln, Logarithmen, etc.) als Brüche 
darstellen.

Was aber auch wichtig ist, das Du Dir bei der Planung des Programmes 
vorab Gedanken über die beteiligten Ein- und Ausgangs-Zahlen und die 
Zwischenwerte und deren jeweilige Grössenordnungen machst.
Von der Bedeutung her ist das damit vergleichbar etwa Ports ansprechen 
zu können oder die Sprache C ersteinmal zu können. Es ist unverzichtbar 
sich diese Fähigkeit anzueignen.

von Bitflüsterer (Gast)


Lesenswert?

Im übrigen kann man selbst bei den transzendenten Funktionen in der 
Regel mit Näherungen arbeiten. Das machen die Bibliotheksfunktionen für 
float und doube auch. Nur mit wesentlich mehr Stellen als es in der 
Praxis mit uCs nötig ist.

von guldn (Gast)


Lesenswert?

Hallo Martin,

wie ja schon gesagt, muss es (adcvalue*0.00488*10) sein, damit du alles 
in mA Einheiten hast.
Mein Vorschlag wäre, alles in µA zu skalieren.

Dann wäre es: (adcvalue*0.0048828..*10*1000)
oder auch (adcvalue*48,828...)
oder (adcvalue*49) , dann hast Du zwar 0.35% Fehler, was aber oft 
vernachlässigbar klein ist.

oder ((adcvalue*(49-0.171875)) =((adcvalue*49-adcvalue*0.171875)) und 
das ist (adcvalue  49 - adcvalue  11 / 64 ).

Das ist dann exakt, ohne Divison (da /64 durch Schieben optimiert wird) 
und ohne Gleitkomma.

von Karl H. (kbuchegg)


Lesenswert?

guldn schrieb:

> oder (adcvalue*49) , dann hast Du zwar 0.35% Fehler, was aber oft
> vernachlässigbar klein ist.

Wobei sich auch die Frage erhebt, wie genau denn eigentlich die 100 Ohm 
des Widerstands sind und ob diese Genauigkeit das 'rummosern' an 0.35% 
Fehler überhaupt rechtfertigt.

Es ist ja nicht nur das, sondern das Problem bei float besteht ja auch 
darin, dass man nur so um die 5 bis 6 signifikante Stellen hat. 
Signifikante Stellen! Nicht Nachkommastellen!

D.h. bei 0.00488 sind die 6 signifikanten Stellen schon aufgebraucht. 
Bei einem Wert von 1023 sind bereits mit 2 Nachkommastellen die 6 
signifikanten Stellen aufgebraucht. Und je öfter man mit einem float 
rechnet, desto mehr 'schmutz' sammelt sich an, so dass aus den 6 
signifikanten Stellen ganz schnell 5 werden. D.h. alles nach der 5. 
Stelle ist nicht mehr vertrauenswürdig.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Bitflüsterer schrieb:
> Der tiefere Grund für die Vermeidung von Gleitkommazahlen ist, dass der
> AVR recht viel Zeit und Code benötigt, um Berechnungen damit
> auszuführen.

Das ist doch Quatsch. Die float-Grundrechenarten brauchen etwa 1kB, 
sodass man auch auf einem aktuellen ATtiny.. bequem float rechnen kann. 
Vergleicht man zudem die Ausführungszeiten von float und int32_t (long), 
besteht da kein großer Unterschied. Float hat den Vorteil des großen 
Wertebereiches bei hinreichender Genauigkeit.

Auch wenn ich im vorliegenden Fall mit Integerwerten rechnen würde, 
zwingend auf float zu verzichten ist unbegründet, solange der Code ins 
Flash-ROM passt.
Die obige Zahlenakrobatik führt doch eher zu zusätzlichen Fehlern, als 
zur Lösung eines Problems.

von Helmut L. (helmi1)


Lesenswert?

m.n. schrieb:
> zwingend auf float zu verzichten ist unbegründet, solange der Code ins
> Flash-ROM passt.

Und fuer nicht verbrauchtes Flash gibt es kein Geld zurueck....

von Bitflüsterer (Gast)


Lesenswert?

@ m.n.

> Das ist doch Quatsch. Die float-Grundrechenarten brauchen etwa 1kB,

"Quatsch" ist das sicher nicht. Für den einen ist 1kB (oder welche 
Grösse auch immer) viel, für den anderen wenig. Jedenfalls ist es nicht 
sehr höflich eine Aussage eines anderen mit "Quatsch" zu bezeichnen.

>...zwingend auf float zu verzichten ist unbegründet...
Von "zwingend" habe ich nichts geschrieben. Es wäre mir angenehmer für 
etwas kritisiert zu werden, was ich wirklich geschrieben habe.

von nochwas (Gast)


Lesenswert?

>Das ist doch Quatsch. Die float-Grundrechenarten brauchen etwa 1kB,
sodass man auch auf einem aktuellen ATtiny.. bequem float rechnen kann.
Vergleicht man zudem die Ausführungszeiten von float und int32_t (long),
besteht da kein großer Unterschied.

Jagt den Troll raus.

von m.n. (Gast)


Lesenswert?

Bitflüsterer schrieb:
> Von "zwingend" habe ich nichts geschrieben.

Was ist denn konkret Deine Aussage?
Du redest von einem 'tiefen Grund', was kein Grund sondern ein 
abgründiges Vorurteil ist.
Hier geht die Hexenverbrennung dann weiter:

Karl Heinz schrieb:
> Und je öfter man mit einem float
> rechnet, desto mehr 'schmutz' sammelt sich an, so dass aus den 6
> signifikanten Stellen ganz schnell 5 werden. D.h. alles nach der 5.
> Stelle ist nicht mehr vertrauenswürdig.

Dann zeigt doch mal eine Berechnung mit float, die so umfangreich ist, 
dass die Zahlen dabei schmutzig werden. Und dann bitte zum Vergleich 
noch die erleuchtende Berechnung mit long-Zahlen, die nicht schmutzig 
sind.

Dann sieht man nämlich viel besser, dass diese Argumentation an den 
Haaren herbeigezogen ist. Aber überkommene Vorurteile wieder aufzuwärmen 
findet immer breite Zustimmung beim staunenden Fußvolk.

Um wieder auf den "Quatsch" zu kommen: mir geht der Quatsch einfach auf 
den Senkel.

nochwas schrieb:
> Jagt den Troll raus.

Jau, auf den Scheiterhaufen.

von Bitflüsterer (Gast)


Lesenswert?

@ m.n.
Schönes Leben noch. Am besten wo anders.

von Karl H. (kbuchegg)


Lesenswert?

Bitflüsterer schrieb:
> @ m.n.
> Schönes Leben noch. Am besten wo anders.

... und nimm deine Tasten-Interrupt Entprellung am besten gleich mit.

von Stefan F. (Gast)


Lesenswert?

Sowohl die berechnung als auch die Verwendung vom float Typ sind hier 
zumindest aus Sicht des Mikrocontrollers unnötig aufwändig.

Vorschlag wie man sowas angehen kann:
1
#define VOLT 1024/5
2
3
if (adcvalue > 2*VOLT ) {...}


VOLT gibt an, welchen Wert der ADC bei einem Volt liefert.
20mA ergeben bei 100 Ohm 2 Volt.

Wenn Du magst, kannst Du auf diese Art auch die Umrechnung in 
Milliampere machen:
1
#define AMPERE 100*VOLT
2
#define MILLIAMPERE AMPERE/1000
3
4
if (adcvalue > 20*MILLIAMPERE ) {...}

AMPERE ist der Wert, den der ADC bei einem Ampere und 100 Ohm liefern 
wuerde (wenn er es koennte).

MILLAMPERE ist der Wert, den der ADC bei einem Milliampere liefert.

Der Trick ist, dass diese Definitionen schon beim Compilieren optimiert 
werden / ganz ohne Fliesskomma-Artihmetik:

20*MILLIAMPERE = 20*100*1024/5/1000 = 409

Es ist nicht 409.6, weil C den ganzen Ausdruck als Integer betrachtet, 
denn alle Teile des Ausdruckes sind Integer Zahlen.

Zur Laufzeit muss der Mikrocontroller so nur noch testen, ob adcValue > 
409 ist. Die Umrechnung der Einheiten entfaellt somit komplett.

von Falk B. (falk)


Lesenswert?


von eProfi (Gast)


Lesenswert?

> #define VOLT 1024/5
> #define AMPERE 100*VOLT
> #define MILLIAMPERE AMPERE/1000

Dabei muss man aufpassen, dass das richtig gerechnet wird, evtl. wird 
das im Präprozessor nämlich nur mit 16 Bit gerechnet, der Wertebereich 
ist dabei schnell überschritten.
Besser mindestens in einer Zahl vorne ein L spendieren, dann wird alles 
long gerechnet (im Präprozessor):
#define VOLT 1024L/5
#define AMPERE 100L*VOLT
#define MILLIAMPERE AMPERE/1000L


Damit der µC nicht in 32bit rechnet, casten:
if (adcvalue > (uint16_T)(20*MILLIAMPERE) ) {...}

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
Noch kein Account? Hier anmelden.