Jump to content

Betaversion der C/C++ Bindings für Mikrocontroller


rtrbt
 Share

Recommended Posts

Update: Ich bin zurück auf dem Kernel 4.19.97+ #1294

Also es liegt bei mir an dem core_freq=250 Eintrag. Wenn ich den nicht auf 250 setze (standardmäßig ist nichts definiert in der /boot/config.txt), dann läuft das neue Raspberry Pi HAL nicht. Auch kann ich die BRICKLET_STACK_SPI_CONFIG_MAX_SPEED_HZ nicht auf 1950000 hochsetzen. Nur mit dem Defaultwert (1400000) kann ich bei mir überhaupt Kontakt zu den Bricklets aufnehmen. Aber das Programm findet auch mit dem alten Kernel weiterhin nur selten alle vier angeschlossenen Bricklets.

Mit dem core_freq=250 Eintrag bekomme ich zumindest teilweise die alten Werte wieder (867 Packages). Auf deine Werte aber komme ich nicht (2000 PPS bei 1,4MHz und 2650 PPS bei 1,95MHz).

Edited by cl-
Link to comment
Share on other sites

Hm das stimmt, ich hatte bei mir den core_freq-Eintrag drin. Ich schreibe mir mal auf die Liste, dass ich die core_freq zur Laufzeit abfrage und die SPI-Clock entsprechend kompensiere. Das klappt aber nur, wenn force_turbo=1 ist, sonst schwankt die core_freq zur Laufzeit und ich bekomme nie eine stabile Clock.

Damit die Tests sinnvoll sind habe ich eine neue Speicherkarte mit dem aktuellen Raspberry Pi OS (vom 27.05., Kernel 4.9.118) genommen. Wenn die core_freq nicht gesetzt wird, ist sie auf 400 Mhz, dann führt meine Einstellung auf 1,4 MHz dazu, dass in Wirklichkeit 1,4/250*400 = 2,24 MHz anliegen, deshalb klappt die Kommunikation nicht

Ich komme mit core_freq=250 auf ~1130 PPS mit dem alten hal_linux (1.4MHz), mit dem hal_raspberry_pi sind die Werte so wie ich sie dir geschrieben hatte, also 2000 bzw. 2650.

Mit dem Kernel 5.4.51+ #1330 (den ich gerade über rpi-update geholt habe) sind die hal_raspberry_pi-Zahlen identisch, mit dem hal_linux komme ich gerade auf 990 PPS (1,4 MHz) bzw. 1170 PPS (1.95 MHz).

Warum das bei dir nicht sauber funktioniert, wenn du die core_freq setzt ist mir noch unklar.

Link to comment
Share on other sites

Ich habe jetzt auch mal das aktuelle Raspberry Pi OS genommen, wieder den aktuellen Kernel 5.4.51+ geladen, die core_freq auf 250 gestellt und vier Accelerometer V2 Bricklets angeschlossen. Ich nutze das hal_raspberry_pi (Beta 6) und diesmal habe in dieser Konfiguration keinerlei Fehler mehr bei der Kommunikation.

Das ist absolut topScheinbar war irgendetwas nicht richtig an meiner vorherigen Konfiguration und/oder Software Zusammenstellung.

Ich komme bei 1,40 MHz SPI Speed jetzt auf ca. 2020 bis 2030 PPS (mit vier Accelerometer V2 Bricklets).

Channel 0: Packet counter is 2535 after 5.000476 seconds.
Channel 1: Packet counter is 2535 after 5.000476 seconds.
Channel 2: Packet counter is 2535 after 5.000476 seconds.
Channel 3: Packet counter is 2536 after 5.000476 seconds.
Combined: 2028.006958 packets per second

Mit 1,95 MHz SPI Speed kriege ich jetzt ca. 2750 bis 2770 PPS (mit vier Accelerometer V2 Bricklets).

Channel 0: Packet counter is 3461 after 5.000326 seconds.
Channel 1: Packet counter is 3458 after 5.000326 seconds.
Channel 2: Packet counter is 3461 after 5.000326 seconds.
Channel 3: Packet counter is 3452 after 5.000326 seconds.
Combined: 2766.219482 packets per second

