mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Unsigned Integer Wert auf 100er Runden


Autor: Da Kn (studentdk)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Karl Zeilhofer (griffin27)
Datum:

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

Autor: Da Kn (studentdk)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Karl Zeilhofer (griffin27)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry, wenn der wert um 24150 herum liegt....

Autor: Da Kn (studentdk)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Karl Zeilhofer (griffin27)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf welcher Hardware arbeitest du?

Autor: Karl Zeilhofer (griffin27)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Da Kn (studentdk)
Datum:

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

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
in welcher form wird es über die UART übertragen? Wenn es in Textform 
ist könnte man es auch dort Manuell runden.

Autor: Da Kn (studentdk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nee nee das mit der UART geht nicht.
Ich muss die Werte vorher im Controller gerundet haben.

Autor: Tim T. (tim_taylor)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: Da Kn (studentdk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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:
Wert = Wert + 50;
if(Wert >= 100)
   //setze letzten 2 stellen auf 0;
else
   Wert = 0;

Aber wie setze ich in C die letzten zwei Stellen auf 0?

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Korrektur, man kommt natürlich auch mit nur einmal Modulo aus:
rest = wert % 100;
wert -= rest;
if (rest >= 50)
    wert += 100;

Autor: Karl Zeilhofer (griffin27)
Datum:

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

Autor: Da Kn (studentdk)
Datum:

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

Autor: Karl Zeilhofer (griffin27)
Datum:

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

Autor: Uhu Uhuhu (uhu)
Datum:

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

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie werden denn die Daten im UART-Empfänger weiterverarbeitet?

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?
  value = ((value + 64)>>7);
  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:
  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!

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: Karl Zeilhofer (griffin27)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: Stefan (Gast)
Datum:

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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man es eh an den PC sendet kann der doch auch das runden 
übernehmen...

Autor: Da Kn (studentdk)
Datum:

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

Autor: Peter (Gast)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da Kn wrote:

> Als String printen ist keine Lösung für mich, muss es mit dem unsigned
> int Wert machen.

Warum?

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Threadersteller:
Wie Schiebst du die Werte letztendlich über die UART?
Irgendwas mit ITOA oder als Struct?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Tim T.:

Du hättest das mit dem Quark ruhig drin lassen können, stimmte ja.
Mein Blick auf den Code dort oben war doch etwas zu kurz.

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
Messwert += 50;
if (Messwert<100) Messwert=0;
else Messwert-=Messwert%100;
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:
if (Messwert>MesswertAlt+100){
 MesswertAlt=Messwert;
 Sende(Messwert);
};
elseif (Messwert+100<MesswertAlt){
 MesswertAlt=Messwert;
 Sende(Messwert);
};
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...

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie wärs damit:
unsigned Messwert();    // Gibt den nächsten Meßwert zurück

unsigned P10[] = { 10000, 1000, 100 };
long V = (long) Messwert();
unsigned R = 0;

for (int i = 0; i < arraysize(P10); i++) {
   do {
      V -= P10[i];
      R += P10[i];
   } while (V >= 0);
   V += P10[i];
   R -= P10[i];
}

if (V >= 50)
   R += 100;

// In R steht jetzt der auf 100 gerundete Wert

Obs letztlich kleiner/schneller ist, als Code mit Division weiß ich 
nicht... Das Prinzip läßt sich noch verfeinern.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, V und R. Bessere Variablen namen hätten es aber auch nicht sein 
können :P

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Variablennamen schreibt man zusammen.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist richtig.

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Version 2:
unsigned Messwert();    // Gibt den nächsten Meßwert zurück

unsigned P[] = { 51200, 25600, 12800, 6400, 3200, 1600, 800, 400, 200, 100 };
long V = (long) Messwert();
unsigned R = 0;

for (int i = 0; i < arraysize(P); i++) {
   if ((V -= P[i]) < 0)
      V += P[i];
   else
      R += P[i];
}

if (V >= 50)
   R += 100;

// In R steht jetzt der auf 100 gerundete Wert

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Me ss wert mit mit doppel s geschrieben.... ;)

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein.

Autor: Sebastian B. (mircobolle)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
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:
unsignend int a = 30154;
unsignend char rest;

rest = a % 100; /* an modulo in irgendeiner form kommt man nicht vorbei */

a = a  - rest;

if (rest >= 50){
  a = a + 100;
}else{
  /* nix */
}



GRUß ;-)

Autor: dom (Gast)
Datum:

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

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.