Forum: Mikrocontroller und Digitale Elektronik AVR ADC Resultat von komplementär nach "linear" umformen


von Ben _. (burning_silicon)


Lesenswert?

hi!

ich habe hier ein kleines problem mit dem resultat einer ADC unter 
verwendung der differential gain stage. das eigentliche messen ist kein 
problem, nur die umwandlung des resultates in eine brauchbare form. der 
AVR spuckt dabei das resultat als "two's complement" aus. der bereich 
-512 bis null geht dabei von 0x200(-512) bis 0x3FF(0), +512 werden zu 
0x1FF. in dieser form ist das ergebnis für mich unbrauchbar, zumal es 
sich noch über ADCH:ADCL verteilt. ich brauch es als 0..1023 bzw 
0x000..0x3FF. aber irgendwie stell ich mich zu doof an um eine schnelle 
umwandlung hinzubekommen, ich würde mich freuen wenn jemand sowas schon 
mal gemacht hat und mir etwas auf die sprünge helfen kann. dezimal wäre 
es einfach (+1024 und erledigt), aber ich bekomm das mit den geteilten 
bereichen irgendwie nicht gebacken, weil der negative bereich "über" dem 
positiven liegt.

von Tim S. (maxxie)


Lesenswert?

mach daraus erstmal ein signed short (bitshift nach links um 6, 
aritmetischer shift nach rechts um 6, das zieht dir das Vorzeichen in 
die oberen 6 bits)

dann einfach +512 um -512 bis + 511 auf 0 bis 1023 zu bringen.
1
signed short val = (((signed short)in << 6) >> 6) + 512 ;

von spess53 (Gast)


Lesenswert?

Hi

In Assembler?

MfG Spess

von Ben _. (burning_silicon)


Lesenswert?

mist, zum zweiten mal vergessen... ja, assembler. entschuldigt bitte!

von Wayne M. (vibra)


Lesenswert?

moin

für 8 Bit hät ich was , war mal für nen Dallas Temp.sensor.

+125    01111101    7Dh
0   00000000   00h
-55   11001001   C9h

das wären die Zahlen -55 bis +125 in einem Byte (8bit)
1
;*************************Two's complement in ASCII*********************************************** 
2
; Hexdezi used two highregister And 5 Low registers 
3
;aa = r16 , r6,r7,r8,r9,r10
4
5
;r6-r7 Hex Bcd wandlung
6
7
; zu wandelnder Wert in aa
8
9
10
.include "2313def.inc"
11
12
13
 .def  aa  = R16      ;input two's complement
14
 .def temp  = R17      ; scratch space
15
16
; Ergebnis in
17
 .def  dezv  =R08    ;dezimal ausgabe in ascii
18
 .def  dezh  =R09    ;
19
 .def  dezl  =R10    ;
20
;************************************************************************ 
21
 rjmp  RESET        ;   Reset Handle
22
;************************************************************************
23
24
25
26
RESET: 
27
  
28
    ldi    zl,Low(RAMEND)  ;Stack setzen
29
    out    SPL,zl
30
    rcall hexdezi
31
loop:  
32
    rjmp loop
33
34
;*****************************************************************************
35
; aa = two's complement number
36
37
hexdezi:
38
            ;wenn negativ 2 zahlen + vorz. sonst 3 
39
  sbrc  aa,7      ;Bit 7 gesetzt dann neg. - test auf negative Zahl
40
  rjmp negaz
41
            ;test auf größer 99
42
bin2bcdh:
43
    clr  temp    ;Clear result msd
44
bBCD83:  
45
    subi  aa,100      ;Input = Input -100
46
    brcs  bBCD84    ;abort If carry Set
47
    inc    temp  
48
    rjmp  bBCD83
49
bBCD84:  
50
    subi  aa,-100
51
    mov    dezv,temp
52
    rcall   bin2bcd
53
    rcall   bcdasc
54
    mov    dezh,r6
55
    mov   dezl,r7
56
    mov   aa,dezv
57
    rcall   bcdasc
58
    mov   dezv,r7
59
    ret
60
;**************************************************************************
61
negaz:
62
    neg    aa      ; negative Zahl ist  zweiercomplement
63
    rcall   bin2bcd
64
    rcall   bcdasc
65
    ldi   temp,'-'    ; minus ascii
66
    mov   dezv,temp
67
    mov    dezh,r6
68
    mov   dezl,r7
69
    ret      
70
71
;**************************************************************************
72
; 8Bit Binär in packed Bcd  -  wert in aa - ergebnis in aa !
73
bin2bcd:
74
    clr    temp    ;Clear result msd
75
bBCD81:
76
    subi  aa,10    ;Input = Input -10
77
    brcs  bBCD82    ;abort If carry Set
78
    subi  temp,-$10  ;für packed Bcd 
79
    rjmp  bBCD81
