Wie werden die Anforderungen an die serielle Geschwindigkeit und den Puffer für die Kommunikation zwischen PC und Mikrocontroller berechnet?

8

Ein häufiges Szenario ist ein PC, der Befehle über RS232 an einen Mikrocontroller sendet. Mein PC-Programm sendet Befehle (die jeweils aus mehreren Bytes bestehen) so schnell wie möglich an einen kleinen Roboter. Der Mikrocontroller am Roboter ist ein Parallax-Propeller.

Ich habe festgestellt, dass, wenn ich Bytes auf der Mikrocontrollerseite nicht schnell genug verarbeite, die Standardpuffer in den gängigen Treibern für die serielle Schnittstelle, die für den Propeller verfügbar sind, sehr schnell überlaufen können. (Die Puffer liegen im Allgemeinen zwischen 16 und 256 Byte). Ich kann diese Puffer willkürlich erhöhen oder meinen eigenen größeren Ringpuffer erstellen, möchte jedoch einen methodischeren Ansatz zur Bestimmung der entsprechenden Größenanforderungen und / oder der minimalen Wartezeit, bevor ich Bytes aus dem Treiberpuffer der seriellen Schnittstelle herausziehe .

Auf den ersten Blick:

  • 115200 == 115,2 Bits pro Millisekunde == ~ 12,8 Bytes pro Millisekunde (unter der Annahme von 1 Stoppbit)

1) Ist das eine gültige Methode zur Berechnung des Timings für serielle Übertragungen?

Auch angesichts meines spezifischen Setups:

  • PC-Programm <--> Bluetooth-Treiber für serielle Profile <--> Bluetooth-Transceiver <- * -> BlueSMIRF-Funkmodem <--> Parallax-Propellerprogramm

2) Was ist die maximale Datenmenge, die ich für einen bestimmten Zeitraum konsistent senden kann, ohne dass Probleme auftreten?

Vielleicht bin ich damit fertig, Dinge zu komplizieren, aber es scheint, dass möglicherweise mehrere Puffer in der obigen Übertragungskette enthalten sind. Wie gehen andere gewöhnlich damit um? Drosseln sie den PC, der auf eine bekannte sichere Rate sendet? Flusskontrolle implementieren? Wie wirkt sich dies bei der Implementierung der Flusssteuerung auf die Bandbreite und die Antwortzeiten aus?

(Wenn es darauf ankommt, besteht mein Experiment darin, einen Joystick auf dem PC zu verwenden, um mehrere Servos mit sofortiger Reaktion auf die Joystickbewegungen zu steuern. Jede kleine Bewegung des Joysticks führt also dazu, dass mehrere Befehle an den Mikrocontroller gesendet werden. Die Befehle sind nicht nur einfach Positionsbefehle beinhalten jedoch auch das Beschleunigen / Deaktivieren von Servos im Laufe der Zeit, und dies ist der Grund, warum der Mikrocontroller eine erhebliche Anzahl von Taktzyklen verbringt, bevor neue Bytes verarbeitet werden.)

Kaliatech
quelle

Antworten:

2

Ich denke, die Form Ihrer Frage ist falsch. Das Problem ist nicht, dass Sie nicht richtig berechnet haben, wie viele Daten pro Sekunde auf den Mikrocontroller geworfen werden können. Es ist so, dass Sie für den Mikrocontroller keine Möglichkeit haben, seine Bereitschaft anzuzeigen, den nächsten Befehl zu empfangen.

Anders ausgedrückt: Wenn Sie versuchen, dieses Problem zu lösen, indem Sie genau berechnen, wie schnell Daten gesendet werden sollen, werden Sie zwangsläufig eines der folgenden Dinge tun:

  1. Senden Sie weniger Daten, als der Mikrocontroller verarbeiten kann
  2. mehr Daten senden als der Mikrocontroller (und im schlimmsten Fall wird es nur sein handhaben kann sehr leicht mehr , so dass die Puffer etwas völlig unabhängig eine Stunde , um Überlauf und führt Sie zu debuggen nehmen)

