Jump to content

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


rtrbt

Recommended Posts

ACHTUNG: Arduinos mit AVR-Mikrocontroller (z.b. der Arduino Uno und Mega) haben einen Logikpegel von 5V und brauchen deshalb einen Pegelwandler zwischen dem Arduino und dem Bricklet. Bricklets haben einen Logikpegel von 3,3V.

Edit: Hier die offizielle Dokumentation.

Moin,

Nachdem es in letzter Zeit häufiger Kundenanfragen danach gab, Bricklets direkt mit einem Mikrocontroller zu verwenden, habe ich die letzten Monate entsprechende Bindings entwicklet. Die erste Beta der C/C++ Bindings für Mikrocontroller, mit denen Koprozessor-Bricklets direkt von z.B. einem Arduino über SPI gesteuert werden können, finden sich im Anhang.

Die Bindings sind in eine allgemeine Implementierung und einen Hardware Abstraction Layer (HAL) für konkrete Plattformen unterteilt. Es gibt zur Zeit einen HAL für Arduino-AVR-Boards, einen für ESP32-Boards wie das NodeMCU und einen für das Raspberry Pi (Zero) + HAT (Zero).

Details zum Interface, zur Implementierung eigener HALs für andere Boards usw. finden sich in der Dokumentation.

Künftig wird es von uns eigene Hardware für die Bindings geben, sowohl ein ESP32-basiertes Board auf dem Programme, die die Bindings verwenden geflasht werden können, als auch ein neues Breakout Board, mit dem Koprozessor-Bricklets direkt mit einem Mikrocontroller verbunden werden können. (hier ein Teaser)

Gruß,
Erik

Edit: Beta 8 mit folgender Änderung:

  • Bug gefixt wegen dem Callbacks nach ~ 35 Minuten nicht mehr funktionierten.

tinkerforge_uc_bindings_2_0_0_beta_8.zip

Link zu diesem Kommentar
Share on other sites

Danke für die Bindings = sehr gut. 

ACHTUNG: Tests mit Arduino UNO & MEGA gestoppt, da ohne Pegelwandler 5V -> 3.3V ausgeführt = Gefahr Bricklet Zerstörung.

Siehe Post #1: Arduinos mit AVR-Mikrocontroller (z.b. der Arduino Uno und Mega) haben einen Logikpegel von 5V und brauchen deshalb einen Pegelwandler zwischen dem Arduino und dem Bricklet. Bricklets haben einen Logikpegel von 3,3V.

Aufbau

Arduino UNO <-> Tinkerforge Breakout Bricklet V1 <-> Tinkerforge IO4 Bricklet V2 <-> LED & Push-Button.

Tests

  •  LED 10x blinken (io4led)
  •  Push-button LED an | aus (io4ledbtn)

Im Anhang: Beschreibung (PDF, En), Source Code.

tf-microcontroller-example.zip

bearbeitet von rwblinn
Hinweis Tests gestoppt da kein Pegelwandler verwendet!
Link zu diesem Kommentar
Share on other sites

Moin,

Erstmal danke für die Tests!

Es überrascht mich etwas, dass der Aufbau funktioniert: Der Arduino Uno hat ein Logik-Pegel von 5V, das Bricklet ist 5V kompatibel, schickt selbst aber nur 3,3V über die MOSI-Leitung. Du hast anscheinend das Glück, dass dein Arduino die 3,3V High-Signale versteht, bei meinem hat das nicht funktioniert. Falls du mit irgendeinem Bricklet keine Antworten bekommst, liegt es vermutlich daran.

Zu dem TODO bei deinem Push-Button-Test: Die Callbacks kommennur, wenn du eine Funktion aufrufst, da sonst keine Kommunikation mit dem Bricklet stattfindet. Es gibt aber pro Device eine Callback-Tick-Funktion, z.B.

int tf_io4_v2_callback_tick(TF_IO4V2 *io4_v2, uint32_t timeout_us);

Die Funktion blockiert für die übergebene Zeit (in Mikrosekunden) und liefert Callbacks aus die reingekommen sind. D.h. wenn du in deiner Loop noch Zeit übrig hast ist es sinnvoll bei allen Bricklets bei denen du Callbacks benutzt die Funktion aufzurufen. Wenn du die ganze Zeit mit dem Bricklet kommunizierst, z.b. bei dir mit get_value, werden die Callbacks dann ausgeliefert, wenn sie während des getters reinkommen.