Die 1,40 MHz SPI Speed laufen aber runder, weil ich für alle Kanäle die (fast) identische Anzahl von Paketen bekomme, während ich bei den 1,95 MHz starke Abweichungen erkennen kann.

Die Error Counts der Accelerometer V2 Bricklets geben keine Auskunft über einen möglichen Grund. Für beide SPI Speeds bekomme ich folgende Werte:

Channel 0: Ack Checksum: 0, Message checksum: 0, Count frame: 0, Count overflow: 0
Channel 1: Ack Checksum: 0, Message checksum: 0, Count frame: 0, Count overflow: 0
Channel 2: Ack Checksum: 0, Message checksum: 0, Count frame: 0, Count overflow: 0
Channel 3: Ack Checksum: 0, Message checksum: 1, Count frame: 0, Count overflow: 0

 

Ich gucke in den nächsten Tagen mal, warum die Abweichungen bei 1,95 MHz zu erkennen sind.
Oder liegt es einfach an der Tatsache, dass die Performance des BCM2835 chips für mehr nicht ausreicht?

Link to comment
Share on other sites

On 7/31/2020 at 11:33 PM, cl- said:

Ich gucke in den nächsten Tagen mal, warum die Abweichungen bei 1,95 MHz zu erkennen sind.
Oder liegt es einfach an der Tatsache, dass die Performance des BCM2835 chips für mehr nicht ausreicht?

Aus dem Stehgreif würde ich sagen der Grund ist folgender:

Bei 1,95MHz brauchst du im Idealfall ~ 300µs um ein (volles) Paket zu übertragen. Das Callback-Polling funktioniert intern so, dass die Bindings vom Bricklet ein Byte Daten abfragen, wenn das 0 ist, dann hat es gerade keine Daten zu senden. Dann wird das nächste Bricklet gepollt. Wenn du jetzt pech hast und Bricklet X hat sein Paket gerade noch nicht bereit, dann werden erst die drei anderen Bricklets abgefragt, bevor es wieder dran kommt. Das kostet im schlimmsten Fall (wenn alle anderen Bricklets Pakete bereit haben) ~900µs, also fast eine Millisekunde, also ist Bricklet X selbst wenn jetzt alles perfekt läuft aus dem Takt und hängt immer ein Paket hinterher.

Ich vermute, dass du, wenn du da noch das letzte Paket rausquetschen willst (und vor allem im Takt bleiben), dann musst du die einzelnen Bricklets von Hand ticken, und zwar so, dass du erst wieder alle vier Bricklets tickst, wenn du alle vier Pakete der letzten Runde hast.

Link to comment
Share on other sites

Ah ok. Ich verstehe! Ich habe noch ein paar Fragen dazu.

1 hour ago, rtrbt said:

Das Callback-Polling funktioniert intern so, dass die Bindings vom Bricklet ein Byte Daten abfragen, wenn das 0 ist, dann hat es gerade keine Daten zu senden. Dann wird das nächste Bricklet gepollt.

Hat ein Bricklet nicht immer Daten zu versenden? Kann dieser Zustand nicht nur am Anfang passieren, wenn das Bricklet gerade erst konfiguriert wurde?

1 hour ago, rtrbt said:

Wenn du jetzt pech hast und Bricklet X hat sein Paket gerade noch nicht bereit, dann werden erst die drei anderen Bricklets abgefragt, bevor es wieder dran kommt. Das kostet im schlimmsten Fall (wenn alle anderen Bricklets Pakete bereit haben) ~900µs, also fast eine Millisekunde, also ist Bricklet X selbst wenn jetzt alles perfekt läuft aus dem Takt und hängt immer ein Paket hinterher.

