Ich benötige einen zusätzlichen Ausgang der vor dem Senden gesetzt wird, und wollte dazu den RTS Pin benutzen, den ja die meisten USB zu TTL Adapter haben. Jetzt habe ich festgestellt, dass das RTS Signal immer mindestens 1ms auf dem selben Pegel bleibt. Ich kann also maximal 500Hz erzeugen.
Das habe ich mit einem CH340 Klon und einem FTDI FT232BL probiert, und beide machen diese 1ms.
Deshalb ist meine Vermutung, dass das irgendwo im Treiber eingestellt ist. Ich benutzte Ubuntu 22.04.2 LTS. Das Programm, das ich zum testen verwende, ist in Python (3.10.6) geschrieben und benutzt pyserial in der Version 3.5:
1
import serial
2
3
port = serial.Serial("/dev/ttyUSB1", 40000, stopbits=serial.STOPBITS_ONE, timeout=0)
4
5
print(serial.VERSION)
6
7
while True:
8
port.rts = True
9
port.rts = False
Kann man dieses Timing irgendwo einstellen?
Dass Python so langsam ist, glaube ich nicht, das Programm läuft auf einem Ryzen 5 5625U mit bis zu 4.3GHz.
Linux und erst recht USB sind von Hause aus nicht echtzeitfähig.
Du hast da einfach gewisse Latenzen, bevor so ein einzelnes USB-Paket überhaupt abgesendet wird. 1ms ist schon eher bestcase und du musst damit rechnen das da auch mal sporadisch bis zu 100ms Pause oder so drin sind. Für irgendwas mit einem definierten Timing ist USB einfach nicht vorgesehen und daher ungeeignet.
Wenn du was genaues brauchst, nehme einen µC und verwenden den in dem du dann z.B. per USB sagst "mach 100 Impulse von 0,5ms im Abstand von 1 ms". Dann kannst du dich da auch drauf verlassen (aber nicht darauf wann die 100 Impulse wirklich anfangen).
Jetzt habe ich festgestellt, dass das RTS Signal immer
mindestens 1ms auf dem selben Pegel bleibt.
[...]
Kann man dieses Timing irgendwo einstellen?
Einstellen kannst du das garnicht. Du könntest höchstens einen anderen USB-Serial-Adapter verwenden, nämlich einen, der mittels HighSpeed kommunizieren kann (selten und teuer). Damit hast du dann eine Latenz von 250µs, also ein Viertel von dem der üblichen FullSpeed-Adapter.
So weit die Theorie. In der Praxis ist es aber oft so, dass minderweitertige Treiber, Firmwares und teildefekte oder ungeeignete Hardware dafür sorgen, dass sich so ein Buszyklus (der eigentlich dafür da ist, zumindest die genannten Latenzen zu garantieren) auch mal verschluckt. Am wahrscheinlichsten bei der Anmeldung neuer Geräte am Bus.
Das sollte zwar nicht so sein, passiert aber in der Praxis doch recht häufig. Man ist also gut beraten, nicht darauf zu setzen, dass die Zykluszeit (und damit die Latenzen) wirklich konstant und unerschütterlich stabil ist.
Zusammenfassend: dein Ansatz ist eine ganz schlechte Idee. Dafür ist USB schlicht nicht gedacht. Das ist auf hohen Durchsatz optimiert, nicht auf geringe Latenzen.
Danke, ich dachte mir schon, dass es am USB liegt.
Eigentlich wollte ich ja einen Break senden, aber das dauert ja noch länger. Unter Linux wohl mindestens 0,25s. Das Protokoll das ich habe startet immer mit einem Break und hat 8N1. Die Pakete sind meist nur 4 Bytes lang. Das sind dann gerade Mal 4 Pakete pro Sekunde.
Ich habe auch schon probiert die Baudrate zu ändern, aber das ist auch sehr langsam.
Ein Portpin war noch frei, also dachte ich, vielleicht ist es damit besser. Ist es auch, aber ich dachte es wäre noch schneller.
Ich dachte auch daran ein Paritybit für den Break anzuhängen, aber da dazu auch die Konfiguration der Schnittstelle geändert werden muss, ist es wohl dasselbe wie beim Ändern der Baudrate.
Eine Möglichkeit wäre jetzt noch den Break in einem zusätzlichen Datenbit zu senden, also 9N1 statt 8N1. Gibt es USB-Adapter die so etwas können? Bis jetzt habe ich nichts gefunden.
Vermutlich bleibe ich dann bei der Lösung mit dem RTS. Ist bis jetzt das schnellste und benötigt keine Sonderlösung.
Eigentlich wollte ich ja einen Break senden, aber das dauert ja noch
länger. Unter Linux wohl mindestens 0,25s. Das Protokoll das ich habe
startet immer mit einem Break und hat 8N1.
Hört sich irgendwie stark nach DMX an...
Und natürlich ist ein Break nicht zwingend mindestens 250ms lang. Wie lang der ist, hängt von der Baudrate ab, auf die die Schnittstelle konfiguriert ist.
Baudrate und Status Leitungen werden über USB via Control Requests eingestellt, das wird bei Fullspeed nicht wesentlich schneller, da für Controll Requests nur 10% der gesammten Bandbreite vorgesehen ist. Das passt schon ganz gut zu 1 Request pro ms.
Und natürlich ist ein Break nicht zwingend mindestens 250ms lang. Wie
lang der ist, hängt von der Baudrate ab, auf die die Schnittstelle
konfiguriert ist.
Ich weiß manche Controller machen einfach ein paar Bits länger.
Aber in meinem Fall macht pyserial das so. Hängt wohl mit termios
zusammen. Soweit ich gelesen habe ruft pyserial tcsendbreak() aus termios auf. Und das macht irgendwas zwischen 0.25 und 0.5s. Wenn man eine Zeit übergibt, dann steht in der Beschreibung: If duration is not zero, it sends zero-valued bits for some implementation-defined length of time.
Also nichts genaues weiß man nicht.
Leider bezieht sich alles was ich im Netz gefunden habe auf tcsendbreak().
Und ich möchte dafür auch nicht selbst irgendwas machen, es sollte schon mit Standardhardware funktionieren.
Bei mir war der Break jedenfalls immer ziemlich lang. Aber auch das muss ja über USB und wird dann wohl nicht schneller sein als der RTS.
P.S. Ist kein DMX, ist was in Anlehnung an LIN selbst gemachtes.
Da kann man noch was anpassen. Ich hatte auch Mal die Idee ein Byte auf
zwei 6Bit Werte aufzuteilen, dann muss aber die Baudrate hoch und mehr als die eingestellten 40kBaud sollen es nicht werden, sonst wird wohl der Empfänger (AVR) überlastet.
Ich benötige einen zusätzlichen Ausgang der vor dem Senden gesetzt wird,
und wollte dazu den RTS Pin benutzen, den ja die meisten USB zu TTL
Adapter haben.
Und was genau soll dieser Ausgang machen? Etwa einen RS485-Treiber ansteuern? Dafür haben die meisten USB-Seriell-Bridges dedizierte Steuerausgänge, die sie vollautomatisch in Hardware ansteuern, sobald sie senden. Per Software an irgendwelchen Leitungen rumwackeln muss man da gar nicht.
Und wenn Du einen längeren Impuls brauchst, häng' da halt ein Monoflop dran.
Der Impuls soll den Anfang eines Datenpakets markieren.
Das mache ich normalerweise mit einem Break-Signal.
Ich habe einen Bus, wo das so passiert, und diesen will ich jetzt
mit dem PC verbinden. Dazu würde ich gerne dasselbe Protokoll benutzen, und nicht wieder irgendwas neues machen. Da das mit dem Break vom PC aus noch viiieeel langsamer ist, suche ich nach einer anderen Möglichkeit dafür.
Die Ziele sind, dass es mit Standard USB-UART-Wandlern funktioniert, dass es hinreichend schnell ist (also die 40kBaud gut ausnutzt), und dass die Änderungen am Protokoll möglichst klein sind.
Da noch ein Portpin frei ist, schien mir das mit dem RTS-Ausgang eine gute Lösung zu sein. Den haben die meisten Adapter.
Da, wie man auf dem angehängten Screenshot sieht, die Daten schon kurz nach dem Setzen des RTS gesendet werden, sich Daten und Steuersignale wohl überlappen, kann man damit immerhin 500 Datenpakete pro Sekunde schicken.
Das ist schon Mal deutlich mehr als bei Benutzung des Breaks.
Die eigentliche Frage war jetzt, ob man das noch ein bisschen beschleunigen kann. Sieht aber wohl nicht so aus.
Der Impuls soll den Anfang eines Datenpakets markieren.
Und der "Impuls" soll während des Datenpaketes dann irgendwann aufhören?
Wie lange vor dem Beginn des Datenpaketes soll denn Dein "Impuls" beginnen?
Mit der von mir vorgeschlagenen Kombination aus der RS485-Treibersteuerung und gegebenenfalls einem Monoflop (retriggerbar) bekommst Du ein Signal, das während des gesamten Datenpakets aktiv ist, und das sehr knapp vor dem Beginn des Datenpakets aktiv wird.
Das Monoflop ist nur dazu da, um etwaige kurze Pausen zwischen den einzelnen gesendeten Bytes zu überbrücken.
Du kannst ja, falls das Oszillogramm da oben mit einem FT232 o.ä. erstellt wurde, zusätzlich noch das Signal TXDEN einblenden. Das ist beim FT232RL üblicherweise auf CBUS2.
Andere USB-UARTs haben auch derartige Signale.
Was ist das für ein Protokoll? 40 kBaud ist eine sehr ungewöhnliche Baudrate; ist das was selbstgebratenes?
Und der "Impuls" soll während des Datenpaketes dann irgendwann aufhören?
Wie lange vor dem Beginn des Datenpaketes soll denn Dein "Impuls"
beginnen?
Es reicht eine Flanke um zu erkennen, dass jetzt ein neues Datenpaket kommt. Die soll natürlich möglichst lang genug vor den Daten kommen, damit der Portinterrupt immer vor dem seriellen Receiverinterrupt kommt. Zurückgesetzt kann er dann irgendwann werden. Laut Messung scheint das zu passen, ich habe es aber noch nicht probiert.
Mit der von mir vorgeschlagenen Kombination aus der
RS485-Treibersteuerung und gegebenenfalls einem Monoflop (retriggerbar)
bekommst Du ein Signal, das während des gesamten Datenpakets aktiv ist,
und das sehr knapp vor dem Beginn des Datenpakets aktiv wird.
Das Monoflop ist nur dazu da, um etwaige kurze Pausen zwischen den
einzelnen gesendeten Bytes zu überbrücken.
Du kannst ja, falls das Oszillogramm da oben mit einem FT232 o.ä.
erstellt wurde, zusätzlich noch das Signal TXDEN einblenden. Das ist
beim FT232RL üblicherweise auf CBUS2.
Andere USB-UARTs haben auch derartige Signale.
Ja, das wäre auch eine Lösung, nur von den beiden Adaptern die ich hier habe hat keiner dieses Signal herausgeführt, RTS aber schon. Falls ich einen eigenen USB-Seriell-Wandler machen muss, wäre das wohl die beste Lösung (wenn es nicht zu knapp vor den Daten kommt, siehe oben). Aber wie gesagt, ich wollte was von der Stange nehmen.
Was ist das für ein Protokoll? 40 kBaud ist eine sehr ungewöhnliche
Baudrate; ist das was selbstgebratenes?
Es reicht eine Flanke um zu erkennen, dass jetzt ein neues Datenpaket
kommt. Die soll natürlich möglichst lang genug vor den Daten kommen,
damit der Portinterrupt immer vor dem seriellen Receiverinterrupt kommt.
Wenn Du das von mir vorgeschlagene Steuersignal verwendest, hast Du bei Deinen 40 kBaud etwa 250 µsec Zeit, nämlich die Dauer für das Senden eines Bytes. Das belegt auf der Leitung zehn Bitzeiten (bei 8n1, nämlich ein Start-, ein Stop- und acht Datenbits).
Deine empfangende UART löst ihren Rx-Interrupt natürlich erst dann aus, wenn sie das Zeichen auch empfangen hat. Das Steuersignal aber wird vor dem Beginn der Übertragung des Startbits gesetzt.
Warum muss ein Datenpaket bei Deinem Protokoll angekündigt werden? Was ist der tiefere Sinn davon?
Wenn ich ein spezielles Timing, zB fuer kurze Reaktionszeit bei hohem Durchsatz will, lagere ich diese Funktionalitaet auf einen AVR aus, welcher dann per USB am PC angeschlossen ist. Der USB bringt die hohen Blocklaengen bei hoher Geschwindigkeit, der AVR macht das Timing. Muss halt etwas buffern.
zB einen schnellen ADC an einem schnellen Bus per kurzen Packeten angeschlossen, auf einen PC bringen. Die kurzen Packete werden einzeln quitiert.
Warum muss ein Datenpaket bei Deinem Protokoll angekündigt werden? Was
ist der tiefere Sinn davon?
Zum synchronisieren, z.B. wenn der Empfänger abgeschaltet war.
Der Busmaster fragt zyklisch Adressen ab, aber nicht alle Empfänger
sind aktiv, sie können zwischendurch schlafen gehen, wenn nichts zu tun ist. Nach dem Aufwachen müssen sie erkennen können, wann ein Telegramm beginnt. Für den Anschluss zum PC soll dasselbe Protokoll verwendet werden (evtl. mit kleinen Änderungen). In diesem Fall ist der PC der Busmaster und der Empfänger hat eine vom PC unabhängige Versorgung, so dass auch hier eine Synchronisation notwendig sein kann.
Wenn ich ein spezielles Timing, zB fuer kurze Reaktionszeit bei hohem
Durchsatz will, lagere ich diese Funktionalitaet auf einen AVR aus,
Wie ich schon geschrieben habe würde ich gerne "normale" USB zu UART ICs verwenden und mir nicht noch eine Baustelle aufmachen. Sollte es gar nicht funktionieren kann ich das ja immer noch.
Zum synchronisieren, z.B. wenn der Empfänger abgeschaltet war.
Das macht man üblicherweise mit einem sinnvoll aufgebauten Protokoll - erst wenn ein Paket mit funktionierender Prüfsumme empfangen wird, wird es akzeptiert. Pakete werden durch Pausen separiert.
Sieh Dir Modbus/RTU an, da wird so ein Verfahren verwendet.
Was passiert bei Deinem "Protokoll", wenn ein Empfänger während des Sendend eingeschaltet wird? Er erkennt (fälschlich) das Paketbeginnsignal und decodiert falsche Daten.
Wie auch immer:
Das Steuersignal der USB-Seriell-Bridge ist für Deinen Zweck ausreichend lange vor Beginn des Pakets aktiv, allerdings wird es erst nach vollständigem Senden des Paketes wieder inaktiv. Wenn Deine Empfänger nur auf eine Flanke des Signales reagieren, ist das aber kein Problem.
Das macht man üblicherweise mit einem sinnvoll aufgebauten Protokoll -
erst wenn ein Paket mit funktionierender Prüfsumme empfangen wird, wird
es akzeptiert. Pakete werden durch Pausen separiert.
Bei dem Protokoll das ich habe wird der Paketanfang durch einen Break
signalisiert. Da ich das Protokoll auch für den PC-Anschluss verwenden will muss ich das also irgendwie anders kenntlich machen. Zusätzliche Pausen möchte ich eigentlich aus Durchsatzgründen nicht haben.
Was passiert bei Deinem "Protokoll", wenn ein Empfänger während des
Sendend eingeschaltet wird? Er erkennt (fälschlich) das
Paketbeginnsignal und decodiert falsche Daten.
Dann wartet der Empfänger bis er einen Break erkennt. Erst danach werden Daten akzepiert. Prüfsumme gibt es natürlich zuätzlich.
Wie ich schon Mal geschrieben habe, ist das Protokoll an das LIN-Bus-Protokoll angelehnt. Da wird es so gemacht. Da das im Fahrzeug schon lange verwendet wird, sollte es wohl funktionieren. Die Spezifikation dafür kann man sich bei https://www.lin-cia.org/standards/ runterladen.
Wie ich schon geschrieben habe, haben die Adapter die ich hier habe das RS485 Signal (TXDEN) nicht.
Ich habe einen mit dem FT232BL, der Chip hat das Signal, aber es ist nicht herausgeführt.
Die anderen sind irgendwelche CH340 Kopien, da habe ich kein Datenblatt davon, sie sind zwar im selben Gehäuse wie der CH340, haben aber eine komplett andere Pinbelegung.
Außerdem habe ich schon genug Baustellen, da muss ich nicht noch einen USB zu seriell Adapter selber machen. Nur wenn es gar nicht anders geht. Aber das mit dem RTS sieht ja ganz gut aus. Ich bin gerade am Testen.