Die Tick-Funktion werde ich mal im Readme dokumentieren. Eventuell kommt noch eine allgemeinere Tick-Funktion, die alle Bricklets mit registrierten Callbacks tickt.

Gruß,
Erik

Edit: Was mir noch aufgefallen ist: In deinem Callback-Handler hast du einen auskommentierten Funktionsaufruf auf dem IO4 Bricklet. Die Bindings unterstützen es aber nicht, aus einem Callback heraus wieder Funktionen aufzurufen, da ein Callback auch mitten in einem anderen Funktionsaufruf ankommen kann.

Link zu diesem Kommentar
Share on other sites

Danke für die weiteren Infos. Fragen dazu:

Die 5V werden dann anscheinend nicht benötigt da die Bricklets mit 3V3 auskommen?
Habe die 5V Leitung zum Breakout weggelassen (nur 3V3) und die Tests funktionieren, sowohl bei einem Arduino UNO und ESP-Wroom-32. 

Das mit dem Callbacks habe ich nicht verstanden.
Erbitte Beispiel, wo ein Input Signal verarbeitet wird.
In meinem Button Beispiel, setzt der Callback nur einen Flag der in der Loop verarbeitet wird.

bearbeitet von rwblinn
Link zu diesem Kommentar
Share on other sites

1 hour ago, rwblinn said:

Die 5V werden dann anscheinend nicht benötigt da die Bricklets mit 3V3 auskommen?

Das hängt vom Bricklet ab. Manche brauchen die 5V, aber bei vielen ist das einfach auf Brickletseite nicht verbunden, weil sie mit 3,3V auskommen.

1 hour ago, rwblinn said:

Das mit dem Callbacks habe ich nicht verstanden.
Erbitte Beispiel, wo ein Input Signal verarbeitet wird.
In meinem Button Beispiel, setzt der Callback nur einen Flag der in der Loop verarbeitet wird.

Die Kurzfassung ist du kannst in deinem Buttonbeispiel anstelle von dem

tf_io4_v2_get_value(&io, io_ret_value);

in der io4ledbtn_loop-Funktion folgendes machen:

tf_io4_v2_callback_tick(&io, 250);

Das blockiert für 250µs und holt Callbacks ab, ohne dass du unnütz den Getter ausführst.

Im Callback selbst hast du

// <TODO> This function is not triggered - need to explore why
// tf_io4_v2_set_selected_value(&io, 0, led_state);

Die Funktion würde nicht ausgeführt werden, weil prinzipiell keine Ausführung von Device-Funktionen in Callbacks erlaubt ist. D.h. das richtige Vorgehen ist, wie du es auch gemacht hast, im Callback nur Informationen zu ändern, die du dann in der Loop benutzt.

Link zu diesem Kommentar
Share on other sites

Im Buttonbeispiel, die Loop geändert:

void io4ledbtn_loop(TF_HalContext *hal) {
	tf_io4_v2_callback_tick(&io, 250);
	// tf_io4_v2_get_value(&io, io_ret_value);

	// Handle button pressed to set the led state on or off.
	// The values button_pressed and led_state are set in the button callback io4ledbtn_cb_input_value()
	if (button_pressed == true){
		tf_hal_log_info("LED state set to %s", led_state ? "true" : "false");
		tf_io4_v2_set_selected_value(&io, 0, led_state);
	}
}

Die Bedingung wird ausgeführt, aber nicht die Function tf_io4_v2_set_selected_value(). Habe verschiedene timeouts probiert.

Push-button pressed channel 1. Led state true
LED state set to true

Übrigens: der Test io4led erfolgreich auf folgende Microcontroller: Arduino UNO, Arduino MEGA, ESP-WROOM-32.

Link zu diesem Kommentar
Share on other sites

Teste bitte mal die Version der Bindings im Anhang. Ich glaube ich habe das Problem gefunden.

