Forum: Mikrocontroller und Digitale Elektronik Festkommaarithmetik


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Frank G. (frankg)


Bewertung
0 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht lesenswert
Frank G. schrieb:
> Ich brauche nur eine Zeile, die
> aber richtig rechnet.

Nimm "float" und dann ist es gut!

von Benjamin S. (recycler)


Bewertung
0 lesenswert
nicht lesenswert
versuch mal:
adc64 =  ((uint64_t)adc16) * 5000;

von Frank G. (frankg)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
Nimm 5000UL, dann rechnet er die Multiplikation mit 32 Bit.

von Falk B. (falk)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
Falk B. schrieb:
> noch einmal in Ruhe
> lesen

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

von Christian S. (roehrenvorheizer)


Bewertung
0 lesenswert
nicht lesenswert

von eProfi (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.