Forum: Mikrocontroller und Digitale Elektronik Festkommaarithmetik


von Frank G. (frankg)


Lesenswert?

Hallo ich habe den obigen Beitrag gelesen und will mich auch daran 
halten. Aber mein Prozessor wohl nicht.

Ich habe eine Schaltung mit einem Mega 16 aufgebaut. Die geht auch.
Am Port PA0 will ich eine Spannung messen (Zwischen 0 und 5 Volt)
Da die 5V aus 1024 Stücken bestehen, muss ich den ADC Wert *5 /1024 
nehmen.
Ich habe am Mega 16 auch eine JTAG Schnittstelle, so dass ich die 
Operationen verfolgen kann
adc16 ist das Korrekte Ergebnis vom AD Programm (uint16_t)
Der Wert soll * 5000 genommen werden und dann durch 1024 Dividiert 
werden
Dann hat man das Ergebnis im mV

        adc16 = (adcval=ADC_Read_Avg(0,10));   Das geht bei fast 4V = 
987
  adc64 =  (adc16 * 5000);               adc64 uint64_t
  adc32 = (adc16 * 5000);                adc32 uint32_t
    adcul = (adc16 * 5000);                adcul unsigned long

Nach dem schrittweisen Ausführen steht bei Wert (Watch Ansicht)
bei allen 3 Variablen 19800    anstatt  4935000   ???
Kann mir jemand helfen was ist falsch? Ich brauche nur eine Zeile, die 
aber richtig rechnet.

von m.n. (Gast)


Lesenswert?

Frank G. schrieb:
> Ich brauche nur eine Zeile, die
> aber richtig rechnet.

Nimm "float" und dann ist es gut!

von Benjamin S. (recycler)


Lesenswert?

versuch mal:
adc64 =  ((uint64_t)adc16) * 5000;

von Frank G. (frankg)


Lesenswert?

m.n. schrieb:
> Nimm "float" und dann ist es gut!

Meine Variablen
volatile float adcf = 0;
volatile uint64_t adc64 = 0;
volatile uint32_t adc32 = 0;
volatile uint16_t adc16 = 0;

Wenn ich im Schritt durchgehe  adc16 = 985

adcf adc64 adc32  alle 9800 ??  (ich hatte die Zeile adcf = adc16 * 
5000);
eingefügt

von Ralph S. (jjflash)


Lesenswert?

... unabhängig davon, dass ich den Sinn dieser Rechnerei nicht 
verstehe.... ein uint64_t gibts zwar beim avr-gcc... imho ist dieser 
allerdings dennoch bloß 32 Bit groß.

von Frank G. (frankg)


Lesenswert?

Sinn...........
ich möchte einfach nur aus dem Wert z.B.985 die Spannung in mV 
ausrechnen und wenn bei einer Teiloperation (* 5000) schon Quatsch 
rauskommt, brauche ich mit dem Ergebnis nicht weiter rechnen.

von eProfi (Gast)


Lesenswert?

Nimm 5000UL, dann rechnet er die Multiplikation mit 32 Bit.

von Falk B. (falk)


Lesenswert?

@Frank G. (frankg)

>Sinn...........
>ich möchte einfach nur aus dem Wert z.B.985 die Spannung in mV
>ausrechnen

Dann solltest du den Artikel Festkommaarithmetik noch einmal in Ruhe 
lesen, dort steht nämlich alles drin. 64 Bit Operationen braucht man 
dazu mal sicher NICHT, 32 Bit reichen.

Expertentip . . .

"Wichtig ist dabei, daß "

etc. pp.

von Frank G. (frankg)


Lesenswert?

Falk B. schrieb:
> noch einmal in Ruhe
> lesen

.......  mach ich, ich bin fast verzweifelt ABER 5000UL GEHT......Danke

von Christian S. (roehrenvorheizer)


Lesenswert?


von eProfi (Gast)


