serielle Schnittstelle ohne Datenverluste lesen

Hi

Ich habe gerade etwas Zeit und will an meinem Ambilight weiter basteln.
Das Ambilight ist mit einem Arduino Pro Mini und einem FDTI-Breakout hardwareseitig aufgebaut und daran hängt ein WS2812b Stripe mit 90 LEDs.
Angebunden ist das ganze an ein XBMC mit Boblight-Addon per USB. Das Addon kommuniziert über einen Port 19333 mit dem sogenannten BoblightDaemon. Dieser wiederum schickt die LED-Daten + 6 Byte Prefix dann über die serielle Schnittstelle zum Arduino. Soweit zum Überblick.

Der Arduino empfängt also 276 Bytes (90*3 +6) über die Schnittstelle. Ich habe gelesen, dass der Eingangspuffer im Arduino 64 Bytes beträgt.

Frage:
Weiss jemand was passiert, wenn ich die Daten dort nicht schnell genug abholen kann?
Der BoblightDaemon sendet die 276 Bytes am Stück. Gehen dann Daten am Ende des Puffers verloren oder am Anfang?

Gruß
Jarny

Siehe hier:

Meine Antwort hat das Problem übersehen: das Ansteuern der LED Strips ist so zeitkritisch, dass während dessen die Interrupts deaktiviert werden.

D.h. du musst eine Pause machen, Daten empfangen und erst dann wieder Daten an die LEDs schicken.

Den anderen Thread hatte ich vorher schon gelesen, aber ich habe dort nicht so recht eine Antwort auf meine Frage gefunden.

Mittlerweile habe ich mir ein Javaprogramm geschrieben mit dem ich den BoblightDaemon simulieren kann. Damit kann ich endlich mal ordentliche Tests machen. :smiling_imp:
Ich kann mir beliebige Datenpakete für die 90 LEDs schicken und habe sogar eine CRC8 Prüfsumme eingebaut, so dass ich relativ zuverlässig Übertragungsfehler erkennen kann. Schade, dass die Entwickler vom BoblightDaemon sowas nicht ebenso eingebaut haben. Naja egal.

Habe mal getestet mit welcher Übertragungsrate ich den FTDI-Breakout quälen kann. Er funktioniert bei mir bis 1MBaud zuverlässig. Das hätte ich nicht erwartet :astonished: . Das Datenpaket von 277 Bytes dauert dann etwa 2,8 ms. Nicht schlecht :smiley:
Bis das nächste Datenpaket reinkommt muss man alle Sachen im Arduino erledigt haben sonst gehen Daten verloren weil der Puffer von 64 Byte an der seriellen Schnittstelle dann überläuft. Auch das konnte ich mit meinem Testprogramm simulieren.

Habe mal grob überschlagen und denke, dass meine 90 LEDs WS2812b in ca. 3.5ms beschrieben sein müssten. Empfangen der Daten und Beschreiben der LEDs dauert also zusammen ca. 6-7ms. Die restliche Zeit bis der BoblightDaemon wieder ein Datenpaket sendet hat man also Zeit um beliebige Sachen mit den Daten anzustellen.

Gruß
Jarny

Du kannst aber nicht gleichzeitig Daten an den Strip schicken und serielle Daten empfangen. Der serielle Empfang ist Interrupt-gesteuert und wenn die Interrupts deaktiviert werden wird auch nichts in den Eingangspuffer gelesen.

Die seriellen Puffer kannst du übrigens ab 1.5.7. (glaube ich) auch größer machen. Siehe \Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h

Serenifly:
Du kannst aber nicht gleichzeitig Daten an den Strip schicken und serielle Daten empfangen

Ja, das weiss ich. Es ging ja um die verbleibende Zeit wenn der BoblightDaemon keine Daten schickt und ich im Arduino keine Daten an die LEDs weiterleite. In der Zeit berechnet man zB Gammakorrektur, Fading und andere Interpolationen. Wenn man zu lange an den Sachen berechnet und der Daemon wieder ein Paket schickt gibts Verluste. Wenn man ein Datenverlust nicht erkennt gibts üble sichtbare Fehler von den LEDs.
Will man zB. 25 LED-Updates pro Sekunde, hat man insgesamt 40ms Zeit für den Datenempfang, Berechnungen und LED-Output. Im Daemon kann man das konfigurieren wie oft pro Sekunde man die LED-Daten über die serielle Schnittstelle senden möchte.

