Rechenoperationen und rundung ausführen

Hallo
ich habe eine LaserAbstand Sensor VL53L0X
der via I2C an einen Arduino Nano die Abstandswerte in mm sendet.

Diese möchte ich durch 32 teilen, mit einer Rundungsfunktion auf eine Ganzzahl bringen und dann in Hi Pegel umwandeln.

Beispiel:
Messwert 617 mm / 32 = 19,28 gerundet = 19
Die 19 sollen in 19 HI Pegel umgewandelt werden. Hi(1us) low(1us) Hi(1us) low(1us) Hi(1us) low(1us) …

Hintergrund:
ist der, das ich dieses Signal mit den 19 Hi pegeln auf einen Zählereingang eines weiteren Microcontroller geben möchte der je nach Abstand zum gemessenen Objekt einen Zählerstand hat.

Verbesserungen sind willkommen, auch einfügen von start und stop bits.

Danke für jede Hilfe

Continuous.ino (797 Bytes)

Rundung:

(617 + 16) / 32 = 19,78 abgeschnitten 19 (625 + 16) / 32 = 20,03 abgeschnitten 20

Kommunikation:

Ohne eine Synchronisation wird das wohl nichts. Da gibt es viele Möglichkeiten. Eine vorhandene Schnittstelle würde ich aber vorziehen, da muß man das Rad nicht neu erfinden. Der Arduino bietet I2C, SPI oder serielle Schnittstellen. Was bietet der "weitere Microcontroller"?

Hallo,

Vorschlag, mathematisch korrekt runden. ;)

value = (int) ((messwert/32.0)+0.5);

Doc_Arduino: Hallo,

Vorschlag, mathematisch korrekt runden. ;)

value = (int) ((messwert/32.0)+0.5);

Was nichts anderes ist als

value = (messwert + 16) / 32;

nur komplizierter, größer und langsamer.

Bei "geteilt durch Zweierpotenz" kann man doch auch shiften:

value = (messwert + 16) >> 5;

Macht der Compiler wahrscheinlich automatisch. Ich finde die Divisionsschreibweise ist leichter lesbar.

slartibartfast: Bei "geteilt durch Zweierpotenz" kann man doch auch shiften:

In Assembler bietet sich sowas an, aber du hast es hier mit einem optimierenden Compiler zu tun. Da ist es nicht immer gut im voraus so extrem zu optimieren. Im Zweifelsfalls kann man sich anschauen was daraus gemacht und eingreifen.

Hallo,

ne Whandall, tut mir ja nun leid, aber wenn hier statt 20 eine 19 als Ergebnis kommt, dann ist das nicht korrekt gerundet.

(617 + 16) / 32 = 19,78 abgeschnitten 19

Zudem kann nicht korrekt gerundet werden, weil in Ganzzahl gerechnet wird. Deshalb meine kleiner Korrekturvorschlag.

Kennst du Äquivalenzumformungen?

(int) ((messwert/32.0)+0.5)
 (int) (messwert/32+1/2)
 (int) (messwert/32+16/32)
 (messwert+16)/32

Es ist das gleiche.

PS. 617 / 32 = 19,28125 und das ist korrekt gerundet = 19

Doc_Arduino:
Zudem kann nicht korrekt gerundet werden, weil in Ganzzahl gerechnet wird.
Deshalb meine kleiner Korrekturvorschlag.

Klack, klack, ich höre Dich auf dem Holzweg :slight_smile:

617 / 32 = 19,28125 soll zu 19 gerundet werden. Daher (int) (617 + 16)/32 = 19
625 / 32 = 19,53125 soll zu 20 gerundet werden. Daher (int) (625 + 16)/32 = 20

Der Lasersensor kann bis 2 m, hatte nicht mal jemand sowas für eine Treppe gesucht?