Wenn ich in deinem Beispiel nun nach ca. 900 µs wieder bei Bricklet X angelangt bin und es befrage, bekomme ich dann die zeitlich jüngsten Daten des Bricklets oder noch die alten Daten von vor ca. 900 µs, die ich fast hätte kriegen können? Oder in anderen Worten: Aktualisieren die Bricklets ihre Daten für den Ausgang, auch wenn sie nicht angefragt werden? Die Frage geht in die gleiche Richtung wie oben. Es ist klar, dass die entsprechende Bricklet Konfiguration gewährleisten muss, dass die Samplingrate/Datarate des Sensors hoch genug ist, um auch wirklich aktuelle Daten zu generieren. Sagen wir, während der Zeit von 900 µs hat der Sensor des Bricklets (KX122 in meinem Fall) mehrmals neue Messwerte generiert.

1 hour ago, rtrbt said:

Ich vermute, dass du, wenn du da noch das letzte Paket rausquetschen willst (und vor allem im Takt bleiben), dann musst du die einzelnen Bricklets von Hand ticken, und zwar so, dass du erst wieder alle vier Bricklets tickst, wenn du alle vier Pakete der letzten Runde hast.

Ja genau! Mir geht es primär um den Takt, die immer gleiche, zeitliche Abfolge von Samples. Wenn ich gewährleisten kann, dass es beispielsweise immer um die 300 us sind, die zwischen zwei aufeinanderfolgenden Pakten liegen, dann kann ich das programmatisch kompensieren.

Besten Dank!

Link to comment
Share on other sites

54 minutes ago, cl- said:

Hat ein Bricklet nicht immer Daten zu versenden? Kann dieser Zustand nicht nur am Anfang passieren, wenn das Bricklet gerade erst konfiguriert wurde?

Nicht unbedingt, die Bricklets haben eine Tick-Rate von 1000Hz, es kann also nur ein Paket pro Millisekunde generiert werden.

54 minutes ago, cl- said:

Wenn ich in deinem Beispiel nun nach ca. 900 µs wieder bei Bricklet X angelangt bin und es befrage, bekomme ich dann die zeitlich jüngsten Daten des Bricklets oder noch die alten Daten von vor ca. 900 µs, die ich fast hätte kriegen können? Oder in anderen Worten: Aktualisieren die Bricklets ihre Daten für den Ausgang, auch wenn sie nicht angefragt werden? Die Frage geht in die gleiche Richtung wie oben. Es ist klar, dass die entsprechende Bricklet Konfiguration gewährleisten muss, dass die Samplingrate/Datarate des Sensors hoch genug ist, um auch wirklich aktuelle Daten zu generieren. Sagen wir, während der Zeit von 900 µs hat der Sensor des Bricklets (KX122 in meinem Fall) mehrmals neue Messwerte generiert.

Das funktioniert im Bricklet folgendermaßen: Es hat einen Ringbuffer für 256 16-Bit-Werte, der vom Sensor über einen Interrupt befüllt wird. Wenn der Ringbuffer voll ist, werden die ältesten Werte überschrieben. Das Callback wird im normalen Bricklet-Tick, also jede Millisekunde generiert, dabei wird nachgesehen, ob ein älteres Callback-Paket noch nicht verschickt werden konnte, falls ja wird es nicht überschrieben.

Du bekommst also, wenn du nicht mit den 1000 PPS des Bricklets schritthalten kannst, ältere Werte, aber nur soweit wie der Ringbuffer sie noch hat.

Im Normalfall hast du noch mehr Buffer im Master Brick und Brick Daemon, aber wenn du die Mikrocontroller-Bindings mit dem HAT benutzt fallen die alle weg. Die Bindings selbst haben nur einen Buffer in den ein Paket passt, da bekommst du also nicht noch mehr Latenz.

54 minutes ago, cl- said:

Ja genau! Mir geht es primär um den Takt, die immer gleiche, zeitliche Abfolge von Samples. Wenn ich gewährleisten kann, dass es beispielsweise immer um die 300 us sind, die zwischen zwei aufeinanderfolgenden Pakten liegen, dann kann ich das programmatisch kompensieren.

