PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mathematisches Problem



Kaiser-F
16.09.2005, 21:26
Hallo zusammen,

Ich komm mal gleich zur Sache:

Zunächst wird eine Hebelstellung per POTI über den ADC ausgelesen.

Dieser Wert liegt zwischen 0 und 1023. = way_akt

Ich nutze aber nicht den vollen weg des Potis, sondern nur
zB von 100 bis 900.

Diese Werte habe ich in way_min und way_max geschrieben.


Ich benötige am scholuss eine 8Bit-breite Zahl.

Also habe ich mir folgende umrechnung ausgedacht:



uint8_t way_rev; // Reverse-Bits
uint16_t way_min = 100; // Reverenz Minimalwert
uint16_t way_mid; // Reverenz Mittelstellung
uint16_t way_max = 900; // Reverenz Maximalwert

uint16_t way_akt; // Rohe-Potistellung
uint16_t way_neu; // Aufbereitete Stellgröße





way_akt = ADC_wert(0);

if( way_akt < way_min ){ way_akt = way_min; }
if( way_akt > way_max ){ way_akt = way_max; }

way_akt = way_akt - way_min;

double factor;
double x;

factor = 1023 / (way_max - way_min);
x = (way_akt / factor);

way_neu = x;
way_neu = ((way_neu)>>2);



PORTC = way_neu;



nunja, leider ist die berechnung nicht so genau.

Deshalb erreiche ich nie meine vollen 255!

Wie könnte ich das am dümmsten lösen? Ich habe schon sämtliche formeln und Co ausprobiert....

Für eure Hilfe wäre ich euch mehr als Dankbar!

Marco78
16.09.2005, 21:34
Ich habe keine Ahnung von C, aber es müsste im Prinzip ja so aussehen:
100=0
900=255
255/800=0,318
Wert=way_akt*0,318

Michael
16.09.2005, 21:35
Hallo Kaiser-F,

Ich nutze aber nicht den vollen weg des Potis, sondern nur
zB von 100 bis 900.

Diese Werte habe ich in way_min und way_max geschrieben.

Nimm also way_diff = way_max - way_min.
Jetzt der Dreisatz:
Way_diff/255 = Way_akt/x
x = Way_akt * 255 / way_dif

x ist der gesuchte 8Bit-Wert.

Gruß, Michael

PasstScho
16.09.2005, 21:52
Hi,
@Michael: ich bin mir nicht sicher ob das 100%ig geht, weil uint16 max 65535 sein kann. Also könnte es bei Werten von Way_akt > 257 Probleme geben...
@Kaiser-F: was mir bei deinem code seltsam vorkommt ist
factor = 1023 / (way_max - way_min);
x = (way_akt / factor);
probier das mal mit
factor = 1023.0 / (double)(way_max - way_min);
x = ((double)way_akt / factor);

PS: einfache floats sollten da doch reichen oder?
.... bin mir jetzt nicht sicher ob es das war, aber das könnte es gewesen sein...
MfG Alex

Michael
16.09.2005, 21:58
Hallo PasstScho,

ich bin mir nicht sicher ob das 100%ig geht,
das passtscho, setz einfach mal Werte ein und rechne es aus.

einfache floats sollten da doch reichen oder?
Besser noch:
Kaiser-F wollte nur 8 bit!
Gruß, Michael

Kaiser-F
16.09.2005, 22:48
Sorry, war gerade nen Happe essen,
Und hab mir 2 Aspirin + C reingehaun.

Evtl hilft mir das mit C :-)



@PasstScho:

factor = 1023 / (way_max - way_min);

(way_max - way_min) ist der "Nutzweg"

durch diesen factor muss ich später teilen.

Das ganze funktioniert wunderbar, nur bekomm ich das mit den Gleitpunktvariablen nicht so hin....

der wert factor, und der spätere Quotient:

way_akt /factor ist dann zu ungenau...

Ich bin mir nicht genau sicher wie ich die Gleitpunktvariablen anwenden muss.

