Forum: Mikrocontroller und Digitale Elektronik FT232R Hardware Handshake Verständnisproblem


von Alexander H. (ill_son)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

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

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

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.

: Bearbeitet durch User
von Georg G. (df2au)


Lesenswert?

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...

von Peter II (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Georg G. (df2au)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

1
if (!transmissionState.IsTransmitting)
2
{
3
  readString = port2ReadFrom.ReadLine();
4
  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

: Bearbeitet durch User
von Alexander H. (ill_son)


Lesenswert?

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.

von Horst (Gast)


Lesenswert?

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.

von holger (Gast)


Lesenswert?

Wenn das ein Progressindikator ist

  this.transmissionState.IncreaseProgress(1);

solltest du das nicht für jedes Byte machen.
Eher so alle 256.

von Mike (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

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.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Ibmkenner (Gast)


Lesenswert?

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!

von Peter II (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

Salut,

kurzes Feedback an alle Ratgeber.

Mit

1
byte[] readBuffer = new byte[SerialPortSettings.ReadBufferSize];
2
3
cnt = this.port.Read(readBuffer, 0, SerialPortSettings.ReadBufferSize);
4
if (cnt > 0)
5
{
6
    buffer.BinaryData.AddRange(readBuffer.Take<byte>(cnt));
7
    this.transmissionState.IncreaseProgress(cnt);
8
}

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

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

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.

von Alexander H. (ill_son)


Lesenswert?

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

von Georg G. (df2au)


Lesenswert?

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.

von GERD (Gast)


Lesenswert?

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."

von Alexander H. (ill_son)


Lesenswert?

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

von Alexander H. (ill_son)


Lesenswert?

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.
1
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
2
{
3
    SerialPort port = sender as SerialPort;
4
5
    try
6
    {
7
        if (port != null)
8
        {
9
            if (port.IsOpen)
10
            {
11
                int bytes2Read = port.BytesToRead;
12
                byte[] bytes = new byte[bytes2Read];
13
                this.portResult.ReadBytes = port.Read(bytes, 0, bytes2Read);
14
                        this.portResult.RingBufferCollection.Enqueue(bytes);
15
            }
16
        }
17
    }         
18
    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

von Alexander H. (ill_son)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.