Außerdem habe ich die io4ledbtn.c leicht verändert angehangen, die Signatur deines Callbacks passte nicht ganz: Der erste Parameter ist entgegen der Dokumentation ein Zeiger auf das Device von dem das Callback kam. Das muss ich mal ins README schreiben. Außerdem benutze ich da jetzt den callback_tick und gebe eventuelle Fehler aus. (Nur den Return Code, für die Fehlerbeschreibungen ist der RAM des Unos zu klein)

PS: Ich habe gerade keinen Button zur Hand und deshalb nur mit einem Kabel Pin 1 der IO-4 auf Masse gezogen, ist das beabsichtigt, dass man immer zweimal drücken muss, damit die LED an oder aus geht?

io4ledbtn.c tinkerforge_uc_bindings_2_0_0_rwblinn.zip

Link zu diesem Kommentar
Share on other sites

Funktioniert. Klasse! Danke.

Logik angepasst = nur jeweils einmal drücken um LED ON oder OFF zu setzen. Im Anhang die letzte Version.

PS:

Im Anhang noch ein ino Beispiel wo keine externe  Deklarationen verwendet werden = "alles in einem" - wollte mal checken ob direkt aus der ino, Tinkerforge Funktionen aufgerufen werden können.

io4ledserial.ino

io4ledbtn.c

bearbeitet von rwblinn
io4ledbtn.c improved
Link zu diesem Kommentar
Share on other sites

2 hours ago, rwblinn said:

Funktioniert. Klasse! Danke.

Sehr gut, ich habe den Fix mal in Beta 2 gepackt.

Der hal_arduino_avr heißt jetzt hal_arduino. Ich habe den HAL hier auch mit einem Teensy (der hat einen ARM-Prozessor) getestet, deshalb die Namensänderung.

2 hours ago, rwblinn said:

Im Anhang noch ein ino Beispiel wo keine externe  Deklarationen verwendet werden = "alles in einem" - wollte mal checken ob direkt aus der ino, Tinkerforge Funktionen aufgerufen werden können.

Das hatte ich übrigens nur so gebaut, damit ich die selbe Demo-Implementierung für die Linux-Demo benutzen kann, der Arduino-Compiler läuft in Linkerprobleme wenn man ein normales Include benutzt weil die demo eine .c, keine .cpp ist.

Link zu diesem Kommentar
Share on other sites

Danke - Post #2 mit Source angepasst.

Nächster Test: Multiple Bricklets an einem Arduino MEGA & NodeMCU.
Fragen dazu (bin kein Elektroniker):

  • Welchen "tri-state buffer chip" wird empfohlen (muß ich bestellen, zB 74HC125)?
  • Erbitte Wiring Beispiel(e) für 2-3 Bricklets?
  • Können an einem Breakout (v1) mehrere Bricklets angeschlossen werden?
  • Wie werden die Ports für die einzelnen Bricklets deklariert?
  • Macht es Sinn bei der Port deklaration auch die Bricklet UID zu definieren?
bearbeitet von rwblinn
Link zu diesem Kommentar
Share on other sites

1 hour ago, rwblinn said:
  • Welchen "tri-state buffer chip" wird empfohlen (muß ich bestellen, zB 74HC125)?
  • Erbitte Wiring Beispiel(e) für 2-3 Bricklets?

Da würde ich zusammengefasst sagen "Nimm nur das NodeMCU und kauf dir ein HAT Zero Brick ". Das hat die korrekten Trenner/Buffer-Chips und du kannst 4 Bricklets anschließen ohne dass du Breakout Bricklets brauchst. Das HAT hat auch 3,3V Logikpegel, also sollte es am NodeMCU funktionieren.

Wenn du das am Arduino machen willst, musst du dann doch etwas basteln: Wir benutzen den 74AHC244 als Trenner-Chip, damit kann man zwei Ports auf einmal verschalten. Der 74AHC125 scheint das Äquivalent dafür zu sein, wenn du nur einen Port brauchst. Du kannst dir die Schaltung vom bald erscheinenden Breakout Bricklet 2.0 ansehen, den ich angehangen habe. Die Schaltung oben links mit dem LD1117 und den CONN_01X03 Jumper kannst du weglassen, wenn du direkt 3,3V und 5V vom Arduino bekommst, für das Breakout Bricklet haben wir das eingebaut, damit man nur 5V einspeisen muss. Die 82Ω Widerstände und die 220pF Kondensatoren brauchst du auch nicht unbedingt. Der Rest sollte auf einem Breadboard aufbaubar sein.