Ich denke, das musst du dann folgendermaßen machen: Du musst erstmal anhand der Durchsatztests eine Datenrate finden, bei der du die Callbacks alle verarbeitet bekommst. Dann kannst du die Callbacks aktivieren und erstmal alle weglesen, damit die Ringbuffer möglichst leer sind. Danach kannst du die Daten aufzeichnen und dabei durch händisches Schedulen der Callback-Ticks der einzelnen Bricklets (also tf_accelerometer_v2_callback_tick im Gegensatz zu tf_hal_callback_tick) sicherstellen, dass die Messungen nicht auseinanderlaufen. Vermutlich musst du dann noch einen Synchronisierungspunkt erzeugen, indem du einen definierten Impuls auf die Accelerometer gibst.

Edit: die 300µs Übertragungszeit musst du nicht kompensieren, das Accelerometer misst währendessen ja weiter.

Link to comment
Share on other sites

Sorry! Das ist nicht so leicht zu verstehen. Vielleicht brauche ich noch einen Kaffee!

Wenn das Accelerometer Bricklet einen kontinuierlichen 8bit Callback für eine Achse eingestellt hat, dann bekomme ich nach dieser Rechnung 1000 Pakete mit jeweils 60 Werten pro Sekunde. Das macht dann 60.000 Samples pro Sekunde, obwohl der Accelerometer nur 25600 pro Sekunde konvertiert. Heißt das in diesem Fall, dass keine 1000 Pakete pro Sekunde, sondern eben nur 427 gesendet werden, um die 25600 Samples auszuliefern?

9 minutes ago, rtrbt said:

Edit: die 300µs Übertragungszeit musst du nicht kompensieren, das Accelerometer misst währendessen ja weiter.

Das meine ich so, dass wenn man alle Signale zeitlich miteinander korrelieren will, haben alle Samples mit dem gleichen Index in den jeweiligen Arrays/Vektoren im besten Fall den gleichen Zeitstempel. Da das beim nacheinander Pollen nicht möglich ist, ist signal[1].time[0] immer die (beispielsweise) 300 µs älter als signal[0].time[0], signal[2].time[0] immer die 300 µs später als signal[1].time[0], etc. Ich greife ja nacheinander, um eben die 300 µs verschoben, auf den gleichen Index zu, wenn ich einzelne Werte der Reihe nach speichere.

Das ist soweit auch nicht schlimm, als ich den Zeitversatz ja dann kenne und beim Vergleichen der Samples entsprechend des resultierenden Zeitversatzes (Indizes werden verändert) kompensiere. Somit vergleiche ich immer Daten mit möglichst identischen Zeitstempeln miteinander. Oder habe ich hier was nicht verstanden?

Link to comment
Share on other sites

15 minutes ago, cl- said:

Heißt das in diesem Fall, dass keine 1000 Pakete pro Sekunde, sondern eben nur 427 gesendet werden, um die 25600 Samples auszuliefern?

Ja. Die Bricklet-Firmware generiert nur ein Callback-Paket wenn es voll wird, also wenn 30 bzw. 60 Samples im Ringbuffer des Bricklets liegen.

20 minutes ago, cl- said:

Das meine ich so, dass wenn man alle Signale zeitlich miteinander korrelieren will, haben alle Samples mit dem gleichen Index in den jeweiligen Arrays/Vektoren im besten Fall den gleichen Zeitstempel. Da das beim nacheinander Pollen nicht möglich ist, ist signal[1].time[0] immer die (beispielsweise) 300 µs älter als signal[0].time[0], signal[2].time[0] immer die 300 µs später als signal[1].time[0], etc. Ich greife ja nacheinander, um eben die 300 µs verschoben, auf den gleichen Index zu, wenn ich einzelne Werte der Reihe nach speichere.

Das ist soweit auch nicht schlimm, als ich den Zeitversatz ja dann kenne und beim Vergleichen der Samples entsprechend des resultierenden Zeitversatzes (Indizes werden verändert) kompensiere. Somit vergleiche ich immer Daten mit möglichst identischen Zeitstempeln miteinander. Oder habe ich hier was nicht verstanden?

