Forum: Mikrocontroller und Digitale Elektronik Binär -> Dezimal


von kent (Gast)


Lesenswert?

So ich habs geschafft und den MCP3422 (Microchip's 18-Bit ADC) am 
I2C-Bus zum laufen gebracht; betrieben wird er im 16Bit-Modus.

Problem: Der Chip hat eine interne Referenzspg. von 2.048V; diese 
entsprechen
0111111111111111 = 2^15-1

Wie wandle ich das in Dezimal um? Hat jemand da einen guten Trick zur 
Hand?
Der Fakto wäre ja 2.48/32767, aber wie macht man das möglichst elegant?

Auf sprut.de habe ich gefunden:

X / 1,024 = X - X/64 - X/128

(http://www.sprut.de/electronic/pic/programm/adclcd/adclcd.html)

Wie kommt man auf solche Tricks bzw. wie findet man solche Ersetzungen 
von Divisionen?

von Achim M. (minifloat)


Lesenswert?

2048mV Referenz, 32768 Counts?

Vorkommaanteil in mV bekommst du sehr easy durch Shiften:
Also um 3 nach Links schieben.


Den Nachkommaanteil der mV eben als 500µV, 250µV und 125µV separat aus 
den 3 "weggeshifteten" Bits des AD berechnen und hintenan hängen. Dazu 
die geshifteten Vorkommastellen in mV mit 1000(schnelle Multiplikation 
mit 1000(*)) multiplizieren und 500, 250 oder 125 je nach gesetzten Bits 
dazuaddieren.

Dann in der Anzeige an entsprechender Stelle das Komma hardcoden oder 
so.

mfg mf

PS: 1000 * x = ( (32 * x - x) * 32 ) + 8 * x
weil 1000 = ( 31 * 32 ) + 8

von Matthias L. (mcl024)


Lesenswert?

kent schrieb:
> betrieben wird er im 16Bit-Modus.
>
> Problem: Der Chip hat eine interne Referenzspg. von 2.048V; diese
> entsprechen
> 0111111111111111 = 2^15-1

Warum nicht 2^16 ??

von Achim M. (minifloat)


Lesenswert?

Die eigentliche Frage ist ja gewesen:

kent schrieb:
> wie findet man solche Ersetzungen
> von Divisionen?

Ersatz von Zehnerpotenzen durch eine möglichst kurze Addition von 
Zweierpotenzen. Darüberhinaus hat man dir mit Referenz = 2,048V 
versucht, das Leben leichtzumachen. Nur ein bisschen Bitfummelei ist 
nötig.


10 = 8 + 2

100 = 64 + 32 + 4

1000 = 1024 - 8 - 16 = (wie bereits gesagt)

10000 = 8192 + 2048 - 256 + 16

usw.

mfg mf

von kent (Gast)


Lesenswert?

Matthias Laubnitz schrieb:
> kent schrieb:
>> betrieben wird er im 16Bit-Modus.
>>
>> Problem: Der Chip hat eine interne Referenzspg. von 2.048V; diese
>> entsprechen
>> 0111111111111111 = 2^15-1
>
> Warum nicht 2^16 ??

Weil es im 2er-Komplement ist; der Chip hat differenzielle Eingänge und 
kann auch negative Eingangsspannungen.

von Achim M. (minifloat)


Lesenswert?

kent schrieb:
> Weil es im 2er-Komplement ist;

Dann eben auf "MSB Gesetzt" prüfen. Wenn gesetzt, dann Zweierkomplement 
der Zahl bilden und das "Minus" in einer Flagvariable speichern. Weiter 
wie bereits besprochen^^.

Dann kannst du lustig Shiften und es zerhaut dir nicht gleich deine 
ganze Zahl. Oder du bindest eine Mathematiklibrary ein und rechnest in 
float. Sofern du in C programmierst und Rechenzeit keine Rolle spielt.

Sonst kann ich dir echt nur Festkommaarithmetik empfehlen. Oder hat dein 
Proz eine Fließkommaeinheit?

mfg mf

von kent (Gast)


Lesenswert?

ok das war hilfreich, Danke für die Antworten!

von kent (Gast)


Lesenswert?

Mini Float schrieb:
> kent schrieb:
>> Weil es im 2er-Komplement ist;
>
> Dann eben auf "MSB Gesetzt" prüfen. Wenn gesetzt, dann Zweierkomplement
> der Zahl bilden und das "Minus" in einer Flagvariable speichern. Weiter
> wie bereits besprochen^^.
>
> Dann kannst du lustig Shiften und es zerhaut dir nicht gleich deine
> ganze Zahl. Oder du bindest eine Mathematiklibrary ein und rechnest in
> float. Sofern du in C programmierst und Rechenzeit keine Rolle spielt.
>
> Sonst kann ich dir echt nur Festkommaarithmetik empfehlen. Oder hat dein
> Proz eine Fließkommaeinheit?
>
> mfg mf

Nein es wird nur ein billiger PIC16F628 verwendet; alles in Software 
(I2C und 16-Bit Arithmetik).

von Achim M. (minifloat)


Lesenswert?

Benutz aber einen genug "longen" Datentyp, sonst wird das mit dem 
schnellen Multiplizieren usw. nichts.
mf

von kent (Gast)


Lesenswert?

der PIC kann nur 8-Bit Rechnungen, ich nehm normalerweise 2 Register um 
16-Bit rechnen zu können. Muss mal überlegen ob ich damit hinkomme wenn 
ich Multi/Div. in einer sinnvollen Reihenfolge mache... jetzt gehts 
erstmal in die Heia ;)

Gruß,

kent

von kent (Gast)


Lesenswert?

So ich hab mir jetzt mal was überlegt :)