Die Lösung besteht darin, dass Ihr Mikrocontroller eine Rückkopplungsflusssteuerung bietet. Im einfachsten Beispiel senden Sie einfach ein einzelnes Zeichen zurück, das die Befehlsvervollständigung darstellt (@ Rocketmagnet's SpaceWire-Vorschlag wäre der robusteste, aber auch schwerfälligste).

Höchstwahrscheinlich können Sie es sich leisten, ein paar Befehle im Puffer zu haben. Die Berechnung, die Sie durchführen sollten, besteht darin, Ihre Puffergröße durch die Größe des größten Befehls zu teilen, den Sie senden, und dann aus Sicherheitsgründen 1 (oder mehr) zu subtrahieren. Dies zeigt Ihnen den größten zulässigen Unterschied zwischen gesendeten und empfangenen Befehlen und ermöglicht es Ihnen, mit maximaler Geschwindigkeit zu arbeiten, indem Sie den Puffer gefüllt halten.

Beispiel

Angenommen, Ihr längster möglicher Befehl ist 7 Byte ("fw 4.32") oder ähnliches, und Sie haben einen 64-Byte-Puffer. Das bedeutet, dass Sie 9 Befehle in den Puffer einfügen können ( ), aber aus Sicherheitsgründen subtrahieren Sie einen und lassen nur 8 Befehle zu. Hier ist ein Python-Psudeocode:7×9=63<64

MAX_UNACKED = 8  # constant, how many un-acked commands to send
unacked = 0      # number of un-acked commands that have been sent
while (true):
    # can send up to MAX_UNACKED commands as we like
    if MAX_UNACKED > unacked:
        send_command()
        unacked = unacked + 1

    # assume that the ack is a single character, we don't care which
    acks = receive_response()      # receive a string of characters
    unacked = unacked - len(acks)  # number of characters = number of acks

Beachten Sie, dass dieser Pseudocode ein Beispiel für Spin-Blocking ist und dass es bessere Möglichkeiten gibt, auf Eingaben zu warten ... aber welche davon speziell davon abhängt, wie Ihr PC-Code funktioniert.

Ian
quelle
Danke, das ist eine großartige Antwort. Die Verzögerung beim Warten auf eine Nachricht vom Typ "ack" ist jedoch ziemlich bedeutend. Ich hatte gehofft, es zu vermeiden, aber vielleicht ist es für 100% Zuverlässigkeit einfach unvermeidlich. Ich bin damit einverstanden, dass das von Rocketmagnet vorgeschlagene Spacewire-Protokoll sehr robust aussieht, aber vielleicht zu viel für das, was ich brauche.
Kaliatech
Denken Sie daran, dass Sie nicht warten müssen, bis jeder Befehl bestätigt wird. Sie können so viele Befehle senden, wie Sie über Pufferplatz verfügen, und dann eine Nachricht pro empfangener Bestätigung senden. Dies sollte Verzögerungen vermeiden (indem die Befehlswarteschlange des Mikrocontrollers voll bleibt), aber verhindern, dass Sie den Puffer überlaufen - das ursprüngliche Problem.
Ian
4

Ich würde zwei mögliche Ansätze vorschlagen.

  1. Verwenden Sie einen "Herzschlag", um ein bekanntes Statuspaket mit einer festen Frequenz zu übertragen, die in Ihr "Geschwindigkeitsbudget" passt. In diesem Fall senden Sie niemals Ad-hoc-Nachrichten direkt vom PC oder der MCU. Sie können lediglich das Statuspaket aktualisieren, das zum geplanten Zeitpunkt gesendet wird.

  2. Setzen Sie Ihrem Übertragungsbudget eine harte Grenze und setzen Sie es durch. Sie können beispielsweise nur alle 100 ms ein Paket senden. Wenn ein zweites Paket vor Ablauf von 100 ms gesendet wird, wird seine Übertragung verzögert, bis das Zeitlimit von 100 ms (z. B. Quantum) abgelaufen ist. Dazu müssen Sie einer Warteschlange Nachrichten hinzufügen und dann mit einer festen Rate aus der Warteschlange senden. Ähnlich in der Herangehensweise an den Herzschlag in # 1, aber etwas effizienter, da Daten nicht gesendet werden, wenn sie sich nicht geändert haben. Der Nachteil dieses Entwurfs besteht darin, dass Sie bei Übersendungen immer höhere Latenzen in Ihre Kommunikation einbauen und bei einer variablen Burst-Kommunikation die Kommunikationslatenz stark variieren kann.

Ich neige dazu, mit # 1 zu arbeiten, um Daten von der MCU zu senden, da der Algorithmus einfach und klein ist.

Jay Beavers
quelle
3

Es gibt keine richtige Antwort darauf, und ich bin sicher, dass Sie bereits alles wissen (oder erraten können), was Sie zur Lösung dieses Problems benötigen. jedoch

Das erste, was zu sagen ist, ist, dass nachgeschaltete Geräte in der Lage sein müssen, sowohl langfristig als auch kurzfristig mit dem Datenfluss umzugehen. Kurzfristig verwenden Geräte Pufferung, um den Fluss zu bewältigen. Langfristig benötigt man Rechenleistung, um auf die Daten einzuwirken.

Ein Problem, das Sie haben, ist, dass Sie nicht alle Schritte in Ihrer Kette kontrollieren. Wenn einer von ihnen Verzögerungen verursacht, können Sie möglicherweise nicht viel dagegen tun, außer ihn zu ersetzen. Ich hätte jedoch gedacht, dass der serielle Bluetooth-Treiber einen schönen großen Puffer haben und mit dem Transceiver und BlueSMIRF gut spielen würde, daher ist es wahrscheinlich sicher, sie zu ignorieren. Ich denke, das eigentliche Problem liegt zwischen dem Propeller und dem PC.

Was in Ihrem Fall zu passieren scheint, ist, dass die Interaktion zwischen dem Datenproduzenten und dem Konsumenten unvorhersehbare Warteschlangenlängen erzeugt, und Sie möchten darauf eine richtige Theorie anwenden.

Interessanterweise wurden Studien zu genau diesem Problem in Bezug auf Warteschlangen in Restaurants, EG- Fallstudien für das Warteschlangenmodell für Restaurants und das Gleichgewicht in Warteschlangen unter unbekannten Servicezeiten und Servicewerten durchgeführt . Es gibt jedoch leichter verständliche Online-Ressourcen wie die Warteschlangentheorie Warteschlangentheorie für Dummies . Aber das, was Sie wirklich wollen, ist wahrscheinlich, wie Sie Nachrichtenwarteschlangen dimensionieren .

Sie haben einige Möglichkeiten:

  • Stellen Sie einfach sicher, dass der PC Daten mit einer ordnungsgemäßen Rate sendet. Entscheiden Sie genau, welche Rate Sie wirklich benötigen, und gehen Sie nicht darüber hinaus.
  • Verwenden Sie ein Echtzeitbetriebssystem auf der MCU, um sicherzustellen, dass die Bytes rechtzeitig verarbeitet werden.
  • Implementieren Sie die Flusskontrolle.

Aber hier ist meine bevorzugte Lösung. Spacewire ! Oder verwenden Sie zumindest das Flusskontrollsystem.

Im Wesentlichen sendet das Downstream-Gerät Bytes an das Upstream-Gerät und gibt die Anzahl der leeren Stellen in seinem FIFO an. Auf diese Weise sendet der Datenproduzent nur das, was der Verbraucher bewältigen kann.

Wenn Sie interessiert sind, können Sie den vollständigen Spacewire-Standard lesen: IEEE 1355 .

Raketenmagnet
quelle
2

Auf den ersten Blick:

115200 == 115,2 Bits pro Millisekunde == ~ 12,8 Bytes pro Millisekunde (unter der Annahme von 1 Stoppbit)

Ist das eine gültige Methode, um das Timing für serielle Übertragungen zu berechnen?

Sehr einfach ist dies in Ordnung - aber vergessen Sie auch keine Start- und Paritätsbits und keinen Protokoll-Overhead entlang der Bluetooth-Verbindungen

Auch angesichts meines spezifischen Setups:

PC-Programm <--> Bluetooth-Treiber für serielle Profile <--> Bluetooth-Transceiver <- * -> BlueSMIRF-Funkmodem <--> Parallax-Propellerprogramm

Ich denke, es ist vernünftig anzunehmen, dass der PC bis zum Modem in der Lage ist, den Datenverkehr mit 115200 Bit / s zu verarbeiten, sodass Sie diese aus der Gleichung streichen können.

Wenn Sie jedoch mehrere Hops wie diesen haben, wird die Verwendung von Flusssteuersignalen verhindert, ohne dass Antwortnachrichten eingeführt werden ... was die Antwortzeit verlangsamen würde.

Nehmen wir nun den schlimmsten Fall ohne Protokoll-Overhead. 115200 Bit / s bedeuten, dass Ihre Parallaxe alle 69us ein Byte empfängt. Durch Hinzufügen von Start-, Stopp- oder Paritätsbits wird diese Rate ein wenig verlangsamt, aber wenn Sie davon ausgehen, dass der schlimmste Fall Ihnen einen gewissen Spielraum gibt .

Dies bedeutet, dass Ihr Controller alle 69us ein Byte empfangen und seine normalen Aufgaben (Berechnungen usw.) ausführen muss.

In Wirklichkeit senden Sie jedoch eine Nachrichtenzeichenfolge mit (n) Bytes, die gepuffert und als Zeichenfolge verarbeitet werden müssen - während Sie weiterhin die normalen Aufgaben ausführen. Das Berechnen des Puffers ist eine Kunst für sich, aber ich würde normalerweise an (mindestens) der doppelten Größe der längsten Zeichenfolge arbeiten (wenn Sie über RAM-Speicher verfügen). Alles andere kann Nachrichten verlieren, wenn eine nicht verarbeitet wird, bevor der Empfang der nächsten beginnt.

Wenn Sie nur auf die Länge der längsten Nachricht beschränkt sind, müssen Sie 100% sicher sein, dass Sie diese Nachricht zwischen dem Empfang des letzten Bytes und dem Empfang des ersten Bytes der nächsten Nachricht verarbeiten können. (Offensichtlich weniger ein Problem mit kleineren Nachrichten).

Andrew
quelle
0

Anstatt die Geschwindigkeitsanforderungen zu berechnen (die von Zeit zu Zeit / von Comp zu Comp variieren können und wann immer Sie den Code ändern), können Sie noch einige andere Dinge tun:

  • Lassen Sie Ihre μC- Anforderung eingeben, indem Sie Interrupts an das Master-Gerät senden (in diesem Fall handelt es sich um einen PC - Sie müssen möglicherweise keine Interrupts verwenden, halten Sie einfach einen Port reserviert). Sie können entweder die Ausgänge vom Master-Gerät puffern lassen (einfacher mit einem PC - effektiv unbegrenzte Pufferung), oder Sie können sie einfach verwerfen, sofern dies nicht erforderlich ist.

  • In Ihrem Szenario ist es besser, die Pufferung vollständig zu beseitigen. Akzeptieren Sie die Eingänge zum μC über den Interrupt-Pin. Haben Sie eine Flag-Variable, die im Grunde "Need Input" auf dem μC sagt. Wenn dieses Flag gesetzt ist, akzeptiert der μC die Eingabe. Andernfalls wird es einfach verworfen. Auf diese Weise wird die gesamte Eingabe schnell verbraucht (Puffer bleiben frei), aber nicht alle werden verwendet. Passen Sie das Timing aus Effizienzgründen an die ungefähre Baudzahl des gesamten Setups an. Anstatt es zu verwerfen, möchten Sie vielleicht Ihr eigenes Puffer-Ding codieren - haben Sie ein kleines Array, in dem einige Eingaben gespeichert sind. Jetzt haben Sie alle vorgelagerten Puffer entfernt und haben einen einzigen "künstlichen" Puffer auf dem μC - über den Sie die vollständige Kontrolle haben.

  • Es ist möglicherweise besser, alle Berechnungen auf dem PC zu speichern und den μC als einfachen Slave-Controller zu verwenden. Geben Sie ihm einfache Bewegungs- / Positionsbefehle, die direkt an die Motoren weitergeleitet werden können. Wenn Sie über genügend Interrupt-Pins / Interrupt-MUXing verfügen, können Sie diese Befehle für eine schnellere Reaktion über Interrupts senden.

Manishearth
quelle