80
bBCD82:
81
    subi  aa,-10
82
    add    aa,temp
83
    ret
84
;***************************************************************************
85
; Bcd - zahlen in 2 ASCII char ---   wert in aa - ergebnis in r6 und r7  h/l Byte
86
;  
87
bcdasc:
88
    mov   temp,aa    ; aa aufheben
89
    lsr  temp
90
    lsr  temp
91
    lsr  temp
92
    lsr  temp    ;Msb ins Lsb schieben
93
    ori    temp,0x30  ;$30 dazuodern erzeugt ascii 30 -39 da nur zahl 0-9
94
    mov    r6,temp
95
    andi  aa,0x0f    ;löschen des Msb
96
    ori    aa,0x30
97
    mov    r7,aa
98
    ret

kann man mit AVRStudio simulieren

von spess53 (Gast)


Lesenswert?

Hi

Ohne Gewähr:

Vorsicht Pseudocode!

 in r16,ADCL
 in r17,ADCH

wenn Bit1 von r17=H dann

 com r16                    ; change sign
 com r17
 subi r16,  LOW(-1)
 sbci r17,High(-1)

weiter mit

 add r16, Low($200)
 adc r17, High($200)

MfG Spess

von Anja (Gast)


Lesenswert?

Hallo,

ist doch ganz einfach:
das Höchstwertige Bit enthält Dein Vorzeichen (0 = +, 1 = -)
Damit das Ganze ein "echtes" 2er Komplement gibt könntest Du
das Vorzeichen in alle bits oberhalb des Vorzeichens eintragen
(also wenn Bit9 gesetzt ist dann setze Bit 10-15 ebenfalls).
In Assembler wäre dies eine einfache Oder-Verknüpfung im High-Byte
mit 0xFC falls Bit 9 gesetzt ist.

Danach hast du ein echtes 2er-Komplement. (-512 bis +511)
Wenn Du jetzt 512 addierst (also 0x02 zum High-Byte) dann
erhälst Du den Zahlenbereich 0..1023

Tims Vorschlag in "C" macht im Prinzip auch nichts anderes.

von Hc Z. (mizch)


Lesenswert?

Du kannst auch ADLAR setzen.  Damit erhältst Du vorzeichenbehaftete 
fortlaufende Zahlen und sparst Dir ggfs. die Schieberei.

von spess53 (Gast)


Lesenswert?

Hi

Noch mal im Reinformat:

     in r16,ADCL
     in r17,ADCH

     sbrs r17,1
     rjmp aaa

     com r16                    ; change sign of dividend
     com r17
     subi r16, LOW(-1)
     sbci r17,High(-1)

aaa: ldi r18, Low($200)
     ldi r19,High($200)

     add r16,r18
     adc r17,r19

MfG Spess

von Ben _. (burning_silicon)


Lesenswert?

stelle fest ich kriegs heute in meine birne nicht rein. vielleicht schon 
zu lange dran rumprobiert. ich probiers mal mit deinem code spess!

von Ben _. (burning_silicon)


Lesenswert?

gibts eigentlich einen AVR der das auch bei differentiellen inputs als 
"lineares" ergebnis ausgeben kann? das wäre für mich glatt ein grund 
dahin zu wechseln. ich kapier den sinn davon nicht, es macht diese 
differentiellen eingänge unnötig kompliziert.

von Ben _. (burning_silicon)


Lesenswert?

geht leider noch nicht, der code negiert mir das ergebnis. sprich beim 
absenken der zu messenden spannung wird eine steigende "gemessen". 
irgendwas ist da noch murks aber ich hab heute keine lust mehr mich 
weiter mit dem schrott zu befassen.

von spess53 (Gast)


Lesenswert?

Hi

>gibts eigentlich einen AVR der das auch bei differentiellen inputs als
>"lineares" ergebnis ausgeben kann? das wäre für mich glatt ein grund
>dahin zu wechseln. ich kapier den sinn davon nicht, es macht diese
>differentiellen eingänge unnötig kompliziert.

Warum?. Das widerspricht für meine Begriffe dem Prinzip der 
differenziellen Messung. Es soll ja bestimmt werden, um welchen Betrag 
eine Spannung grösser oder kleiner als eine andere ist. Möglicherweise 
liegt dein Denkfehler schon etwas weiter davor. Aber da niemand weiss, 
worum es geht...........

MfG Spess

von Ben _. (burning_silicon)


Lesenswert?

es geht um eine möglichst genaue spannungsmessung in einem vorgegebenen 
bereich. mehr nicht. um die auflösung zu verbessern wollte ich den 
vollen messbereich auf den interessanten spannungsbereich bringen, das 
wären 9-16V. die 9V müssen daher irgendwie "weg". mit einem 
differentiellen verstärker und einer 10x gain stage ginge das sooo schön 
wenn man anstelle GND an einen pin eine feste offsetspannung anlegt, 
welche der des spannungsteilers bei 9V entspricht. der 
differentialverstärker würde bei der messung von 12V nun nur noch 3V 
"sehen" und der ADC kann im bereich von 9-16V mit vollen 10bit messen. 
jedenfalls in der theorie.