Aalso... gemessen werden Spannungen von 0...15V. Diese kann man mit 
einem Spannungsteiler auf die max. Eingangsspannung des ADC von 2.048V 
runterteilen (Faktor 2.048/15). Der ADC macht daraus einen binären Wert 
von 0..2^15-1 (Faktor 2^15-1/2.048). Ich würde jetzt im PIC den Wert mit 
15000/2^15 malnehmen um dezimale Zehntelmilivolt rauszubekommen.

10.000/2^15 = (2^13 + 2^11 + 2^4 - 2^8) / 2^15 = 2^-2 + 2^-4 + 2^-11 - 
2^-7

Also angewendet auf das binäre Ergebnis e des ADC:

e>>2 + e>>4 + e>>11 - e>>7

Jetzt habe ich das beispielhaft mal für ein paar Werte durchgerechnet, 
und leider kamen da ziemliche Abweichungen bei raus.

Eine Eingangsspg. von 15V ergibt einen Wert von 149970 (ist ok).
Eine EIngangsspg. von 0.1234V ergibt einen Wert von 0.1275 (nicht ok).

Frage: Woher kommt dieser Fehler?

Ich denke mal durch das Rechtsschieben verliert man ja definitiv 
Genauigkeit (Information geht verloren). Müsste man das irgendwie binär 
runden?

Außerdem wird durch 2^15 geteilt obwohl der max. Wert des ADC bei 2^15-1 
liegt.