2 hours ago, rwblinn said:

Können an einem Breakout (v1) mehrere Bricklets angeschlossen werden?

Nein. (Bzw. nur ein Isolator Bricklet und dahinter ein anderes)

2 hours ago, rwblinn said:

Wie werden die Ports für die einzelnen Bricklets deklariert?

Die Ports sind nicht an die Bricklets gekoppelt: Du musst nur deklarieren, welche Ports es gibt und welcher Pin der zugehörige Chip-Select-Pin ist. Die HAL-Initialisierung fragt dann einmal an allen Ports ab, welche Bricklets angeschlossen sind.

2 hours ago, rwblinn said:

Macht es Sinn bei der Port deklaration auch die Bricklet UID zu definieren?

Nein, weil du pro Port mehr als ein Bricklet angeschlossen haben kannst. (Im Moment z.B. wenn du ein Isolator Bricklet benutzt)

breakout-V2.pdf

Link zu diesem Kommentar
Share on other sites

6 hours ago, rtrbt said:

Da würde ich zusammengefasst sagen "Nimm nur das NodeMCU und kauf dir ein HAT Zero Brick ". Das hat die korrekten Trenner/Buffer-Chips und du kannst 4 Bricklets anschließen ohne dass du Breakout Bricklets brauchst. Das HAT hat auch 3,3V Logikpegel, also sollte es am NodeMCU funktionieren.

Danke für die ausführliche Infos. Werde es (erstmal) mit einem HAT Zero Brick versuchen …

Link zu diesem Kommentar
Share on other sites

  • 2 weeks later...

ACHTUNG: Tests mit Arduino UNO & MEGA gestoppt, da ohne Pegelwandler 5V -> 3.3V ausgeführt = Gefahr Bricklet Zerstörung.

Siehe Post #1: Arduinos mit AVR-Mikrocontroller (z.b. der Arduino Uno und Mega) haben einen Logikpegel von 5V und brauchen deshalb einen Pegelwandler zwischen dem Arduino und dem Bricklet. Bricklets haben einen Logikpegel von 3,3V.

Update: Habe das HAT Zero erhalten und gestartet mit einem Arduino MEGA und LED Blink Beispiel.
Frage vorweg
Wie müßen die CS Ports im ino Sketch deklariert werden. Lt Doku gibt es 5 CS pins aber 4 Ports A-D?
Wiring: Chip Select Pins HAT (=MEGA): 13 (=53), 16(=49), 18(=47), 15(=45), 22 (=43)


1 Bricklet IO4 an Port A

Deklaration ino Sketch: TF_Port ports[1] = { { .chip_select_pin=53, .port_name='A' }};

LED Blink Beispiel funktioniert.
Hinweis: nach Upload, Power HAT Zero wegnehmen und wieder anschließen sonst passiert nichts oder Fehler -10 erscheint nach der HAT Init.

2 Bricklets IO4 (Port A) und IO16 (Ports B)

Deklaration ino Sketch: TF_Port ports[2] = { { .chip_select_pin=53, .port_name='A' }, { .chip_select_pin=49, .port_name='B' }};

Fehlermeldung -10 nach der HAT Init.

 

Denke: Das Wiring x-mal überprüft, ist doch ein wenig unübersichtlich = vieleicht besser auf das neue Tinkerforge ESP32 Board warten.

bearbeitet von rwblinn
Hinweis: Test gestoppt da ohnen pegelwandler ausgeführt.
Link zu diesem Kommentar
Share on other sites

Moin,

24 minutes ago, rwblinn said:

Habe das HAT Zero erhalten und gestartet mit einem Arduino MEGA und LED Blink Beispiel.

Nimm wie gesagt nur das NodeMCU mit dem HAT, der MEGA hat 5V Logikpegel, damit kannst du dir im schlimmsten Fall das HAT beschädigen. (Das HAT hat 3,3V Logikpegel, ich habe bei mir auf dem Tisch eins liegen, dass ich vermutlich genau mit diesem Fehler kaputt gemacht habe.)

