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.
Frank G. schrieb: > Ich brauche nur eine Zeile, die > aber richtig rechnet. Nimm "float" und dann ist es gut!
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
... 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ß.
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.
@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.
Falk B. schrieb: > noch einmal in Ruhe > lesen ....... mach ich, ich bin fast verzweifelt ABER 5000UL GEHT......Danke
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.
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.
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; |
@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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.