Irgendein Vorschlag wie man es verbessern kann?
Eine Verwendung von float scheidet leider aus; hatte gestern versucht 
die float-Bibliothek von Microchip einzubinden und die passt nicht mehr 
in den Controller :(
Die Integer-Bibliothek würde wohl noch reinpassen. Ist da eine bessere 
Genauigkeit zu erwarten als bei der Bitshift-Methode?

Danke schonmal für Vorschläge & schönen Sa.

-Kent

von Tip (Gast)


Lesenswert?

Du machst dir das Leben leichter, wenn du den Eingangsspannungsbereich 
auf 16,384 V festlegst, auch wenn das die Auflösung minimal reduziert.

von Achim M. (minifloat)


Lesenswert?

Klar, wenn du einfach rechtsschiebst, fallen hinten Bits runter.
"Tip" hat die für dich optimale Lösung für dein "wie bekomme ichs in den 
Controller". Dein Threadtitel fragt aber eigentlich nach was anderem.

Runden, wie wir es gewohnt sind, geht indem du die Hälfte der neuen 
niederwertigsten Stelle vor dem Abschneiden/Rechtsshift dazuzählst.
mfg mf

PS:
kent schrieb:
> (Faktor 2.048/15)
Nach allem was wir dir erzählt haben is das nicht dein Ernst oder?

von kent (Gast)


Lesenswert?

Da brauche ich das ergebnis ja nur einmal nach rechts schieben... 
genial, danke!

von Achim M. (minifloat)


Lesenswert?

Und um die 16,384V auf 2,048V abzubilden, muss vorher Analog durch 8 
geteilt werden. Da würde ich dir ein Widerstandsnetzwerk 10x 10k 
empfehlen.
mfg mf

von eProfi (Gast)


Lesenswert?

> Wie kommt man auf solche Tricks bzw. wie findet man solche
> Ersetzungen von Divisionen?
Indem man die Binärdarstellung der beteiligten Zahlen unter die Lupe 
nimmt und darüber meditiert, was dies eigentlich bedeutet.


Ich würde schon den Spannungsbereich voll ausnutzen.

Dein Maximalwert bei 15,000V ist 32767, Du willst also 32767 nach 15000 
umrechnen:

um 1 Bit    nach rechts schieben  --> 16383

um 4 weiter nach rechts schieben  -->  1023   subtrahieren = 15360
um 2 weiter nach rechts schieben  -->   255   subtrahieren = 15105
um 2 weiter nach rechts schieben  -->    63   subtrahieren = 15042
um 1 weiter nach rechts schieben  -->    31   subtrahieren = 15010
um 2 weiter nach rechts schieben  -->     7   subtrahieren = 15003
um 1 weiter nach rechts schieben  -->     3   subtranieren = 15000
oder
um 1 Bit    nach rechts schieben  --> 16383
um 4 weiter nach rechts schieben  -->  1023   subtrahieren = 15360
um 1 weiter nach rechts schieben  -->   511   subtrahieren = 14848
um 2 weiter nach rechts schieben  -->   127   addieren     = 14975
um 2 weiter nach rechts schieben  -->    31   addieren     = 15006
um 2 weiter nach rechts schieben  -->     7   subtrahieren = 14999
um 2 weiter nach rechts schieben  -->     1   addieren     = 15000
bei ausreichend genauem Ergebnis kann man schon eher abbrechen,
z.B. nach der 4. Zeile.

Eine weiter Möglichkeit ist eine erweiterte Multiplikation:
und anschließendes Kürzen mit 2^17:
32767 * 60000 = 1966020000   / 131072 = 14999,542236328125
32767 * 60003 = 1966118301   / 131072 = 15000,292213439941

Das Kürzen um 2^17 ist einfach: Du nimmst das Upper-Word des 
Mult-Ergebnisses und schiebst es um eins nach rechts.
Wenn Du runden willst, merkst Du dir das bit0 der Mult und addierst es 
zum Ergebnis.

Dieses Vorgehen hat den Vorteil, dass 60003 der Kalibrierfaktor ist. 
Stimmt Dein Spannungsteiler nicht exakt, kannst Du das mit diesem Faktor 
kompensieren.
Auch eine Temperaturkompensation ist damit möglich, z.B. mit 
LookUp-Table.

Eine schnelle 32x32-->64-Bit Mult findest Du z.B. hier:
Beitrag "Re: Rechnen mit AVR"
Weiter unten auch in AVR-asm.

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.