29 minutes ago, rwblinn said:

Wie müßen die CS Ports im ino Sketch deklariert werden. Lt Doku gibt es 5 CS pins aber 4 Ports A-D?
Wiring: Chip Select Pins HAT (=MEGA): 13 (=53), 16(=49), 18(=47), 15(=45), 22 (=43)

Es gibt 5 CS-Pins, weil das HAT Zero selbst einen Koprozessor hat. Aus Sicht der Bindings ist das HAT auch ein Brick, mit dem du Daten austauschen kannst. Im Sketch musst du die Pins auflisten, die du auf deiner Seite benutzt, also wenn du den MEGA weiter benutzt (mach das wie gesagt nicht!) wäre folgendes korrekt:

TF_Port ports[5] = {{
    .chip_select_pin=53,
    .port_name='A'
}, {
    .chip_select_pin=49,
    .port_name='B'
}, {
    .chip_select_pin=47,
    .port_name='C'
}, {
    .chip_select_pin=45,
    .port_name='D'
}, {
    .chip_select_pin=43,
    .port_name='I'
}};

 

Ich vermute, dass deine Probleme mit dem Fehler -10 (errors.h sagt, das ist TF_E_DEVICE_NOT_FOUND) daher kommen, dass der MEGA die Antworten der Bricklets nicht versteht, weil ein 3,3V High nicht stark genug ist.

Link zu diesem Kommentar
Share on other sites

Erstmal Danke an Erik!

Sehr cool, dass das jetzt möglich ist. Ich werde damit in Zukunft viel arbeiten, das steht fest!

Ich habe ein paar schnelle Tests gemacht, nur um zu schauen, wie der Workflow ist und ob alles so arbeitet, wie es sollte.
Ich habe ein RaspBerry Pi 4 mit TF Hat genommen, um 4 Accelerometer V2 Bricklets anzusteuern. Ich habe die entsprechende demo.c angehangen (Achtung: Quick & Dirty Programmierung).

Es hat alles auf Anhieb funktioniert. Ich checke in der kommenden Woche, was passiert, wenn ich die Continuous Callback verwenden an allen 4 Bricklets und wie hoch ich gehen kann damit.

Cheers,
Claudio

demo.c

Link zu diesem Kommentar
Share on other sites

Hi Claudio,

Die Demo sieht soweit gut aus, es kann aber sein, dass du den Callback-Tick pro Device etwas länger ziehen musst, auf dem Raspberry Pi kann es dir bei ungünstigem Scheduling sonst passieren, dass du auf einem Device garnicht tickst und auf einem anderen dafür mehr, ist ja kein Echtzeitsystem. Bei meinen Tests meine ich mich daran erinnern zu können, dass etwas im Bereich 500 oder 1000µs besser lief.

Ob das Continuous Callback schneller läuft als mit dem Brick Daemon würde mich auf jeden Fall auch interessieren, poste da mal deine Ergebnisse, wenn du getestet hast. Ich benutze im Linux-HAL zwar den selben Code wie der Brick Daemon, aber es gibt ein paar Stellen an denen ich aggressiver mit den Timings sein kann (Zielplattform sind ja Mikrocontroller), da kann es gut sein, dass du etwas mehr Performance rausziehen kannst.

1 hour ago, cl- said:

Wenn du sagst, dass High Level Callbacks nicht unterstützt werden, was genau meinst du damit?
Sind beispielsweise die Connect, Disconnect oder Enumerate Callbacks High Level?

Die drei Callbacks gibt es garnicht, Connect und Disconnect fallen weg weil kein Brick Daemon läuft, zu dem man sich verbinden kann, Enumerate fällt weg, weil die Bindings beim hal_init nachsehen, welche Devices angeschlossen sind, danach aber nur mit diesen Devices arbeiten.

