Forum: Mikrocontroller und Digitale Elektronik ADC Kalibrierung


von Stefan S. (eric996)


Lesenswert?

Guten Tag,

um bei einem ATmega16 (ADC) die Messgenauigkeit zu verbessern benutze 
ich folgenden Code:

ADC_Wert = ADC_Wert*0.964; funktioniert, bläht den Code aber auf.

ADC_Wert = ADC_Wert*247/256; das funktioniert nicht (denke das liegt am 
int                               Bereich)
ADC_Wert = (ADC_Wert*247)>>8; das funktioniert nicht (kommt nur murks 
raus)

bei float Deklarierung meckert der Kompiler
Da ich im Weiteren verlauf des Programms mit ADC_Wert als integer 
arbeite darf nicht nach String wandeln.

Da muss es doch einen eleganteren Weg geben?

Danke.

von Falk B. (falk)


Lesenswert?

@  Eric S. (eric996)

>ADC_Wert = ADC_Wert*0.964; funktioniert, bläht den Code aber auf.

Logisch, das ist ja auch ne Float-Multiplikation. Besser geht es mit 
Festkommaarithmetik.

MfG
Falk

von Benedikt K. (benedikt)


Lesenswert?

@Falk
Genau das möchte er ja.

@Eric
Versuchs mal so:
1
ADC_Wert = (unsigned long) ADC_Wert*247/256;

Oder auch so:
1
ADC_Wert = ADC_Wert*247UL/256;

Beides sollte gleichwertig sein.

von Stefan S. (eric996)


Lesenswert?

Da habe ich schon mal gelesen, wenn ich das richtig sehe wird aus dem 
Integer ein String gemacht, brauche jedoch einen Zahlenwert.

von Werner (Gast)


Lesenswert?

unsigned long ist kein string...

von Karl H. (kbuchegg)


Lesenswert?

Eric S. schrieb:
> Da habe ich schon mal gelesen, wenn ich das richtig sehe wird aus dem
> Integer ein String gemacht, brauche jedoch einen Zahlenwert.

Nein. Da wird kein String daraus gemacht.
Welchen Datentyp hat den ADC_Wert?
Ich vermute mal, das das bei dir ein int ist.

So jetzt nimm einfach mal an, dass ADC_Wert gleich 1024 sei.
1024 * 247 macht 252928

Autsch. Die maximale Zahl, die mit einem int darstellbar ist ist nur 
32767. Das Ergebnis der Multiplikation mit 247 ist also für einen int 
vieeeel zu gross.

Behoben wird das, indem man den Compiler zwingt die Multiplikation im 
Zahlenraum long oder unsigned long zu machen. Dann geht sich das aus.

von Stefan S. (eric996)


Lesenswert?

Danke !

das hat funktioniert:


ADC_Wert = (unsigned long) ADC_Wert*247/256;

von Jorge (Gast)


Lesenswert?

Die Verwendung von Kettenbrüchen ermöglicht manchmal eine Verbesserung 
der Performance indem mit kleineren Brüchen, in 8-Bit oder 16- Bit 
Darstellung bei höherer Genauigkeit der Näherung gearbeitet wird.

Beispiel mit JavaScript:
http://www.henked.de/javascript/kettenbrueche.htm

Beispielsweise wird die Skalierung für eine Grafik einmal über einen 
Kettenbruch angenähert und dann kann man alle Elemente damit auf dem 
Display anzeigen. Oder wie im vorliegenden Fall wenn es zeitkritisch 
wäre.

Das Produkt im Zähler muss natürlich zuerst berechnet werden und darf 
keinen Überlauf provozieren. Weil der Kettenbruch konvergiert kann man 
die Genauigkeit wählen - hoffentlich.

Dies ist manchmal besser als einfach mit 100 oder 1000 zu multiplizieren 
was dann zu einem Überlauf führen kann.

>>ADC_Wert = (unsigned long) ADC_Wert*247/256;
Das ist finde ich relativ geschickt /256 und >>8 sollte vom Compiler her 
optimierbar sein, es muss aber
ADC_Wert = ((unsigned long) ADC_Wert*247)/256; heissen, weil "/" eine 
höhere Priorität hat und so die Genauigkeit verschenkt/verloren wird.

von Jorge (Gast)


Lesenswert?

0.964=241/250

von Benedikt K. (benedikt)


Lesenswert?

Jorge schrieb:
> es muss aber
> ADC_Wert = ((unsigned long) ADC_Wert*247)/256; heissen, weil "/" eine
> höhere Priorität hat und so die Genauigkeit verschenkt/verloren wird.

Das steht hier aber anders:
http://de.wikibooks.org/wiki/C-Programmierung:_Liste_der_Operatoren_nach_Priorit%C3%A4t
Diese Operatoren sind afaik gleichwertig und werden daher von links nach 
rechts ausgewertet.

von Jorge (Gast)


Lesenswert?

>Diese Operatoren sind afaik gleichwertig und werden daher von links nach
>rechts ausgewertet.

Gut, dass ich es jetzt endlich auch gelernt habe. Sogar in Pascal ist es 
so. Wo ich das nun wieder herhabe ~:)
Danke dafür!

von Falk B. (falk)


Lesenswert?

@  Jorge (Gast)

>0.964=241/250

Ja und?

247/256 sind aber rechentechnisch besser, weil /256 = 8 Bit recht 
schieben.

Ändert a) an dem Grundproblem der Festkommaarithmetik wenig und b) 
sollte der OP beim nächsten mal besser lesen. Dort steht ziemlich am 
Anfang was von Überlauf vermeiden.

MfG
Falk

von Jorge (Gast)


Lesenswert?

Hi Falk,


>Ändert a) an dem Grundproblem der Festkommaarithmetik wenig und b)
>sollte der OP beim nächsten mal besser lesen. Dort steht ziemlich am
>Anfang was von Überlauf vermeiden.

Du hast natürlich Recht. Gerade beim AVR, der Multiplikation drauf hat. 
Bei einem kleinen PIC oder wie in meinem Fall einem 386SX ohne 
Coprozessor sieht es anders aus. Mein Ding musste Mehrkanaldaten 
anzeigen (ISA).

27/28  bietet eine gute Näherung (0.9643), man kann die Multiplikation 
mit dem Bruch in Assembler auf Additionen und Subtraktionen zurückführen 
und damit einen Überlauf von vorneherein vermeiden:

- Nenner subtrahieren solange bis das Ergebnis kleiner Null und 
mitzählen
- dann solange den Messwert addieren bis grösser Null und mitzählen

Also am Ende ist der Messwert 27mal addiert worden und der Nenner wurde 
so oft es geht abgezogen, der Rest bleibt. Dabei kommt es nie zu einem 
Überlauf.


Ein 386 schafft die Multiplikation und Division nicht in einem Takt 
sodass das Vorgehen eine mehr als doppelte Geschwindigkeit für die 
Darstellung der Fieberkurven brachte. Der Flaschenhals war dann das 
Löschen des Bildschirms (300ms) was über eine Änderung der Farbpalette 
einer der 16 VGA-Farben entschärft wurde. Das Löschen des gesamten 
Schirms wurde dann in 1/16 Abschnitten "hidden" erledigt.

Es hängt natürlich von der konkreten Anwendung ab, rechts Schieben ist 
wirklich sehr effektiv vor allem mit einem Barrel Shifter.

Gruss

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.