Lesenswert?

Viele Wege führen nach Rom:

1024 und 5000 kann man maximal um 8 kürzen, am besten durch 4, es 
bleiben
256 und 1250
d.h. statt der Division durch 256 lässt man das unterste Byte weg.

Aber es gibt noch eine elegantere Lösung, wenn man sich die Bits und 
deren Wertigkeit ansieht:
1
Bit Wert mV
2
 9  512  2500
3
 8  256  1250
4
 7  128   625
5
 6   64   312,5
6
 5   32   156,25
7
 4   16    78,125
8
 3    8    39,0625
9
 2    4    19,53125
10
 1    2     9,765625
11
 0    1     4,8828125
1
uint16_t mask = 512, add = 2500, mV = 0;
2
do{
3
  if(adc & mask) mV+=add;
4
  add >>=1;
5
  }while((mask>>=1)!=0);
6
//Dann ist die maximale Summe: 2500+1250+625+312+156+78+39+19+9+4= 4992
7
8
//Genauer ist wegen Runden:
9
uint16_t mask = 512, add = 2500, mV = 0;
10
do{if(adc & mask) mV+=add; add >>=1;} while((mask>>=1)>4);add = 20;  // mask ist jetzt 4
11
do{if(adc & mask) mV+=add; add >>=1;} while((mask>>=1)!=0);
12
13
//Dann ist die maximale Summe: 2500+1250+625+312+156+78+39+20+10+5= 4995
14
15
//Man kann auch Loop-Unrolling machen, ist auch nicht viel länger, aber schneller:
16
uint16_t mV = 0;
17
  if(adc & 512) mV+=2500;
18
  if(adc & 256) mV+=1250;
19
  if(adc & 128) mV+=625;
20
  if(adc &  64) mV+=312;
21
  if(adc &  32) mV+=156;
22
  if(adc &  16) mV+=78;
23
  if(adc &   8) mV+=39;
24
  if(adc &   4) mV+=20;
25
  if(adc &   2) mV+=10;
26
  if(adc &   1) mV+=5;
27
28
Noch schneller wird es, wenn wir den ADC-Wert in zwei Bytes zerlegen:
29
uint16_t mV = 0;adch = ADCH; adcl = ADCL; 
30
//zwischenspeichern, damit sich nichts während der Abfrage ändert
31
  if(adch &   2) mV+=2500;
32
  if(adch &   1) mV+=1250;
33
  if(adcl & 128) mV+=625;
34
  if(adcl &  64) mV+=312;
35
  if(adcl &  32) mV+=156;
36
  if(adcl &  16) mV+=78;
37
  if(adcl &   8) mV+=39;
38
  if(adcl &   4) mV+=20;
39
  if(adcl &   2) mV+=10;
40
  if(adcl &   1) mV+=5;
Alles aus dem Stegreif und ungetestet.

von S. R. (svenska)


Lesenswert?

Ralph S. schrieb:
> ein uint64_t gibts zwar beim avr-gcc... imho ist dieser
> allerdings dennoch bloß 32 Bit groß.

Das ist falsch. Wo ein int64 draufsteht, ist auch ein int64 drin.

Aber: "double" und "float" sind auf dem AVR gleich.

von eProfi (Gast)


Lesenswert?

Wenn man bei adc 1023  5000mV angezeigt haben will:
1
  if(adch &   2) mV+=2500;
2
  if(adch &   1) mV+=1250;
3
  if(adcl & 128) mV+= 626;
4
  if(adcl &  64) mV+= 313;
5
  if(adcl &  32) mV+= 157;
6
  if(adcl &  16) mV+=  79;
7
  if(adcl &   8) mV+=  40;
8
  if(adcl &   4) mV+=  20;
9
  if(adcl &   2) mV+=  10;
10
  if(adcl &   1) mV+=   5;
11
12
Wenn bei adc 1023  3300 mV angezeigt werden soll:
13
  if(adch &   2) mV+=1650;