Serenifly:
Die seriellen Puffer kannst du übrigens ab 1.5.7. (glaube ich) auch größer machen. Siehe \Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h

Habe hier im Moment die 1.5.6 in Verwendung. Ich hab mal in die HardwareSerial.h geschaut und gesehen, dass dort der Puffer einen konstanten Wert hat und nicht über eine Methode geändert werden kann.
Geht das etwa mit der neueren Version?

Gruß
Jarny

Jarny:
Habe hier im Moment die 1.5.6 in Verwendung. Ich hab mal in die HardwareSerial.h geschaut und gesehen, dass dort der Puffer einen konstanten Wert hat und nicht über eine Methode geändert werden kann.
Geht das etwa mit der neueren Version?

Nein. So flexibel ist das ganze nun auch nicht. Du kannst den Puffer zu beginn des kompilierens auf diese Größe feststellen. Im laufenden Programm kannst du da nichts mehr ändern.

Tatsächlich. In den Sourcen die mit IDE Version 1.5.8 ausgeliefert werden kann man das #DEFINE für SERIAL_RX_BUFFER_SIZE selbst setzen. Cool!
Danke für den Tipp Serenifly und sschultewolter

Gruß
Jarny

Mit dem kleinen Nachteil, dass wenn die Interrupts längere Zeit zu sind, gar nichts vom 1 byte hardware puffer des atmega328p oder atmega2560 (4 Serial USART) gelesen und in den Software Puffer geschrieben wird.

Ich muss das Topic nochmal hochholen weil ich ne Frage bezüglich #defines habe:
In der HardwareSerial.h die bei der Arduino IDE 1.5.8 mitgeliefert wird steht ab Zeile 37 folgender Codeblock:

#if !(defined(SERIAL_TX_BUFFER_SIZE) && defined(SERIAL_RX_BUFFER_SIZE))
  #if (RAMEND < 1000)
   #define SERIAL_TX_BUFFER_SIZE 16
   #define SERIAL_RX_BUFFER_SIZE 16
  #else
   #define SERIAL_TX_BUFFER_SIZE 64
   #define SERIAL_RX_BUFFER_SIZE 64
  #endif
#endif

Ich dachte, wenn ich jetzt in meinem eigenen Programm folgenden Code schreibe:

#define SERIAL_RX_BUFFER_SIZE 512
#define SERIAL_TX_BUFFER_SIZE 8

… dann würde ich den Receive-Buffer auf 512 Bytes und den Transmit-Buffer auf 8 Bytes setzen.
Dem ist aber leider nicht so :frowning: (sehe ich am Speicherverbrauch)
Wenn ich die Puffer in der HardwareSerial.h anpasse funktioniert die Puffergrößenänderung natürlich. Ich will aber verständlicherweise meine Puffer in meinem eigenen Programmen definieren können und nicht in den mitgelieferten CoreLib-Sourcen rumändern.

Kann mir jemand erklären warum meine #defines für die Puffer nicht angenommen werden? Hat das mit irgendeiner Reihenfolge zu tun mit der die Sourcen compiliert werden?

Danke und Gruß
Jarny

Jarny:
Kann mir jemand erklären warum meine #defines für die Puffer nicht angenommen werden? Hat das mit irgendeiner Reihenfolge zu tun mit der die Sourcen compiliert werden?

Wahrscheinlich

Außerdem sollte man Makros erst mal #undef rückgängig machen bevor man sie neu definiert. Aber selbst das wird ziemlich sicher nicht gehen, weil die Core Files wohl zuerst kompiliert werden. Eigentlich logisch, da sich dein Programm ja darauf bezieht.

Hmmm :slightly_frowning_face:

