USB-Serial C++ Timing Probleme



  • Hallo Zusammen 🙂

    Ich habe ein C++ Programm geschrieben um einen Sensor unter Linux
    auszulesen. Es handelt sich um den CHR-6dm, der per USB angeschlossen
    wird, aber per serieller Schnittstelle (/dev/ttyUSB0) ausgelesen.
    Es handelt sich um einen USB Serial Converter: Future Technology Devices
    International, Ltd FT232 USB-Serial (UART) IC
    Ich kann über diese Schnittstelle mit dem CHR-6dm kommunizieren, was
    auch schon sehr gut klappt. Im Prinzip geht es mir nur darum, möglichst
    schnell Daten auszulesen. Dies ist von 20-300Hz möglich:
    "The CHR-6dm is factory-configured to broadcast angles and angular rates
    at 200 Hz over a TTL UART at 115200 baud. The serial protocol uses 8
    data bits, 1 stop bit, and no parity."
    Der Sensor schickt mir genau 39 Byte die ich lesen möchte.

    Ich öffne also eine serielle Verbindung:

    // Seriell Schnittstelle öffnen und konfigurieren
    int fd_seriell = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY |
    O_NONBLOCK);
    int flags = fcntl(fd_seriell, F_GETFL);
    flags = ( O_DIRECT | O_FSYNC | O_RSYNC | O_DSYNC);
    if (fd_seriell == -1) perror("open_port: Unable to open serial port
    ");
    else fcntl(fd_seriell, F_SETFL, 0);
    leselaenge = 39;
    setzeOptionen(&fd_seriell);

    und konfiguriere sie:
    http://nopaste.immersight.de/?3ad970837b6016ae#JIN...
    Die Zeit zwischen dem Lesen von der Schnittstelle messe ich dabei. Das
    Gerät ist auf 300 Hz eingestellt uns sendet mir damit alle 5,5ms 39
    Byte.
    read((*p_fd_seriell),&stream,leselaenge);//Stream lesen

    Das Ergebnis ist leider nicht zufriedenstellend. Anstatt alle 5,5ms
    einen Wert auszulesen, ergibt sich dieses komische Muster:
    http://nopaste.immersight.de/?d00eb0eb88bf16d7#Z5e...

    Als hässliche Lösung habe ich das nun so gemacht. Vor dem Read setze ich
    den Thread einfach 3ms schlafen:
    boost::this_thread::sleep(boost::posix_time::microseconds(3000));
    Dann ergibt sich ein regelmäßiges Muster wie ich es gerne hätte (der
    Beweis dass es geht):
    http://nopaste.immersight.de/?95e44e07fbf177f3#BmX...

    Ich frage mich warum der read-Aufruf nicht einfach wartet bis wieder 39
    Byte vorhanden sind. Wie kann denn in 3 mal nacheinander in 0.05ms etwas
    ausgelesen werden?

    Vielen Dank schonmal im Voraus! 🙂



  • Ich mache sowas anders:
    1.) Warten auf Daten
    2.) Alle verfuegbaren Daten auslesen
    3.) Parsen der Daten, falls vollstaendig Paket in Messagequeue schreiben
    4.) Goto 1.)

    Ich frage mich warum der read-Aufruf nicht einfach wartet bis wieder 39
    Byte vorhanden sind

    man read schrieb:

    read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

    Also ganz einfach: weil das Verhalten der Funktion nicht so spezifiziert ist.



  • Hab nun die Ausgabe um eine Angabe der gelesenen Byte erweitert:
    http://nopaste.immersight.de/?5a40863665485dd0#1z84uxEzXxHnjDgX6ImBSpSaCAOLHF/BwCyH4vg5v5Q=

    Man sieht deutlich dass er immer nur 39 Byte liest! Ich will eigentlich einen blockierenden Aufruf haben der 4 MilliSekunden blockiert (sollte) und mir dadurch einen super Takt liefert. (Diese Kode läuft in einem eigenen Thread!).
    Also die Aussage das read hier mehr oder weniger als 39 Byte liest lässt sich nicht bestätigen knivil.

    Ich mache sowas anders:
    1.) Warten auf Daten
    2.) Alle verfuegbaren Daten auslesen
    3.) Parsen der Daten, falls vollstaendig Paket in Messagequeue schreiben
    4.) Goto 1.)

    Wie machst du denn das? Mit einem Signal? Ich warte ja auch auf Daten, nämlich bis 39 Byte angekommen sind, das macht doch read? Oder wie soll man das sonst machen?



  • @fabske
    Poste mal den gesamten Code deiner Empfangsschleife.



  • und mir dadurch einen super Takt liefert.

    Etwa 1000 Lines of Code brauche ich dafuer (geschaetzt). Habe gerade mein Projekt nicht zur Hand, um nachzuzaehlen.



  • Wie gesagt, es handelt sich um ein Modul innerhalb eines Frameworks das in einem eigenen Thread (arbeiten()) läuft: http://nopaste.immersight.de/?0051b943683dc619#wdtCo8wtxDqlUCQcTu6YsoLjNFYYjvkOJwjQtIgsoAI=

    Mich würde echt interessieren knivil wie du USB halbwegs echtzeitfähig machst?



  • 1.) Also ein Thread liesst Daten und packt das Paket* in eine Msg-Queue ( http://www.youtube.com/watch?v=309Y-QlIvew&list=PL1835A90FC78FF8BE&index=9&feature=plpp_video )
    2.) Ein anderer Thread wartet solande, bis ein Paket in der Msg-Queue verfuegbar ist.

    Producer & Consumer halt. Die meiste Arbeit ist in die Ansteuerung des Com-Ports geflossen. Unter Windows mit AsyncIO. Echtzeitfaehig ... naja, manchmal liegen 2 Pakete in der Msg-Queue (bei 1000 Hz Sendefrequenz). Aber Windows ist kein Echtzeitsystem.

    *Dazu musst du natuerlich wissen, wie du aus deinem Datenstrom ein Paket machst. Die Anzahl von 39 Byte reicht da nicht bzgl. Uebertragunsfehler oder Synchronisation nicht.



  • @fabske
    Das sieht soweit auf den ersten Blick OK aus ...

    Vielleicht funkt dir der Linux-Treiber des USB/RS232 Adapters dazwischen.
    Ein USB 2.0 Gerät kann man jede Millisekunde pollen (vielleicht auch schneller, hab den genauen Wert vergessen), d.h. für deine Anwendung sollte das reichen. Ein Jitter von 1-2 ms wäre ja vermutlich kein Beinbruch.

    Unter Windows hatte ich mit USB/RS232 Adaptern auf jeden Fall noch kein solches Problem, da waren die Daten immer quasi "sofort" verfügbar (also mit einer Verzögerung < 2ms, was für mich immer vollkommen ausreichend war).

    Ich würde die Frage vielleicht eher im Linux Forum stellen.
    C++ Problem ist es auf jeden Fall keines, und vermutlich auch kein grundsätzliches Problem mit deinem Code.

    Nochwas: mach dir ein kleines Test-Projekt, wo du den ganzen Auswertungs-Code etc. rauswirfst. Nur Lesen von der Schnittstelle + Checksum & Sync prüfen, Erfassung wann was passiert ist (wann wie viel Daten gelesen, war Checksum OK, war Sync OK etc.), und verzögerte Ausgabe. Einerseits um sicherzustellen dass du nicht durch irgendwas anderes Verzögerungen verursachst (z.B. durch die Ausgabe der Debug-Infos, weitere Verarbeitung der Daten - was auch immer). Und andrerseits weil es in so einem Test-Projekt einfacher sein sollte diverse Dinge auszuprobieren.


Anmelden zum Antworten