Hallo!
Vielleicht kann mir jemand von Euch helfen. Ich möchte einige
Spannungswerte in mV über die UART ausgeben.
Die Werte (Datentyp unsigned int) liegen zwischen 0mV und 35000mV. Damit
die Werte nicht zu sehr schwanken würde ich gern in 100mV Schritten
runden.
Bsp.:
Gemessener Wert: Ausgegebener Wert:
24113mV 24100mV
24159mV 24200mV
Bin mittlerweile schon ziemlich am verzweifeln.
Habt Ihr vielleicht eine Idee???
Gruß
Daniel
Woran verzweifelst Du konkret? Anhand der Erwähnung von unsigned int
gehe ich mal davon aus, dass es sich um C handelt, wobei eine explizite
Erwähnung der Programmiersprache eigentlich zu den
Selbstverständlichkeiten gehört. Und eine Rundung ist in C eigentlich
kein Problem, allerdings müsstest Du dazu schon mal ein paar
Randbedingungen rausrücken...
gerundet = ((wert + 50)/100)*100;
fertig!
Edit: wenn die Werte nicht so sehr schwanken sollen, dann wäre
vielleicht auch ein Filter angebracht. Denn ist der Messwert um 24100
herum, so schwankt es wieder hin und her.
Ja genau es handelt sich un C, hatte ich vergessen zu erwähnen.
Das Problem ist, dass ich keinen float-Wert auf "x" Kommastellen runden
möchte, sondern wie in dem Beispiel angegeben einen unsigned int in
100er Schritten.
Der Wert der sich also in der Variablen befindet beträgt wie im Beispiel
24113. Nun würde ich diesen Wert gerne so runden, dass anschließen 24100
in der Variablen steht.
Fehlt sonst noch etwas?
Da Kn wrote:
> Das Problem ist, dass ich keinen float-Wert auf "x" Kommastellen runden> möchte, sondern wie in dem Beispiel angegeben einen unsigned int in> 100er Schritten.
Schön, die Lösung hat Karl ja mittlerweile geliefert...
Karl Zeilhofer wrote:
> gerundet = ((wert + 50)/100)*100;>> fertig!>> Edit: wenn die Werte nicht so sehr schwanken sollen, dann wäre> vielleicht auch ein Filter angebracht. Denn ist der Messwert um 24100> herum, so schwankt es wieder hin und her.
Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit
für eine weitere Multiplikation und Division habe.
Karl Zeilhofer wrote:
> Edit: wenn die Werte nicht so sehr schwanken sollen, dann wäre> vielleicht auch ein Filter angebracht. Denn ist der Messwert um 24100> herum, so schwankt es wieder hin und her.
Richtig, eine Rundung glättet nur unzureichend. Glätten kann man mit
einer (vernünftigen) Mittelwertbildung.
Da Kn wrote:
> Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit> für eine weitere Multiplikation und Division habe.
Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht
zwar nicht so schön aus, erfüllt aber den Zweck...
Da Kn wrote:
> Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit> für eine weitere Multiplikation und Division habe.
Ach, guck mal da. Genau das sind die Randbedingungen, die Du oben
verschwiegen hast. Dazu gehört neben der Programmiersprache auch, was
für Ressourcen Dir zur Verfügung stehen. So langsam kommt es also
raus...
Der nächste Punkt (nachdem Du Deine Hardware mal beschrieben hast) wäre
jetzt die Frage, was mit den gerundeten Werten überhaupt geschehen soll.
Eine Multiplikation (Division) mit (durch) eine(r) Konstante benötigt
auf den meisten Systemen übrigens gar nicht so viel Zeit. Wenn es nicht
grad um Mikrosekunden geht, ist das relativ unkritisch. Aber wie schon
gesagt: Das hängt von den verdammten Randbedingungen ab, die bislang nur
Du selbst kennst!
Also bei dem Controller handelt es sich um einen MSP430F2272, der mit
einer Taktfrequenz von 16Mhz läuft.
Es sollen 3 Werte in 100er Schritten gerundet werden. Ich habe jetzt
gerade nocheinma nachgemessen. Für die Rundung eines Wertes über das
Verfahren was Karl vorgeschlagen hatte benötige ich für einen Wert eine
Zeit von ca 30us.
Ich könnte mich mit einer Zeit von maximal 10us anfreunden.
Addiere 50 auf deinen Messwert.
Wenn Messwert größergleich 100 ist,
setze die beiden letzten stellen 0.
Sonst
setze Messwert 0.
Randbedingung: Nur Positive Messwerte.
Tim T. wrote:
> Randbedingung: Nur Positive Messwerte.
Weitere Randbedingung: funktioniert nur, wenn der Messwert kleiner 100
ist. Man kann mit dieser Methode also Messwerte kleiner 100 zu 0 oder
100 runden.
Man bräuchte schon noch zusätzlich ein Modulo, und das braucht dann
wieder signifikant Zeit.
> Addiere 50 auf deinen Messwert.> Wenn Messwert größergleich 100 ist,> setze die beiden letzten stellen 0.> Sonst> setze Messwert 0.>>> Randbedingung: Nur Positive Messwerte.
Soweit logisch:
1
Wert=Wert+50;
2
if(Wert>=100)
3
//setze letzten 2 stellen auf 0;
4
else
5
Wert=0;
Aber wie setze ich in C die letzten zwei Stellen auf 0?
Da Kn wrote:
> Soweit logisch:
Nö, denn die Methode rundet bei den meisten Werten (alle größer 100)
nicht, sondern streicht nur die letzten beiden Stellen.
> Aber wie setze ich in C die letzten zwei Stellen auf 0?
Indem du den Modulo 100 abziehst.
Für ein korrektes Funktionieren dieser Methode bräuchte man also zweimal
den Modulo-Operator. Dürfte (wenn überhaupt) nur unwesentlich schneller
sein, wie einmal * und einmal /.
Du kannst zum wert 50 addieren, in einen String printen (sprintf()), und
dort die entsprechenden Zeichen auf '0' setzen.
Diesen String sendest du dann per UART.
Das mit dem Modulo klappt bringt aber nicht wirklich eine Zeitersparnis.
Als String printen ist keine Lösung für mich, muss es mit dem unsigned
int Wert machen.
Karl Zeilhofer wrote:
> Da Kn wrote:>> Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit>> für eine weitere Multiplikation und Division habe.>> Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht> zwar nicht so schön aus, erfüllt aber den Zweck...
Addiere jeweils 2^n Werte auf und schiebe die Summe n Bits nach rechts.
Dann hast du eine Mittelwertbildung, ohne einen Divisionsbefehl
ausführen zu müssen.
Wenn der Wertebereich nicht zu groß ist, kann man auch eine
Lookup-Tabelle verwenden.
1.) In zeitkritischen Anwendungen würde ich auf'm MSP430 nie
Multiplikationen oder gar Divisionen berechnen lassen. Wer µC
programmiert, sollte sich mit dem Binärsystem anfreunden und z.B.
Multiplikationen mit 2, 4, 8, 16, usw. per Shiftoperation durchführen
(Division analog).
Bei 16MHz hast Du in 10µs genau 160 Taktzyklen Zeit die Berechnung
durchzuführen. Das ist nicht wenig aber für Mul und Div zu wenig!
Wie wär's, wenn Du nicht auf 100'er sondern z.B auf 128'er Runden
würdest?
1
value=((value+64)>>7);
2
value=value<<7;
Das dürfte bei 16MHz in etwas mehr als 1µs erledigt sein!
Allerdings wird Dein Compiler da noch was Optimieren wollen,
was man aber auch "von Hand" hinbekommt, wenn man nämlich das Runden
durch Ausmaskieren den unteren Bits bewerkstelligt:
1
value=(value+64)&0xFF80;
Das wären dann 4TZ @ 16MHz = 250ns... schneller geht's kaum!
2.) Wie schon erwähnt wurde, bekämpft man "zappelnde" Werte nicht mit
Runden, sondern mit Mittelwertbildung!
Auch hier ist das Binärsystem Dein Freund, wenn Du 2, 4, 8, usw. Werte
zum Mitteln nimmst, weil dann die Division per Shift wieder einfach und
schnell wird!
Karl Zeilhofer wrote:
> Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht> zwar nicht so schön aus, erfüllt aber den Zweck...
Dann erkläre doch mal, wie man das macht, im Binären mit Shift auf 100
zu runden?
Stefan Ernst wrote:
> Karl Zeilhofer wrote:>> Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht>> zwar nicht so schön aus, erfüllt aber den Zweck...>> Dann erkläre doch mal, wie man das macht, im Binären mit Shift auf *100*> zu runden?
Stefan (Gast) hats bereit genau nach meiner Vorstellung vorgeführt.
Karl Zeilhofer wrote:
> Stefan (Gast) hats bereit genau nach meiner Vorstellung vorgeführt.
Und wo bitte wird da auf 100 gerundet?
Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte
hat.
>Und wo bitte wird da auf 100 gerundet?>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte>hat.
Das war ja auch ein Vorschlag...!
Wenn ich für eine Rundung 30µs brauche aber nur 10µs zur Verfügung habe,
muss man halt Kompromisse eingehen! ;-)
Ja mir war auch klar das es mit schieben viel schneller geht.
Normalerweise würde ich es auch imer so machen.
Der Problemfall hier ist aber das ich in 100er Schritten runden muss.
Naja ich werde mal sehen ob ich das ganze noch etwas schneller
hinbekomme.
>Und wo bitte wird da auf 100 gerundet?>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte>hat.
nur weil es im Decimalsystem nicht schön aussieht ist es doch im
Binärsystem gut gerunden. Man muss halt etwas universeller Denken.
(Es gibt genau 10 Arten von Menschen. Die, die binär verstehen, und die,
die es nicht verstehen.)
Peter wrote:
>>Und wo bitte wird da auf 100 gerundet?>>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte>>hat.> nur weil es im Decimalsystem nicht schön aussieht ist es doch im> Binärsystem gut gerunden. Man muss halt etwas universeller Denken.
Und welchen Zweck erfüllt dann der im Binärsystem gerundete Wert? Gar
keinen, denn eine korrekte Wertberuhigung (wie schon mehrfach erwähnt)
macht man per Mittelwertbildung. Der einzige Grund für eine Rundung sind
halt "schönere" Werte.
Jein, in der Sache ansich richtig, nur mein Ton gefiel mir selber
nicht^^
Abgesehen davon war ich in der Annahme, da ich nicht genau gelesen habe,
das der OP die Messwerte als ASCII überträgt, da ist ein Nullen
natürlich einfach...
Da dem aber wohl nicht so ist, fiele mir auch nichts ein ausser:
1
Messwert+=50;
2
if(Messwert<100)Messwert=0;
3
elseMesswert-=Messwert%100;
4
sende(Messwert);
ein, wobei ich aber denke das der Modulo länger als eine Multiplikation
und Division zusammen braucht.
Ein anderer Ansatz wäre den Messwert erst zu Senden wenn er mehr als
100mV vom alten verschieden wäre:
1
if(Messwert>MesswertAlt+100){
2
MesswertAlt=Messwert;
3
Sende(Messwert);
4
};
5
elseif(Messwert+100<MesswertAlt){
6
MesswertAlt=Messwert;
7
Sende(Messwert);
8
};
Rundet zwar nicht direkt, sorgt aber dafür das das Zappeln aufhört.
Alles in allem aber auch nicht ganz meine Lösungsart, da ich in diesem
Falle eher die Auflösung des ADC runterschrauben würde...
Da Kn wrote:
> Damit> die Werte nicht zu sehr schwanken würde ich gern in 100mV Schritten> runden.>> Bsp.:>> Gemessener Wert: Ausgegebener Wert:> 24113mV 24100mV> 24159mV 24200mV
Was meinst du mit "schwanken" deine ausgegebenen Werte haben ein
größeres Delta als deine gemessenen Werte.
Was du willst ist quantisieren. Also Genauigkeit für die Ausgabe
verschwenden.
Wenn du das Schwanken eindämmen willst, nimm die schon erwähnte Methode
des Mittelwertes, dies geht sehr effektiv mit:
1
y=(x_alt+x_neu)>>1;/* (alt+neu)/2 */
Das mit deinen 100 ist natürlich ne ganz spezielle Anforderung. Wie auch
schon erwähnt wurde kann man sowas mit 2^n Werten am effektivsten
umsetzen.
ansonsten kommt in etwa sowas raus:
1
unsignendinta=30154;
2
unsignendcharrest;
3
4
rest=a%100;/* an modulo in irgendeiner form kommt man nicht vorbei */
Hey Daniel,
seit wann verwendet ihr denn an der FH MSP? ;-)
Achja, wenn du die Werte in ASCII über die UART schickt, dann habt ihr
dort ja auch schon Module und Divison drin, vielleicht kann man dort
optimieren?
Gruß
ein Ex-Mitstudent