Gibts irgendeine andere Möglichkeit das in meinem Sketch einzustellen? Kann ja nicht im Sinne der Entwickler gewesen sein, dass der User jedesmal in den CoreLib-Sourcen den Code ändern muss um die Puffer einzustellen.
Ich will die Puffergrößen ja nicht einheitlich so haben sondern nur für bestimmte Projekte. Ausserdem kann man den eigenen Code dann nicht wirklich weitergeben.

Hat noch jemand ne Idee das Problem zu lösen? Bin nicht 100% sattelfest mit C/C++.

Gruß
Jarny

PS: #undef hat wie schon vermutet nichts gebracht

Da müsstest du die Library anpassen und die Größe bei begin() übergeben. Dann die Puffer nicht statisch, sondern dynamisch mit new anlegen. Und dann bei end() mit delete wieder löschen.

Und dann muss man noch ein paar andere Dinge hier und da leicht anpassen, weil z.B. die Ring Puffer mit diesen Makros rechnen

Ohne an der Lib rumzufummeln geht es nicht.

Serenifly:
Da müsstest du die Library anpassen und die Größe bei begin() übergeben. Dann die Puffer nicht statisch, sondern dynamisch mit new anlegen. Und dann bei end() mit delete wieder löschen.

Und dann muss man noch ein paar andere Dinge hier und da leicht anpassen, weil z.B. die Ring Puffer mit diesen Makros rechnen

Na das möchte ich nicht. Weiss auch nicht, ob dann alle Optimierungen der Lib noch funktionieren wenn man die Größe dynamisch macht. An einigen Stellen wird ja Modulo Buffersize gerechnet und da weiss ich nicht ob er dann noch bei Puffergrößen die einer Zweierpotenz entspricht die Modulo-Rechnung durch Bitshift ersetzt anstatt zu dividieren.

Serenifly:
Ohne an der Lib rumzufummeln geht es nicht.

Ok. Ich frag mich zwar warum die dann das #if !(defined(SERIAL_TX_BUFFER_SIZE) && defined(SERIAL_RX_BUFFER_SIZE)) im Code stehen haben wenn man es eh nicht umdefinieren kann, aber egal.
Schade, dass es da keine andere Möglichkeit gibt :frowning: .

Gruß
Jarny

Schade, dass es da keine andere Möglichkeit gibt

Tja, die IDE macht es dir einfach, verbaut dir dadurch aber auch ein paar Freiheiten.

Das Problem ist, dass beim Übersetzen von HardwareSerial.cpp nur Arduino.h und HardwareSerial.h vordefiniert sind. In "normaler" Umgebung könntest du beim Compiler-Aufruf mit -D alles mögliche vor der ersten include Datei vorbelegen.

Also, ich glaube, es hilft dir gar nicht, den Puffer zu vergrößern.
Der Puffer wird ja nur gefüllt, wenn Interrupts erlaubt sind.
Und das sind sie ja nicht, wenn der Stripe gerade mit Daten versorgt wird.

Im Daemon kann man das konfigurieren wie oft pro Sekunde man die LED-Daten über die serielle Schnittstelle senden möchte.

Kann der Daemon vielleicht auch xon/xoff, oder ein Hardwarehandshake?
Dann ist das das Mittel der Wahl.

combie:
Also, ich glaube, es hilft dir gar nicht, den Puffer zu vergrößern.
Der Puffer wird ja nur gefüllt, wenn Interrupts erlaubt sind.
Und das sind sie ja nicht, wenn der Stripe gerade mit Daten versorgt wird.
Kann der Daemon vielleicht auch xon/xoff, oder ein Hardwarehandshake?
Dann ist das das Mittel der Wahl.

Doch, der größere Puffer hilft mir weil die Ansteuerung der LEDs nur ein Teil des ganzen ausmacht. Für die ganzen restlichen Berechnungen (Interpolationen, etc.) hab ich den Rücken frei weil er ungestört den Puffer vollschreiben kann ohne dass ich zwischendurch garantiert die Daten abholen muss damit nichts verloren geht :wink:

Gruß
Jarny