in der praxis schreibt der atmel kack aber vor, daß die messung nicht 
von 0..1023 sondern von -512..511 zu erfolgen hat. klasse idee, kann mir 
mal einer sagen wieso das sein MUSS?! okay, das bedeutet als erstes, daß 
die referenzspannung zum "wegkompensieren" der unwichtigen 9V um 0.25V 
angehoben werden muß damit man den vollen messbereich bekommt. aber daß 
dieses "+-"-verhalten bis ins programm reingezogen wird ist mir 
unverständlich - ein adc-ergebnis im bereich 0..1023 wäre deutlich 
einfacher zu handhaben. wenn dann jemand positive/negative ausgaben 
braucht wäre das ebenfalls sehr einfach möglich, das höchste bit läßt 
sich immer noch als vorzeichen gebrauchen...

von Karl H. (kbuchegg)


Lesenswert?

Ben _ schrieb:

> in der praxis schreibt der atmel kack aber vor, daß die messung nicht
> von 0..1023 sondern von -512..511 zu erfolgen hat. klasse idee, kann mir
> mal einer sagen wieso das sein MUSS?!

Weil die Differenz nun mal positiv oder negativ sein kann.
Wo liegt denn da das Problem?
Vorzeichenbit ansehen, erweitern auf 16 Bit und 512 addieren. Das kann 
doch nicht so schwer sein.

> einfacher zu handhaben. wenn dann jemand positive/negative ausgaben
> braucht wäre das ebenfalls sehr einfach möglich,

das ist genauso einfach/schwierig wie der umgekehrte Weg.

Du stellst dich an, wie jemand der sich wundert warum sein OpAmp auch 
negative Differenzen verstärkt.

von Ben _. (burning_silicon)


Lesenswert?

na das problem ist aber auch, daß sich durch den schrott die 10x gain 
stage wie eine 5x gain stage verhält. das ist für eine sinnvolle 
strommessung mit der internen referenz schon wieder die ganz unterste 
grenze. 200x würden sich wie 100x verhalten, das ist aber schon wieder 
zuviel des guten. reale 10x wären schon schick gewesen, so kostet das 
die hälfte der auflösung. zum glück immer noch genug, aber es wäre halt 
mehr drin gewesen.

ich fänds praktischer wenn ich immer einen wert zwischen 0..1023 
bekommen würde, den nullpunkt kann man sich dann mit einfachen ADDs bzw. 
SUBs hinschieben wo man ihn haben will.

habe für das eigentliche problem aber eine supereinfache lösung 
gefunden:
mit einem XOR auf das vorzeichenbit ballern und der spuk ist vorbei!

von Gastjjhjjklji (Gast)


Lesenswert?

Und warum nimmst Du nicht die Mitte des interessierenden Bereichs als 
Referenzspannung? Da Du die ja sowieso mit einem einfachen 
Spannungsteiler erzeugen willst ist die dort genauso ungenau.

Gast

von Ben _. (burning_silicon)


Lesenswert?

> Und warum nimmst Du nicht die Mitte des interessierenden Bereichs als
> Referenzspannung?
hab ich. anders gehts ja auch nicht.

von Hc Z. (mizch)


Lesenswert?

Ben _ schrieb:
> geht leider noch nicht, der code negiert mir das ergebnis. sprich beim
> absenken der zu messenden spannung wird eine steigende "gemessen".

Das Ergebnis darf auch nicht komplementiert werden, es muss das 
Vorzeichen ergänzt (nach oben erweitert) werden.  Code:
1
     in r16,ADCL
2
     in r17,ADCH
3
4
     sbrs r17,2
5
     rjmp is_positive
6
7
     ori r17,0xFC    // Vorzeichen erweitern
8
9
is_positive:

Ergebnis ist eine Zahl zwischen -4096 bis +4095.

von Hc Z. (mizch)


Lesenswert?

Der letzte Satz war nicht ganz richtig:  Das Ergebnis ist eine Zahl 
zwischen -512 und +511.  Wie Du von dort aus auf 0..1023 kommst, bedarf 
wohl keiner Erklärung.

von Michael U. (amiga)


Lesenswert?

Hallo,

     in r16,ADCL
     in r17,ADCH

     subi r17,0x02   // add 02 zum H-Teil
     andi r17,0x03   // Überträge ignorieren

sollte passen.

Gruß aus Berlin
Michael

von Ben _. (burning_silicon)


Lesenswert?

IN  R16,ADCL
IN  R17,ADCH
LDI R18,2
EOR R17,R18

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.