void setup() {
  Serial.begin(250000);
  int value;
  for (int messwert = 500; messwert < 564; messwert++) {
    Serial.print(F("Messwert = "));
    Serial.print(messwert);
    Serial.print(F(", "));
    value = (int) ((messwert / 32.0) + 0.5);
    Serial.print(value);
    value = (messwert + 16) / 32;
    Serial.print(F(", "));
    Serial.println(value);
  }
}
void loop() {}
Messwert = 500, 16, 16
Messwert = 501, 16, 16
Messwert = 502, 16, 16
Messwert = 503, 16, 16
Messwert = 504, 16, 16
Messwert = 505, 16, 16
Messwert = 506, 16, 16
Messwert = 507, 16, 16
Messwert = 508, 16, 16
Messwert = 509, 16, 16
Messwert = 510, 16, 16
Messwert = 511, 16, 16
Messwert = 512, 16, 16
Messwert = 513, 16, 16
Messwert = 514, 16, 16
Messwert = 515, 16, 16
Messwert = 516, 16, 16
Messwert = 517, 16, 16
Messwert = 518, 16, 16
Messwert = 519, 16, 16
Messwert = 520, 16, 16
Messwert = 521, 16, 16
Messwert = 522, 16, 16
Messwert = 523, 16, 16
Messwert = 524, 16, 16
Messwert = 525, 16, 16
Messwert = 526, 16, 16
Messwert = 527, 16, 16
Messwert = 528, 17, 17
Messwert = 529, 17, 17
Messwert = 530, 17, 17
Messwert = 531, 17, 17
Messwert = 532, 17, 17
Messwert = 533, 17, 17
Messwert = 534, 17, 17
Messwert = 535, 17, 17
Messwert = 536, 17, 17
Messwert = 537, 17, 17
Messwert = 538, 17, 17
Messwert = 539, 17, 17
Messwert = 540, 17, 17
Messwert = 541, 17, 17
Messwert = 542, 17, 17
Messwert = 543, 17, 17
Messwert = 544, 17, 17
Messwert = 545, 17, 17
Messwert = 546, 17, 17
Messwert = 547, 17, 17
Messwert = 548, 17, 17
Messwert = 549, 17, 17
Messwert = 550, 17, 17
Messwert = 551, 17, 17
Messwert = 552, 17, 17
Messwert = 553, 17, 17
Messwert = 554, 17, 17
Messwert = 555, 17, 17
Messwert = 556, 17, 17
Messwert = 557, 17, 17
Messwert = 558, 17, 17
Messwert = 559, 17, 17
Messwert = 560, 18, 18
Messwert = 561, 18, 18
Messwert = 562, 18, 18
Messwert = 563, 18, 18

Die Zeitmessung ergibt eine etwa 11 fach höhere Geschwindigkeit bei Integer Berechnung.

micros float = 2392
micros  int  = 216
unsigned long floatTime;
unsigned long intTime;

