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


von Da K. (studentdk)


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

von Johannes M. (johnny-m)


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...

von Karl Z. (griffin27)


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.

von Da K. (studentdk)


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?

von Johannes M. (johnny-m)


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...

von Karl Z. (griffin27)


Lesenswert?

sorry, wenn der wert um 24150 herum liegt....

von Da K. (studentdk)


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.

von Johannes M. (johnny-m)


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.

von Karl Z. (griffin27)


Lesenswert?

Auf welcher Hardware arbeitest du?

von Karl Z. (griffin27)


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...

von Johannes M. (johnny-m)


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!

von Da K. (studentdk)


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.

von Peter (Gast)


Lesenswert?

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

von Da K. (studentdk)


Lesenswert?

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

von Tim T. (tim_taylor) Benutzerseite


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.

von Stefan E. (sternst)


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.

von Da K. (studentdk)


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:
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?

von Stefan E. (sternst)


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 /.

von Stefan E. (sternst)


Lesenswert?

Korrektur, man kommt natürlich auch mit nur einmal Modulo aus:
1
rest = wert % 100;
2
wert -= rest;
3
if (rest >= 50)
4
    wert += 100;

von Karl Z. (griffin27)


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.

von Da K. (studentdk)


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.

von Karl Z. (griffin27)


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...

von Uhu U. (uhu)


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.

von Martin (Gast)


Lesenswert?

Wie werden denn die Daten im UART-Empfänger weiterverarbeitet?

von Stefan (Gast)


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?
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!

von Stefan E. (sternst)


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?

von Karl Z. (griffin27)


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.

von Stefan E. (sternst)


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.

von Stefan (Gast)


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! ;-)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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

von Da K. (studentdk)


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.

von Peter (Gast)


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.)

von Stefan E. (sternst)


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.

von P. S. (Gast)


Lesenswert?

Da Kn wrote:

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

Warum?

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

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

von Stefan E. (sternst)


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.

von Tim T. (tim_taylor) Benutzerseite


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:
1
Messwert += 50;
2
if (Messwert<100) Messwert=0;
3
else Messwert-=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...

von Uhu U. (uhu)


Lesenswert?

Wie wärs damit:
1
unsigned Messwert();    // Gibt den nächsten Meßwert zurück
2
3
unsigned P10[] = { 10000, 1000, 100 };
4
long V = (long) Messwert();
5
unsigned R = 0;
6
7
for (int i = 0; i < arraysize(P10); i++) {
8
   do {
9
      V -= P10[i];
10
      R += P10[i];
11
   } while (V >= 0);
12
   V += P10[i];
13
   R -= P10[i];
14
}
15
16
if (V >= 50)
17
   R += 100;
18
19
// 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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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

von Uhu U. (uhu)


Lesenswert?

Variablennamen schreibt man zusammen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Ist richtig.

von Uhu U. (uhu)


Lesenswert?

Version 2:
1
unsigned Messwert();    // Gibt den nächsten Meßwert zurück
2
3
unsigned P[] = { 51200, 25600, 12800, 6400, 3200, 1600, 800, 400, 200, 100 };
4
long V = (long) Messwert();
5
unsigned R = 0;
6
7
for (int i = 0; i < arraysize(P); i++) {
8
   if ((V -= P[i]) < 0)
9
      V += P[i];
10
   else
11
      R += P[i];
12
}
13
14
if (V >= 50)
15
   R += 100;
16
17
// In R steht jetzt der auf 100 gerundete Wert

von Simon K. (simon) Benutzerseite


Lesenswert?

Me ss wert mit mit doppel s geschrieben.... ;)

von Uhu U. (uhu)


Lesenswert?

Nein.

von Sebastian B. (mircobolle)


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:
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
unsignend int a = 30154;
2
unsignend char rest;
3
4
rest = a % 100; /* an modulo in irgendeiner form kommt man nicht vorbei */
5
6
a = a  - rest;
7
8
if (rest >= 50){
9
  a = a + 100;
10
}else{
11
  /* nix */
12
}


GRUß ;-)

von dom (Gast)


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

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
Noch kein Account? Hier anmelden.