High-Level-Callbacks sind Callbacks, die High-Level-Funktionen sind. (So weit, so nichtssagend). High-Level-Funktionen sind alle die intern streamen, also einen Funktionsaufruf der Bindings, bestes Beispiel dafür ist write_pixels vom LCD 128x64, in mehrere Pakete aufteilen, weil der Payload (in diesem Fall bis zu 128*64 boolsche Werte) nicht in ein Paket passt. High-Level-Callbacks sind dann zum Beispiel die für die Thermal-Bilder vom Thermal Imaging Bricklet oder das Spektrum des Sound Pressure Level Bricklets.

Edit: Alle High-Level-Funktionen (also auch die Callbacks) haben eine entsprechende Low-Level-Variante. Man kann die Funktionalität also trotzdem verwenden, muss sich aber das Streaming selbst implementieren. Ich habe die High-Level-Callbacks mit vorgegebener Streaming-Implementierung rausgenommen, da die Daten temporär gehalten werden müssen, sie aber definitiv zu groß für den RAM eines Mikrocontrollers sind. (z.B: 80*60*2 =9600 Byte für das Thermal-Bild) Wenn man als User jetzt seine eigene Streaming-Logik implementiert, kann man z.b. ohne dass man das ganze Thermal-Bild im RAM hält, die wärmste Stelle im Bild finden.

Link zu diesem Kommentar
Share on other sites

1 hour ago, rtrbt said:

Die Demo sieht soweit gut aus, es kann aber sein, dass du den Callback-Tick pro Device etwas länger ziehen musst, auf dem Raspberry Pi kann es dir bei ungünstigem Scheduling sonst passieren, dass du auf einem Device garnicht tickst und auf einem anderen dafür mehr, ist ja kein Echtzeitsystem. Bei meinen Tests meine ich mich daran erinnern zu können, dass etwas im Bereich 500 oder 1000µs besser lief.

Den Callback-Tick habe ich noch nicht ganz verstanden.Ich gebe zu, ich konnte mir die API noch nicht im Detail angucken und weiß daher nicht genau, wie die callback_tick Routinen funktionieren.

Wenn das Programm für 1000 µs blockiert und Callbacks eines Bricklets empfängt und ausliefert, dann verpasse ich doch die Callbacks der anderen angeschlossenen Bricklets oder nicht? Daher habe ich 250 µs gewählt, damit ich zumindest in 1 ms alle (in meinem Fall) vier Bricklets abfragen kann.

Link zu diesem Kommentar
Share on other sites

Der Tick macht genau was du schreibst: Er blockiert für die übergebene Zeit und liefert Callbacks des Bricklets aus. Da auf einem Mikrocontroller kein Multitasking verfügbar sein muss, muss man das als Nutzer händisch machen.

1 hour ago, cl- said:

Wenn das Programm für 1000 µs blockiert und Callbacks eines Bricklets empfängt und ausliefert, dann verpasse ich doch die Callbacks der anderen angeschlossenen Bricklets oder nicht? Daher habe ich 250 µs gewählt, damit ich zumindest in 1 ms alle (in meinem Fall) vier Bricklets abfragen kann.

Hm, da hast du recht, für deinen Fall sind die 250µs wirklich sinnvoller. Noch ein Gedanke: Du willst ja eigentlich nicht die 250µs stehen, sondern falls innerhalb dieser Zeit ein Callback ankam weißt du ja, dass das nächste erst eine Millisekunde später kommen kann. Die Restzeit kannst du sinnvoller verwenden. D.h. ich baue in den Callback-Tick ein, dass du einen Timeout von 0 mitgeben kannst, das heißt dann "versuche ein Callback zu empfangen, falls gerade eins da ist, wenn nicht, brich sofort ab". Dann kannst du in deiner Loop alle Callbacks mit 0 als Timeout ticken und solltest damit nochmal deutlich effizienter sein.

Du kannst ein ähnliches Verhalten testen, indem du TF_TFP_SLEEP_TIME_US in bindings/tfp.c auf z.b. 50 setzt und dann die Callback-Ticks mit z.B. 100µs aufrufst.

Link zu diesem Kommentar
Share on other sites

Wenn ich den Brick Daemon am Raspberry Pi deinstalliert habe, bekomme ich keinen Kontakt zu den Bricklets.
Es geht bei mir nur, wenn ich den Brick Daemon habe und nach jedem Neustart deaktiviere.