void setup() {
  unsigned long von = 0;
  unsigned long bis = 0;

  Serial.begin(250000);
  int value;
  for (int messwert = 500; messwert < 564; messwert++) {
    Serial.print(F("Messwert = "));
    Serial.print(messwert);
    Serial.print(F(", "));
    von = micros();
    value = (int) ((messwert / 32.0) + 0.5);
    bis = micros();
    floatTime += bis - von;
    Serial.print(value);
    von = micros();
    value = (messwert + 16) / 32;
    bis = micros();
    intTime += bis - von;
    Serial.print(F(", "));
    Serial.println(value);
  }
  Serial.print(F("micros float = "));
  Serial.println(floatTime);
  Serial.print(F("micros  int  = "));
  Serial.println(intTime);
}
void loop() {}
Messwert = 500, 16, 16
Messwert = 501, 16, 16
Messwert = 502, 16, 16
Messwert = 503, 16, 16
Messwert = 504, 16, 16
Messwert = 505, 16, 16
Messwert = 506, 16, 16
Messwert = 507, 16, 16
Messwert = 508, 16, 16
Messwert = 509, 16, 16
Messwert = 510, 16, 16
Messwert = 511, 16, 16
Messwert = 512, 16, 16
Messwert = 513, 16, 16
Messwert = 514, 16, 16
Messwert = 515, 16, 16
Messwert = 516, 16, 16
Messwert = 517, 16, 16
Messwert = 518, 16, 16
Messwert = 519, 16, 16
Messwert = 520, 16, 16
Messwert = 521, 16, 16
Messwert = 522, 16, 16
Messwert = 523, 16, 16
Messwert = 524, 16, 16
Messwert = 525, 16, 16
Messwert = 526, 16, 16
Messwert = 527, 16, 16
Messwert = 528, 17, 17
Messwert = 529, 17, 17
Messwert = 530, 17, 17
Messwert = 531, 17, 17
Messwert = 532, 17, 17
Messwert = 533, 17, 17
Messwert = 534, 17, 17
Messwert = 535, 17, 17
Messwert = 536, 17, 17
Messwert = 537, 17, 17
Messwert = 538, 17, 17
Messwert = 539, 17, 17
Messwert = 540, 17, 17
Messwert = 541, 17, 17
Messwert = 542, 17, 17
Messwert = 543, 17, 17
Messwert = 544, 17, 17
Messwert = 545, 17, 17
Messwert = 546, 17, 17
Messwert = 547, 17, 17
Messwert = 548, 17, 17
Messwert = 549, 17, 17
Messwert = 550, 17, 17
Messwert = 551, 17, 17
Messwert = 552, 17, 17
Messwert = 553, 17, 17
Messwert = 554, 17, 17
Messwert = 555, 17, 17
Messwert = 556, 17, 17
Messwert = 557, 17, 17
Messwert = 558, 17, 17
Messwert = 559, 17, 17
Messwert = 560, 18, 18
Messwert = 561, 18, 18
Messwert = 562, 18, 18
Messwert = 563, 18, 18
micros float = 2392
micros  int  = 216

Hallo,

spinn ich jetzt? Ich glaube nicht, weil ich habe es getestet. Wenn ich das rechnen lasse und serial anzeigen, erhalte ich 19 statt 20.

(617 + 16) / 32

probiert es doch einfach mal aus.
Serial.begin(9600);
int value = (617 + 16) / 32;
Serial.println(value);
Edit:

Moment, jetzt weiß ich was ihr wollt. Tja Leute, will ja nicht meckern, aber das ist total unleserlich geschrieben. Muss ich jetzt mal so deutlich sagen. Kein Mensch erkennt ihr die Rundungsfunktion. Ich bekomme 19 angezeigt obwohl mein Taschenrechner 19,78 anzeigt. Der wird wohl erstmal jeder bekloppt in der Birne. Bis man mitbekommt das mit der 617 ohne +16 eigentlich gerundet werden soll.

Gut, jetzt kann ich beruhigt schlafen :sleeping:

Ich denke du warst der irrigen Ansicht (int) würde einen float Ausdruck runden, das ist nicht der Fall, er wird abgeschnitten.

Hallo,

bin zur Zeit mit meinem Zweitrechner etwas frustriert den ich hier nebenbei fit mache. Man sollte bei sowas keine kritischen Postings abgeben ... :astonished: sorry

Edit: Ich denke du warst der irrigen Ansicht (int) würde einen float Ausdruck runden, das ist nicht der Fall, er wird abgeschnitten. Ne anders. Ich hatte Probleme eure +16 als eure Rundungskorrektur zu erkennen. Ich dachte ihr wolltet irgendwas ganzzahliges rechnen und hatte mich echt schon gewundet das ihr alten Hasen das ohne float machen wollt. Ich habe immer alles gerechnet wie es eben da stand und mich gewundert das ihr sagt die Rundung stimmt. Den Trick kannte ich noch nicht.