Ich glaube, dass du die 300µs nicht kompensieren musst. Zumindest unter der Prämisse, dass du dir ein Signal auf die Accelerometer geben kannst zur Synchronisierung des Starts: Wenn du das kannst und danach die Callback-Pakete verarbeiten kannst, ohne dass die Ringbuffer der Bricklets sich füllen, dann verlierst du keine Messwerte, das heißt vom Startzeitpunkt aus hast du jeweils eine vollständige Messung. Die Messungen kannst du dann zueinander verschieben, sodass die Startzeitpunkte übereinander liegen und da der Sensor selbst mit einer festen Frequenz misst, ist ein leichter Jitter (also eine Latenzschwankung) auf dem Übertragungsweg egal. Die Übertragung ist ja über den Ringbuffer von der eigentlichen Datenerhebung entkoppelt.

Link to comment
Share on other sites

Moin,

Beta 7 ist jetzt im Post oben. Die Bindings sind jetzt in einem Zustand den ich als "fertig" bezeichnen würde, die Beta lasse ich aber noch bis zum Release der Hardware offen.

Ich freue mich wie immer über jedes Feedback, vorallem zur Dokumentation.

Gruß,
Erik

Link to comment
Share on other sites

  • 11 months later...

Hallo,

Ich habe mich in den letzten Tagen mit dem C++ Binding auseinandergesetzt (siehe dieser Thread bzw dieses Github Repository)

Die Version im Repository funktioniert ganz ordentlich mit einem ESP32, doch wenn ich eine Display & Touchscreen Library über einen separaten SPI Bus einbinde (unabhängige GPIO Pins) sinkt der Loop Aufruf von ~38000x auf ~40x pro Sekunde.

Dadurch sind die Temperatur Werte um mehr als 10 Sekunden verzögert und das Programm generell etwas laggy und somit für einen PID Regler unbrauchbar.

Vermutlich liegt es an dieser Vorgehensweise:

Am 3.8.2020 um 12:12 schrieb rtrbt:

Das funktioniert im Bricklet folgendermaßen: Es hat einen Ringbuffer für 256 16-Bit-Werte, der vom Sensor über einen Interrupt befüllt wird. Wenn der Ringbuffer voll ist, werden die ältesten Werte überschrieben. Das Callback wird im normalen Bricklet-Tick, also jede Millisekunde generiert, dabei wird nachgesehen, ob ein älteres Callback-Paket noch nicht verschickt werden konnte, falls ja wird es nicht überschrieben.

Du bekommst also, wenn du nicht mit den 1000 PPS des Bricklets schritthalten kannst, ältere Werte, aber nur soweit wie der Ringbuffer sie noch hat.

Gibt es einen Mechanismus mit dem man auch aktuelle Temperaturwerte bekommt wenn nur ~40x/sec angefragt wird?

Viele Grüße,

Stefan

PS: Bin langsam am überlegen ob es nicht einfacher ist ein MAX31856 bzw MAX31865 Breackout Board über I2C anzubinden für die Temperaturmessung. Nur für Analog Eingang und Analog Ausgang habe ich keine passenden Breakout Boards gefunden die über I2C angebunden werden..

 

Edit: ich habe die Naheliegende Lösung gefunden: Einfach direkt im Loop die Funktion

tf_thermocouple_v2_get_temperature(&t, &temperature);

aufrufen und den Callback deaktivieren..

Bin dadurch auf einen Loop Count von ~75 pro Sekunde gekommen und die Temperaturwerte sind auch wieder "snappy" und ich "happy" :)

Edited by stif
Link to comment
Share on other sites

Die Getter zu benutzen ist die einfachste Variante, du könntest aber alternativ auch die Callback-Frequenz runterdrehen auf etwas, dass du auf dem ESP verarbeiten kannst. Das hätte den Vorteil, dass weniger Traffic erzeugt wird. (Der Unterschied ist aber im Vergleich zu Stacks über USB nicht so stark). Da die Temperatur typischerweise eher träge ist solltest du mit z.B. 50 ms Callback-Interval noch gut hinkommen.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...