Ich probier gleich mal eire Anregungen aus.

Danke vorerst mal für die Superschnelle Hilfe!

EDIT:



@Michael: ich bin mir nicht sicher ob das 100%ig geht, weil uint16 max 65535 sein kann. Also könnte es bei Werten von Way_akt > 257 Probleme geben...


der ADC liefert max. 1023. Da es ein 10Bit ADC ist, bin ich gezwungen
ein uint16_t zu verwenden.

Kaiser-F
16.09.2005, 23:09
Also;

Merkwürdigerweise erhalte ich wenn ich es EINFACH so schreibe:



uint8_t way_rev; // Reverse-Bits
uint16_t way_min = 100; // Reverenz Minimalwert
uint16_t way_mid; // Reverenz Mittelstellung
uint16_t way_max = 900; // Reverenz Maximalwert

uint16_t way_neu; // Aufbereitete Stellgröße

uint16_t way_akt; // Rohe-Potistellung
uint16_t way_nutz;
uint16_t factor;
uint16_t x;



way_akt = ADC_wert(0);

if( way_akt < way_min ){ way_akt = way_min; }
if( way_akt > way_max ){ way_akt = way_max; }

way_akt = way_akt - way_min;

way_nutz = (way_max - way_min);


factor = 1023 / way_nutz;
x = way_akt / factor;


way_neu = x;
way_neu = ((way_neu)>>2);



PORTC = way_neu;


Das "genauste" Ergebnis: 200....

wenn man rechnet: 1023/800 = 1,27875. mit uint16_t sind macht das: 1

also.... für nix.....



mit

uint8_t way_rev; // Reverse-Bits
uint16_t way_min = 100; // Reverenz Minimalwert
uint16_t way_mid; // Reverenz Mittelstellung
uint16_t way_max = 900; // Reverenz Maximalwert

uint16_t way_neu; // Aufbereitete Stellgröße

uint16_t way_akt; // Rohe-Potistellung

uint16_t way_nutz;

double factor;
double x;



way_akt = ADC_wert(0);

if( way_akt < way_min ){ way_akt = way_min; }
if( way_akt > way_max ){ way_akt = way_max; }

way_akt = way_akt - way_min;

way_nutz = (way_max - way_min);


factor = 1023.0000 / way_nutz;
x = way_akt / factor;


way_neu = x;
way_neu = ((way_neu)>>2);



PORTC = way_neu;

kommt raus: 156... ??

Kaiser-F
16.09.2005, 23:17
au au au... das wird peinlich!

156 war rechnerisch absolut richtig!



uint8_t way_rev; // Reverse-Bits
uint16_t way_min = 100; // Reverenz Minimalwert
uint16_t way_mid; // Reverenz Mittelstellung
uint16_t way_max = 900; // Reverenz Maximalwert

uint16_t way_neu; // Aufbereitete Stellgröße

uint16_t way_akt; // Rohe-Potistellung

uint16_t way_nutz;

double factor;
double x;



way_akt = ADC_wert(0);

if( way_akt < way_min ){ way_akt = way_min; }
if( way_akt > way_max ){ way_akt = way_max; }

way_akt = way_akt - way_min;

way_nutz = (way_max - way_min);


factor = way_nutz / 255.0000;
x = way_akt / factor;


way_neu = x;



PORTC = way_neu;


der Fehler lag darin, dass der factor nicht 1023/way_nutz ist,
sondern way_nutz/255.


Danke an Michael, durch seinen Tipp "DREISATZ" bin ich draufgekommen.

Vielen Danke nochmal an alle!

PasstScho
16.09.2005, 23:24
Hi,
Genau das ist der Grund warum ich das zu (double) gecastet habe.
In C oder C++ ist 1000/800 nicht das gleiche wie 1000.0/800.0 ... bei 1000/800 teilt dein Programm mit Zahlen ohne Komma!
Wenn du 1000.0/800.0 machst, sieht er das .0 und erkennt, dass du mit Komma rechnen willst.
Du musst in deinem Programmen also richtig zwischen int und float unterscheiden.
Ich prüfe mal grad die andere Lösung von Michael .... theoretisch könnte sie funktionieren, aber auch nicht so wirklich...