eine etwa 11 fach höhere Geschwindigkeit bei Integer Berechnung

Und nur die Zeitmessung, ganz ohne Berechnung? Erst wenn du das von beiden Berechnungen abziehst, kriegst du das wahre Verhältnis von integer zu float.

Aber: Wenn man berücksichtigt, dass micros() nur in viererSchritten wächst, sieht man schon bei deiner 216, dass deine Messmethode an ihrer Grenze ist: 216/64 ist kleiner als 4, also gab es bei einigen Durchläufen eine Differenz von 0 zwischen den zwei micros() Aufrufen.

Ein anderes Aber: /32 braucht gar keine Division, sondern nur ein >> 5. Ist also unfair, das mit einer float-Division zu vergleichen

Bei mir war die Berechnung mit explizitem Shift langsamer, aber miss selbst.

Edit: das war aber ein Artefakt der 4 micros Auflösung.

Bei einer Messung über alle 1024 Möglichkeiten bekomme ich

micros float = 27368
micros  int  = 2388
micros int>> = 2384

Hier wird es dann ein Faktor von etwa 12 (und Shift ist genauso schnell, die Division wird also anscheinend in einen Shift umgewandelt.

unsigned long floatTime;
unsigned long intTime;
unsigned long intShiftTime;

void setup() {
  unsigned long von = 0;
  unsigned long bis = 0;

  Serial.begin(250000);
  volatile int value;
  von = micros();
  for (int messwert = 0; messwert < 1024; messwert++) {
    value = (int) ((messwert / 32.0) + 0.5);
  }
  bis = micros();
  floatTime += bis - von;
  von = micros();
  for (int messwert = 0; messwert < 1024; messwert++) {
    value = (messwert + 16) / 32;
  }
  bis = micros();
  intTime += bis - von;
  von = micros();
  for (int messwert = 0; messwert < 1024; messwert++) {
    value = (messwert + 16) >> 5;
  }
  bis = micros();
  intShiftTime += bis - von;
  Serial.print(F("micros float = "));
  Serial.println(floatTime);
  Serial.print(F("micros  int  = "));
  Serial.println(intTime);
  Serial.print(F("micros int>> = "));
  Serial.println(intShiftTime);
}
void loop() {}

michael_x: Und nur die Zeitmessung, ganz ohne Berechnung? Erst wenn du das von beiden Berechnungen abziehst, kriegst du das wahre Verhältnis von integer zu float.

Stimmt. Das Abziehen würde das Verhältnis aber noch weiter verschlechtern.

michael_x: Aber: Wenn man berücksichtigt, dass micros() nur in viererSchritten wächst, sieht man schon bei deiner 216, dass deine Messmethode an ihrer Grenze ist: 216/64 ist kleiner als 4, also gab es bei einigen Durchläufen eine Differenz von 0 zwischen den zwei micros() Aufrufen.

Stimmt. Das Vorgehen habe ich jetzt geändert.

michael_x: Ein anderes Aber: /32 braucht gar keine Division, sondern nur ein >> 5. Ist also unfair, das mit einer float-Division zu vergleichen

Na ja, das Teilen durch 32 war doch die Aufgabe, umso abwegigiger ist da der Float Weg. Abgesehen davon ist eine Division durch 32 auch für Floats keine Division sondern nur eine Exponenten Subtraction.

Doc_Arduino: bin zur Zeit mit meinem Zweitrechner etwas frustriert den ich hier nebenbei fit mache. Man sollte bei sowas keine kritischen Postings abgeben ... :astonished: sorry

Du hast mein Mitgefühl :)

Doc_Arduino: Den Trick kannte ich noch nicht.

Und ich dachte, 0,5 als 16/32 vorher zu addieren, sei so bekannt, daß ich es nicht ausführlich erläutern muß. Mein Fehler ;D

Nun haben wir schon fast ein Kompendium.