14
  if(adch &   1) mV+= 826;
15
  if(adcl & 128) mV+= 413;
16
  if(adcl &  64) mV+= 207;
17
  if(adcl &  32) mV+= 104;
18
  if(adcl &  16) mV+=  52;
19
  if(adcl &   8) mV+=  26;
20
  if(adcl &   4) mV+=  13;
21
  if(adcl &   2) mV+=   6;
22
  if(adcl &   1) mV+=   3;
23
24
Wenn bei adc 1023  3297 mV angezeigt werden soll:
25
  if(adch &   2) mV+=1650;
26
  if(adch &   1) mV+= 825;
27
  if(adcl & 128) mV+= 413;
28
  if(adcl &  64) mV+= 206;
29
  if(adcl &  32) mV+= 103;
30
  if(adcl &  16) mV+=  52;
31
  if(adcl &   8) mV+=  26;
32
  if(adcl &   4) mV+=  13;
33
  if(adcl &   2) mV+=   6;
34
  if(adcl &   1) mV+=   3;

von Falk B. (falk)


Lesenswert?

@eProfi (Gast)

>Alles aus dem Stegreif und ungetestet.

Und auch völlig daneben. Der Op wollte nicht mehr als einen ADC-Wert in 
ein eine Meßgröße umrechnen und nicht irgendwelche Exkurse in die Welt 
der Micro- und Nonsensoptimierung unternehmen. Das Ziel erreicht man mit 
einer einfachen, übersichlichtlichen Zeile C-Code, die zu 99% auch 
vollkommen ausreicht.

von eProfi (Gast)


Lesenswert?

Es geht noch schneller, wenn man die Reihenfolge umdreht und die ersten 
Additionen (solange die Summe in 8 Bit passt), mit 8 Bit ausführt:
1
typedef union{
2
  uint16_t W; /Word
3
  struct{
4
    uint8_t L; //Lo-Byte (Reihenfolge je nach Endianess)
5
    uint8_t H; //Hi-Byte (Reihenfolge je nach Endian-ness)
6
  };
7
} un_i16;
8
9
  un_i16 mV;
10
  mV.W=0;
11
  if(adcl &   1) mV.L+=   5; //8-Bit-Addition
12
  if(adcl &   2) mV.L+=  10;
13
  if(adcl &   4) mV.L+=  20;
14
  if(adcl &   8) mV.L+=  40;
15
  if(adcl &  16) mV.L+=  79;
16
  if(adcl &  32) mV.W+= 157; //16-Bit-Addition
17
  if(adcl &  64) mV.W+= 313;
18
  if(adcl & 128) mV.W+= 626;
19
  if(adch &   1) mV.W+=1250;
20
  if(adch &   2) mV.W+=2500;
21
22
//bei 3300 geht noch eine Zeile mehr mit .L:
23
  mV.W=0;
24
  if(adcl &   1) mV.L+=   3; //8-Bit-Addition
25
  if(adcl &   2) mV.L+=   6;
26
  if(adcl &   4) mV.L+=  13;
27
  if(adcl &   8) mV.L+=  26;
28
  if(adcl &  16) mV.L+=  52;
29
  if(adcl &  32) mV.L+= 104;
30
  if(adcl &  64) mV.W+= 207; //16-Bit-Addition
31
  if(adcl & 128) mV.W+= 413;
32
  if(adch &   1) mV.W+= 826;
33
  if(adch &   2) mV.W+=1650;
Um den vollen 16-bit-Wertebereich zu nutzen, kann man die addierten 
Werte mit 8 (nur 4 shifts), 10 (human readable), 13 (65535/5000=13,107), 
16 (nur 4 shifts) oder 19 (65535/3300=19,859) multiplizieren (dadurch 
werden sie genauer) und nach den Additionen wieder dividieren, bringt 
aber fast nichts.

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.