MfG Alex

Kaiser-F
16.09.2005, 23:50
Hi auch,

@ Passtscho: Du hast vololkommen Recht!

Wenn ich schreibe: (way_max[ch] - way_min[ch]) / 255; ist das Ergebnis ungenau!

Wenn ich aber schreibe: (way_max[ch] - way_min[ch]) / 255.0000;
dann erhalte ich das Richtige ergebnis. wobei 255.0000 etwas übertrieben wirkt ;-)

Hier alles in Kurzform in einer Funktion:

( mit globalen Arrays )



void WAY_PROP ( uint8_t ch ){

if( way_akt[ch] < way_min[ch] ){ way_akt[ch] = way_min[ch]; }
if( way_akt[ch] > way_max[ch] ){ way_akt[ch] = way_max[ch]; }
way_akt[ch] = way_akt[ch] - way_min[ch];

double factor = (way_max[ch] - way_min[ch]) / 255.0000;
way_end[ch] = way_akt[ch] / factor;

}

Felix G
17.09.2005, 00:08
die Anzahl der Nullen hinter dem Punkt ist egal...

sobald hinter einer Zahl ein ".0" kommt, wird sie als float behandelt (mit der entsprechenden Genauigkeit)

PasstScho
17.09.2005, 00:09
Hi,
in der Zeile "way_end[ch] = way_akt[ch] / factor; " ist das auch schon so eine Sache...
way_akt[ch] ist doch ein uint16_t aber factor ist ein float oder double.
In dem Fall hast du nicht wirklich festgelegt, ob er jetzt mit floats oder mit ints rechnen soll.
Erst wenn du "way_end[ch] = (uint16_t)((double)way_akt[ch] / factor);" machst, kannst du dir wirklich sicher sein, dass er tut was du willst.
Das ist zwar übertrieben, da es vorher ja auch ging, allerdings ist es wirklich ärgerlich, wenn man lange nach einem Fehler sucht und es an einem fehlenden "(float)" lag.
Ich mache das deshalb bei Formeln mit verschiedenen Variablen-Typen immer so, dass der Compiler garnicht erst daran denken kann etwas anders zu machen als ich es will ;) ...
Dein 255.0000 ist allerdings wirklich übertieben, denn 255.0 reicht sicher. :)

Wer sich wirklich mit Compilern auskennt (ich tu es nicht), für den ist es völlig klar, was wann überflüssig ist und wann nicht, allerdings geh ich lieber auf Nummer sicher...

MfG Alex

Felix G
17.09.2005, 00:21
ist eigentlich ganz einfach...

der Compiler wird keine verlustbehafteten Operationen ausführen (es sei denn man zwingt ihn dazu),
d.h. er wird nie aus einem float ein int machen, sondern immer umgekehrt. (das Ergebnis ist dann auch ein float)


mach mal einen typecast von float nach int, dann wirst du höchstwahrscheinlich eine Warnung vom Compiler erhalten
(da bei dieser Operation Daten verloren gehen)

Kaiser-F
17.09.2005, 00:35
Vielen Dank für eure Tipps!

Hab meine Zeile gerade auf
"way_end[ch] = (uint16_t)((double)way_akt[ch] / factor)"
geändert.

Ihr seid ein Schatz :-) !

Kjion
17.09.2005, 11:01
Moin,

mich würde mal interessieren, wieviel mehr Flashspeicher die Rechnung in dieser Form verbraucht im Gegensatz zu der reinen Integer Rechung. Kannst du das mal kurz ausprobieren ??

MfG Kjion

Marco78
17.09.2005, 16:22
Danke an Michael, durch seinen Tipp "DREISATZ" bin ich draufgekommen.
Ja, Sorry. Ich wollte dir einen Viersatz unterjubbeln. Damit geht es ja nicht.