Hallo,
folgendes Problem treib mich nun schon eine Weile um:
Ich habe Hardware entwickelt, die mit einem PC über die serielle
Schnittstelle kommuniziert, via FT232R. Vom PC zum Gerät ist das kein
Problem, weil nur wenige, kurze Kommandos gesendet werden. In die
Gegenrichtung werden jedoch ordentlich Daten übertragen. Nun habe ich
festgestellt, dass dabei Daten verloren gehen. Ich habe auf mehreren
Rechnern Versuche gemacht. Bei altueller Hardware ist das Problem
weniger gravierend, bzw. tritt nicht auf. Bei älteren Rechnern,
weilweise mit XP jedoch scheint die Übertragung nicht hinterher zu
kommen.
In der Hardware schaue ich beim Senden auf die RTS-Leitung des FT232R.
Bei High-Pegel unterbreche ich die Datenüertragung, so wie das bei FTDI
auf der Webseite steht.
Nun die Frage: Macht der FT232R das mit der RTS-Leitung selbt? Bei FTDI
steht, das passiert in Hardware. Oder muss ich im Seriell Port das
Handshake aktivieren? Mir fehlt etwas das Verständnis, wo der
Flaschenhals ist, auf der USB-Leitung oder dahinter im Rechner, der die
Daten nicht schnell genug aus dem Puffer holt.
Muss ich im Programm zwingend
1
SerialPort port = new SerialPort("NameOfPort", 256000, Parity.None, 8, StopBits.One);
2
port.Handshake = Handshake.RequestToSend;
setzen, oder nicht? Sorry, dass ich hier Romane schreibe, bin gerade
ewas verzweifelt.
Grüße, Alex
sicher das es nicht nur ein Software Problem ist? Auch XP konnte schon
100Mbit über netzwerkkarten ausnutzen ohne Probleme.
Ist zwar nicht wirklich vergleichbar aber deine 256000 bits/s sind auch
nur 32kb/s - also nichts.
Teste erst mal mit einer anderen Software, zur not einfach die CMD.
cat COM1 > Datei.txt
und schicke vom µC einfach das Alphabet rüber (unendlich oft).
Dann schau nach ob wirklich Daten verloren gehen.
Es gehen definitiv Daten verloren. Ich vergleiche das, was an Detenmenge
gesendet werden muss, mit dem, was ankommt und da gibt es Differenzen.
Wie gesagt, das Problem tritt besonders bei alten Rechnern auf.
Grüße, Alex
Alexander H. schrieb:> Es gehen definitiv Daten verloren. Ich vergleich das, was an Detenmenge> gesendet werden muss, mit dem, was ankommt und da gibt es Differenzen.> Wie gesagt, das Problem tritt besonders bei alten Rechnern auf.
ja und? Wenn du Fehler in der Software hast, dann kann sich das auf
anderen Rechner anders auswirken.
Handshake mehr dafür gedacht, das wenn im "modem" der Puffer voll wird,
das es dem Rechner mitteilen kann das er aufhören soll zu senden.
Wenn aber schon Daten zum Rechner verloren gehen ohne das sie in der
Software ankommen, wie willst du da ein Stopp signalisieren?
Hast du einen Vergleich mit einer Anderen Software gemacht ob Daten
verloren gehen?
Am besten einen LoopBack test machen.
Alexander H. schrieb:> Mir fehlt etwas das Verständnis, wo der> Flaschenhals ist, auf der USB-Leitung oder dahinter im Rechner, der die> Daten nicht schnell genug aus dem Puffer holt
Der Flaschenhals ist vermutilch der FIFO im FT232 Chip, denn dessen
Daten werden unregelmäßig abgeholt. Wenn der voll ist, wird RTS gesetzt
und der µC muss mit dem Senden warten.
Wenn das PC Programm etwas naiv geschrieben wurde, kann aber auch dort
der Flaschenhals sein. Meistens sieht das so aus:
...
Lese_Puffer();
Verarbeite_Puffer();
Lese_Puffer();
...
Wenn jetzt das Verarbeiten länger dauert, können Daten verloren gehen.
Jim Meba schrieb:> Der Flaschenhals ist vermutilch der FIFO im FT232 Chip, denn dessen> Daten werden unregelmäßig abgeholt.
unwahrscheinlich. Der Chip kann bis 1Mbit/s - dann wird der Puffer auch
dafür ausgelegt sein, so mal hier nur 1/4 davon gebraucht wird.
Außer hat eine USB-Festplatte am gleiche Hostcontroller und überträgt
dort fleißig Daten.
Vielleicht noch etwas zum Programm. Ich lese erst alle Daten binär,
schreibe sie in eine
1
List<byte>
und verarbeite diese nach der Übertragung.
Meine Frage zielte auf die Bedienung des Hardware Handshakes hin. Kann
der PC denn FT232R veranlassen, die RTS Leitung zu setzten? Dass es bei
schnelleren Rechnern geht, KÖNNTE ja darauf hindeuten, dass der Rechner
nicht hinterher kommt.
Es kommen übrigens schon Daten im unteren Megabyte-Bereich zusammen pro
Übertragung.
Peter II schrieb:> dann wird der Puffer auch> dafür ausgelegt sein
Datenblatt gelesen? 384 Byte Receive Buffer / 128 Byte Transmit Buffer
Das ist nicht die Welt...
Alexander H. schrieb:> Vielleicht noch etwas zum Programm. Ich lese erst alle Daten binär,> schreibe sie in eineList<byte>
dann kann schon das Problem sein.
Wenn du ständig Daten an die Liste anfügst, dann muss ständig die Liste
vergrößert werden. Das kostet viel viel zeit.
Der UART-Puffer ist teilweise nur 16byte groß, da muss man schnell die
Daten abholen.
Oder kennst du vorher schon die Größe und empfängst es auf einmal?
Zeige mal etwas vom quellcode.
Georg G. schrieb:> Datenblatt gelesen? 384 Byte Receive Buffer / 128 Byte Transmit Buffer>> Das ist nicht die Welt...
die Welt nicht, aber usb 1.1 hat schon 12 Mbit/s. Da ist genug reserve
das man dem Buffer fast nicht braucht.
Peter II schrieb:> Buffer fast nicht braucht
Windows legt per Definition ab und an Pausen ein für interne
Verwaltungsarbeiten. Da kann USB so schnell sein, wie es will. Wie gross
ist der Buffer auf der PC Seite im emulierten COM Treiber? Vermutlich
die üblichen 16 Bytes (die man aber während der Initialisierung
vergrößern kann). Irgendwo muss dann für die Pause der Datenstrom
gepuffert werden oder man muss Handshake machen.
if (readString.Contains(CustomClasses.ATresponse.TransmissionStart))
5
{
6
this.transmissionState.IsTransmitting = true;
7
this.bw_read.ReportProgress(0, readString);
8
}
9
}
10
else
11
{
12
readByte = this.port.ReadByte();
13
bufferList.Add(readByte);
14
this.transmissionState.IncreaseProgress(1);
15
}
Relevant ist der else-Zweig, dort werden die Binärdaten gelesen.
Könnte es was bringen, der List eine gewisse Größe zuzuweisen, damit
diese nicht immer vergrößert, sondern nur beschrieben werden muss?
Empfangspuffer ist 4096
Nochmal die Frage zu RTS. Macht das nur der FT232R, wenn sein Puffer
voll ist, oder geht das auch vom PC aus?
Bin gleich zurück. Kinder sind im Hof am baden. Danke erst mal für das
tolle Feedback. Ich meld mich dann nochmal.
Ja, im Allgemeinen bringt es oft mehr Performance, wenn man vorallokiert
und nicht jedes Mal um eine Einheit vergrößert. Das ist im Allgemeinen
erheblicher Overhead, der da vermieden wird.
Ich schreibe im Allgemeinen, weil es stark implementierungs-abhängig
ist.
Alexander H. schrieb:> Nochmal die Frage zu RTS. Macht das nur der FT232R, wenn sein Puffer> voll ist, oder geht das auch vom PC aus?
Ich bin mir gar nicht sicher, ob der FT232R von sich aus überhaupt etwas
mit der RTS-Leitung macht. Die wird transparent vom PC gesteuert. Starte
auf dem PC einfach mal ein Terminalprogramm, mit dem du RTS steuern
kannst (z.B. HTerm), dann siehst du's direkt.
Zu Handshake und RTS sagt FTDI folgendes:
http://www.ftdichip.com/Support/FAQs.htm#HwGen3
Das mit dem Progressindikator werde ich mal berücksichtigen. Ist
eigentlich logisch und nicht so clever von mir gewesen.
Alexander H. schrieb:> Könnte es was bringen, der List eine gewisse Größe zuzuweisen, damit> diese nicht immer vergrößert, sondern nur beschrieben werden muss?
ja, man verwendet einfach ein Array und keine list.
Byte[] buffer = new Byte[MAX_SIZE]();
hier schon das nächste Problem:
1
this.port.ReadByte();
du liest jedes Byte einzeln!!!! Kein Wunder das es viel zu langsam ist.
Der Overhead ist damit viel zu gross.
Verwende lieber diese Funktion:
http://msdn.microsoft.com/de-de/library/ms143549(v=vs.110).aspx
Wie wird denn der coden den du gepostet hast aufgerufen? Vermutlich
sogar im OnRecive Event? Wenn ja hast du dort den nächsten fehler
gemacht. Dort musst du alle Daten lesen die da sind und nicht nur ein
Byte. Das würde erklären warum du Daten verlierst.
Hallo Peter,
ich werde das ausprobieren. Ich lese in der Tat die Bytes im
Received-Event. Ich lese kontinuierlich, bis keine Daten mehr zu lesen
sind und ein Time-out passiert.
Eine Frage noch zu Deinem Vorschlag. Wass passiert, wenn weniger Bytes
im Puffer sind als im Parameter "Count" angegeben. Bekomme ich dann eine
Exception oder wird dann einfach gelesen, was da ist?
Grüße, Alex
Alexander H. schrieb:> ich werde das ausprobieren. Ich lese in der Tat die Bytes im> Received-Event.
und was ist wenn 2 Byte auf einmal gekommen sind? Kann besonders bei
langsamen PC auftreten. Dann bleiben sie zwar im Buffer aber es erfolgt
kein erneutes Event. Du musst wenn du mit Events arbeitest immer
Bytesavailable auswerten.
Auch dein port2ReadFrom.ReadLine() darfst du nicht im Event aufrufen.
Das Blockiert dein Programm wenn kein Zeilenende kommt.
> Eine Frage noch zu Deinem Vorschlag. Wass passiert, wenn weniger Bytes> im Puffer sind als im Parameter "Count" angegeben. Bekomme ich dann eine> Exception oder wird dann einfach gelesen, was da ist?
steht alles in der Doku. Es werden einfach die Byte gelesen die
vorhanden sind. Im return steht dann wie viele Bytes gelesen wurden.
Eine timeoutException kommt nur wenn gar kein Byte gelesen wurde.
Die volle Baudrate konnten schon die ersten XT PC's völlig problemlos
verarbeiten.
Bei Deinen Baudraten wirst Du auf Hardwarehandshake sicher auch
verzichten können.
Ich wette Deine Software läuft bei niederringen Baudraten einwandfrei
versuche das mal.
Du hast sicher ein Softwareproblem.
Womit programmierst Du auf der PC Seite, wieso gibt es hier keinen
Sourcecode von Dir.
So kann doch niemand helfen!
Ibmkenner schrieb:> Womit programmierst Du auf der PC Seite, wieso gibt es hier keinen> Sourcecode von Dir.> So kann doch niemand helfen!
hast du den Thread überhaupt gelesen?
Source ist vorhanden, Sprache ist auch eindeutig erkennbar.
Fehler sind (vermutlich) auch schon gefunden.
> Die volle Baudrate konnten schon die ersten XT PC's völlig problemlos> verarbeiten.
damals war die volle Baudrate aber nur 115.000.
und allocierter Liste läuft es im Augenblick auch auf unserem ältesten
XP-Rechner.
Falls sich jemand wundert, dass ich BytesToRead nicht abfrage. Ich
möchte, dass die Readfunktion ins Timeout läuft (1000 ms), wenn keine
daten mehr kommen. Das ist für mich das Zeichen, das alles übertragen
wurde.
Vielen Dank für die vielen Ratschläge.
Grüße, Alex
Alexander H. schrieb:> und allocierter Liste läuft es im Augenblick auch auf unserem ältesten> XP-Rechner.>> Falls sich jemand wundert, dass ich BytesToRead nicht abfrage. Ich> möchte, dass die Readfunktion ins Timeout läuft (1000 ms), wenn keine> daten mehr kommen. Das ist für mich das Zeichen, das alles übertragen> wurde.> Vielen Dank für die vielen Ratschläge.
Schön das es jetzt geht. Aber als Richtig und sauber würde ich das noch
immer nicht Bezeichnen.
im Event hat man nicht auf ein Timeout zu warten!
Entweder arbeitest mit Event, dann liest man nur Zeichen die aktuell
verfügbar sind oder man arbeitet mit einen Eigenen Thread und Liest
einfach mit Read die Daten ein.
Auch wird das Array bei jedem Eventaufruf neu erstellt. Dann werden die
Daten auch noch einmal umkopiert. Du hast wirklich glück die PCs heute
alle schnell genug sind, denn performant programmiert ist da nicht.
Man legt einen buffer an, und in diesen werden dann die Daten eingelesen
und zwar mit einem Offset, wenn man mehrfach read verwendet.
Und ein Protokoll das Daten bis zum Timeout liest ist eigentlich auch
etwas sehr schlechtes. Wenn jemand den Stecker zieht, kommt auch ein
Timeout und es wurden nicht alle Daten übertragen. Auch das Daten immer
noch verloren gehen könne, weil du keine Prüfsummer hast.
Ich hoffe nur das keine wirklich wichtige Dinge damit gemacht werden.
Hallo Peter,
auch wenn es jetz läuft, werde ich mir das ganze noch mal zu Gemüte
führen. So richtig gefällt mir das auch nicht mit dem Timeout. Mein
Problem ist unter anderem, dass ich sowohl String-Daten wie auch 16Bit
-Binärdaten empfange. Ich habe lange überlegt, wie ich eine Ende-Kennung
gestalten kann beim Senden der Binärdaten, aber mir fällt nix
Eindeutiges ein, was nicht auch als Daten-Byte interpretierbar wäre.
Vielleicht kannst Du mir einen Rat geben. Dann hätte ich das Problem mit
dem Timeout nicht.
Grüße, Alex
Alexander H. schrieb:> Eindeutiges ein, was nicht auch als Daten-Byte interpretierbar wäre.
Für diese Fälle wurde das KISS Protokoll erfunden. Ein Zeichen wird als
Escape Zeichen (FESC) definiert. Dazu gibt es Frame Anfang-Ende (FEND).
Jedes Frame beginnt mit FEND und endet mit FEND. Tritt FEND mitten in
den Daten auf, so wird es als FESC-FEND gesendet. Tritt FESC auf, sendet
man FESC-FESC.
Der Overhead ist minimal. Wer mag, fügt noch eine Prüfsumme hinzu.
Peter II schrieb:> Jim Meba schrieb:>> Der Flaschenhals ist vermutilch der FIFO im FT232 Chip, denn dessen>> Daten werden unregelmäßig abgeholt.>> unwahrscheinlich. Der Chip kann bis 1Mbit/s - dann wird der Puffer auch> dafür ausgelegt sein, so mal hier nur 1/4 davon gebraucht wird.>> Außer hat eine USB-Festplatte am gleiche Hostcontroller und überträgt> dort fleißig Daten.
SCHROTT USB FTDI aus china können auch weniger, viel weniger sogar.
ZITAT:
"Nach drei Tagen das Teil erhalten. Schnell die Arduino Software
installiert. Das Teil angestöpselt ( blinkt munter rot vor sich hin).
Dann die Odysse mit den FTDI Treibern nachvollzogen und alles
entsprechend manuel umgefrickelt - der NANO zuckt immer noch nicht. Nach
dem Upload kommen Fehlermeldungen, aus denen sich schließen lässt das
die Kommunikation fehlschlägt. Manchmal werden auch die Ports wild
gewechselt. Booten, Reset, alles bringt nichts. Letzten Endes finde ich
in irgendeinem Forum eine Diskussion, die damit endet, dass es sich bei
solchen China Kopien von diesem Board schlicht und ergreifen um
funktionsunfähige Boards handelt, da vermutlich der falsche Chip
anstelle des FTDI aufgelötet wurde. Ich habe also einen Tag Zeit
verloren um heraus zu finden, dass diese Teile nicht nur Schrott sind,
sondern sogar nicht laufen können. Andere User haben tagelang nach der
Ursache gesucht und so steht es jetzt im netz, in Stein gemeißelt und
ich kann das nur bestätigen. Diese China Klone mit den gefälschten FTDI
Chips laufen nicht. Sie sind Schrott. Nicht kaufen. Oder eines
bestellen, zurück geben und dem Laden einen saftigen Kommentar
reinschreiben.
Also öffne ich - stinksauer - einen Fall, und darf dann Päckchen packen
und die beiden NANOS die eher NONAs sind zurück schicken - vermutlich
über Berlin nach HonKong.
Eigentlich wollte ich mich ja nicht groß mit Hardware beschäftigen, ich
wollte einen kleinen Mikrocontroller, den ich an den USB anstöpsel,
schnell mit meinem Programm (3 7-Segmentanzeigen per Nummernblock
ansteuern) flashe und fertig."
Ich sehe schon, das ist noch nicht durchgestanden, denn am Ende hätte
ich gern eine sauber Lösung und nix gefrickeltes. Man lernt nicht aus.
@Georg: Vielen Dank. Das lässt sich ja dann doch recht einfach
implementieren. Werde ich so übernehmen.
Grüße, Alex
So, ich habe nach einiger Recherche, Versuchen und "Programmierübungen"
eine Lösung gefunden, von der ich denke, dass sie deutlich besser ist
als der alte Stand. Ich würde euch noch einmal um Feedback bitte. Vielen
Dank auch nochmal an alle und besonders an Georg (df2au) für die tolle
Unterstützung beim Implementieren des KISS-Protokolls.
catch (Exception exc) { /*throw new Exception(exc.Message, exc);*/ }
19
this.portResult.GetFrameFromBufferAsynch();
20
}
21
22
public void GetFrameFromBufferAsynch()
23
{
24
bool startNew = true;
25
if (this.thread != null)
26
startNew = !this.thread.IsAlive;
27
28
if(startNew)
29
{
30
this.thread = new System.Threading.Thread(new System.Threading.ThreadStart(this.getKissFrameFromReadBuffer));
31
this.thread.Start();
32
}
33
}
Ich verwende eine ConcurrentCollection (ist thread safe) als FIFO, in
die ich die empfangenen byte[] Arrays mit Enqueue(array) zufüge.
GetFrameFromBufferAsynch() liest die byte[] Arrays mit TryDequeue aus
und macht aus dem KISS-Protokoll wieder die Rohdaten in einem eigenen
Tread. Decodierte Frame werden per Event an das Window weitergemeldet.
Ist das FIFO leer wird der Thread beendet. Immer wenn neue Daten
empfangen werden, wird geschaut, ob das FIFO schon leer ist oder der
Thread noch arbeitet. Es werden die Daten angehängt und der Thread wird
gegebenenfalls wieder gestartet.
Vielleicht hat der eine oder andere ein ähnlich geartetes Problem und
findet das hier hilfreich. Wie gesagt, Verbesserungsvorschläge sind
erbeten.
Beste Grüße,
Alex
Kurze Ergänzung noch. Obwohl ich den Empfangsevent so kurz wie möglich
gehalten habe, passiert es noch, dass Daten verloren gehen. Das aber
eigentlicghnur noch, wenn ich auf einem Singlecore-Rechner mit zwei
Datenübertragungen gleichzeitig arbeite. Wäre es sinnvoll, den Thread
immer am Laufen zuhalten und zu warten, bis neue Daten in der Queue
sind? Ich kann nicht so recht abschätzen, wie wieviel Zeit
GetFrameFromBufferAsynch, also das starten des Threads benötigt. Mir
gefällt auch die neue Speicherallozierung
byte[] bytes = new byte[bytes2Read];
nicht. Ich weiß aber nicht, wie ich das mit einem statischen Speicher
lösen soll. Weil ich ja keine leeren. Array-Elemente in die Queue
schreiben will, müsste ich das wieder umkopieren, was auch nur Zeit
braucht.
Grüße, Alex