Could not open '/sys/class/gpio/gpio25/direction': EACCES (13)Failed to init hal: failed to set GPIO direction (return code -101)
Failed to create acc: -10
Segmentation fault

Was mache ich falsch?

Link zu diesem Kommentar
Share on other sites

Anbei meine erweiterte Demo für die 4 Accelerometer V2 Bricklets mit Continuous Callback.
Aus irgendeinem Grund funktioniert aber das normale (Periodic) Callback nicht mehr, seit ich den Continuous Callback drin habe.
Ist da irgendeine Reihenfolge falsch?

Ich komme mit den Default Werten für TF_TFP_SLEEP_TIME_US  (250) und 250 us Tick-Intervall in der demo_loop() auf jeweils ca. 12500 Werte für alle vier Accelerometer V2 Bricklets. Ich lese eine Achse bei 16 Bit Auflösung.

Das ist noch vergleichbar mit dem Brick Daemon. Ich denke aber, dass man das noch optimieren kann.

demo_continuous.c

bearbeitet von cl-
Link zu diesem Kommentar
Share on other sites

Moin,

2 hours ago, cl- said:

Could not open '/sys/class/gpio/gpio25/direction': EACCES

Das sieht aus als ob du das Programm nicht als Root startest. Die Root-Rechte brauchst du, damit du auf die SPI-Schnittstelle zugreifen darfst.

2 hours ago, cl- said:

Aus irgendeinem Grund funktioniert aber das normale (Periodic) Callback nicht mehr, seit ich den Continuous Callback drin habe.
Ist da irgendeine Reihenfolge falsch?

Eventuell musst du erst set_configuration und dann die set_(continuous_)acceleration_callback_configuration aufrufen. Habe ich aber nicht getestet.

2 hours ago, cl- said:

Ich komme mit den Default Werten für TF_TFP_SLEEP_TIME_US  (250) und 250 us Tick-Intervall in der demo_loop() auf jeweils ca. 12500 Werte für alle vier Accelerometer V2 Bricklets.
Das ist noch vergleichbar mit dem Brick Daemon. Ich denke aber, dass man das noch optimieren kann.

Ich habe das hier mal getestet und komme auf 1850 Pakete pro Sekunde, also ~13900 Werte pro Sekunde und Bricklet (d.h. es würde für 16-Bit-Werte einer Achse mit 12800Hz reichen). Ich habe dabei die BRICKLET_STACK_SPI_CONFIG_MAX_SPEED_HZ in hal_linux.c auf 2000000 gesetzt, dann läuft die SPI-Kommunikation mit 2MHz, was das Maximum ist, das die Bricklets können und die Callback-Tick-Optimierung eingebaut, die ich erwähnt hatte.

Ich habe dann mal mit dem Logic-Analyzer nachgesehen und festgestellt, dass selbst wenn ich das Programm "ideal" optimieren würde, man nicht die 25600 Hz bei 4 Bricklets schaffen wird: Der Linux-Kernel macht zwischen jedem Byte eine etwas längere Pause, die effektive Durchschnittsfrequenz liegt bei ~1.76 MHz. Mit der Frequenz kommt man dann auf 220 kByte/s unter der Annahme, ich könnte durchgängig Daten per SPI senden/empfangen. Ein Paket hat 60 Byte Payload, dazu 11 Byte Protokollheader usw. macht 71 Byte, ergibt ~3100 Pakete pro Sekunde, also 23250 Werte pro Sekunde und Bricklet.

Durch den Overhead des Programms und weil ich auch auf das Bricklet warten muss, bis ein Acknowledgement kommt usw. verliert man dann nochmal einiges, auch deshalb, weil es vorkommt, dass aufgrund von Timing-Pech das Pi oder das Bricklet ein Paket neu senden muss.

Falls du wirklich den vollen Durchsatz brauchst, sehe ich als Option nur noch, dass du einen ESP32 nimmst. Der ESP hat zwei benutzbare SPI-Einheiten, theoretisch könntest du einen HAL für jede SPI-Einheit anlegen und dann die HALs und deren zugeordnete Bricklets mit jeweils einem eigenen Thread auf einem der zwei Kerne des ESPs laufen lassen.

Link zu diesem Kommentar
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.

Gast
Reply to this topic...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...