FTD2XX.dll Streaming, bleibt hängen und stürzt ab.



  • Hallo miteinander.

    Ich habe seit längerem ein "kleines" Problem, beidem ich überhaupt nicht mehr weiter komme: Ziel ist es ein Stream-Transfer von Daten zu erreichen, von einem PC zu einem MCU. Doch leider happerts irgendwie irgendwo und ich habe keine Ahnung, wo das Problem ist, respektive wie ich es orten könnte.

    Das ganze Streamen läuft über einen Timer ab. Dieser wird periodisch alle 1ms aufgerufen um zu überprüfen ob der MCU neue Daten benötigt (passiert etwa alle 1.5s). Wenn ja, stellt er diese zusammen und sendet sie. Eigentlich sollte das nicht so schwer sein.

    Zu den Problemen:

    1. Der TX-Buffer ist noch eine Char-Array, da ich der Funktion ft_write keine Liste oder Array übergeben kann. Hat hier eventuell jemand ein Tipp?
      Fehlermeldung ist, dass das 2. Argument nicht in LPVOID konvertiert werden kann.

    2. Besagtes "Hängenbleiben". Heiss soviel, wie die Software reagiert nicht mehr...
      Uhrsache habe ich wie gesagt keine Ahnung, was es sein könnte.

    Hier ist einmal die Timer-Funktion:

    public: System::Collections::Generic::List<static unsigned int> x_achse;
    	public: System::Collections::Generic::List<static unsigned int> y_achse;
    	public: System::Collections::Generic::List<static unsigned char> red;
    	public: System::Collections::Generic::List<static unsigned char> green;
    	public: System::Collections::Generic::List<static unsigned char> blue;
    	static int first = 1;
    	static unsigned int NUM = 5292 * 6;
    
    	private: System::Void timerCom_Tick(System::Object^  sender, System::EventArgs^  e) {
    					char TxBuffer[20];
    					char RxBuffer[20];
    					static char first = 1;
    					static int last_point = 0;											// Pointer für Listenzugriff
    					static char buffer[64000];
    					char RX_ENQ = 0;
    static int rx_val = 0;									// Zählt Anzahl Anfragen für neue Werte
    System::Collections::Generic::List<static unsigned char> StreamBuf;			// Versuch TX Array mit Liste dynamisch zu machen
    StreamBuf.Clear();
    					for(size_t i = 0; i < 20; i++){
    						TxBuffer[i] = 0;
    						RxBuffer[i] = 0;
    					}
    
    					/*
    						Streaming
    					*/
    					if((buttonStart->Tag == "1") && (loadToolStripMenuItem->Tag == "1") && (radioButtonPCC->Checked == true)){
    						if(first == 1){														// Ersten Werte wurden gesendet 
    							first = 0;
    							NUM *= 2;														// Beim ersten mahl doppelte Punkte senden
    						}	
    						else{
    							FT_GetStatus(ftHandle, &RxBytes, &TxBytes, & EventDWord);
    							if(RxBytes > 0){
    								ftStatus = FT_Read(ftHandle, RxBuffer, RxBytes, &BytesReceived);
    								if(ftStatus == FT_OK){
    									if(RxBytes == 1){
    										if(RxBuffer[0] == ENQ){
    											// Stream-Anfrage
    											RX_ENQ = 1;
    										}
    										else{												
    											// Kein ENQ erhalten
    											RX_ENQ = 0;
    										}
    									}
    									else{
    										// To mutch RxBytes
    										for(size_t i = 0; i < (sizeof(RxBuffer) / sizeof(RxBuffer[0])); i++){
    											if(RxBuffer[i] == ENQ){							// Unter anderem ENQ erhalten
    												RX_ENQ = 1;
    											}
    											else{											
    												// Kein ENQ erhalten;
    												RX_ENQ = 0;
    											}
    										}
    									}
    								}
    								else{
    									// FT_Read Failed
    									L_info->Text = "Error: FTDI not connected";
    									Icon_box->Visible = false;
    								}
    							}
    							else{
    								// Kein ENQ erhalten
    								RX_ENQ = 1;
    							}
    						}
    
    						for(size_t i = 0; i < 20; i++){
    							TxBuffer[i] = 0;
    							RxBuffer[i] = 0;
    						}
    
    						/*
    							Frame streamen
    						*/
    						if(RX_ENQ == 1){
    rx_val ++;
    label2->Text = System::Convert::ToString(rx_val);
    
    							buffer[0] = ENQ;
    							buffer[1] = SOH;													// SOH und CMD senden
    							buffer[2] = STREAM;
    							buffer[3] = NUM >> 8;
    							buffer[4] = (NUM & 0xFF);
    							for(size_t i = 5; i < (NUM + 5); (i += 6)){
    								buffer[i] = red[last_point];				// Werte zum Senden bereitstellen
    								buffer[i + 1] = green[last_point];
    								buffer[i + 2] = blue[last_point];						
    								buffer[i + 3] = (x_achse[last_point] >> 8);
    								buffer[i + 4] = ((x_achse[last_point] & 0xF0) | (y_achse[last_point] >> 12));
    								buffer[i + 5] = ((y_achse[last_point] >> 4) & 0xFF);
    								if(last_point == x_achse.Count - 1){
    									if(checkBox_rep->Checked == true)
    										last_point = 0;
    									else
    										break;
    								}
    								else
    									last_point ++;
    							}
    							buffer[NUM + 6] = ACK;
    							buffer[NUM + 7] = EOT;
    							if(ftStatus == FT_OK){
    								ftStatus = FT_Write(ftHandle, buffer, (NUM + 8), &BytesWritten);
    								if (ftStatus == FT_OK) {										// FT_Write OK 
    									for(size_t i = 0; i < (NUM + 8); i++)						// Buffer löschen
    										buffer[i] = 0;
    									if(NUM == 63504) NUM /= 2;									// Streaming mit halber Buffergrösse, nach 1. mal
    								}
    								else{
    									// FT_Write Failed
    									L_info->Text = "Error: FTDI not connected";
    									Icon_box->Visible = false;
    								}
    							}
    							else{
    								L_info->Text = "Error: FTDI not connected";
    								Icon_box->Visible = false;
    							}
    
    							RX_ENQ = 0;
    						}
    					}
    				}
    

    3. Frage: Wie könnte ich beim Visual Studio C++ Express feststellen, weshalb ein Programm hängen bleibt, respektive wo der letzte Punkt war an dem es noch funktioniert hatte (ähnlich wie Breakpoints)?

    Besten Dank für die Unterstützung
    MFG
    P51D



  • Hi P51D,

    sorry, es ist schon sehr spät (bin schon fast im Halbschlaf 😉 )
    Deshalb hab ichs mir erlaubt, meine Kommentare hier abzugeben, ohne auch nur eine Zeile von Deinem Quellcode zu lesen.

    P51D schrieb:

    Das ganze Streamen läuft über einen Timer ab. Dieser wird periodisch alle 1ms aufgerufen um zu überprüfen ob der MCU neue Daten benötigt (passiert etwa alle 1.5s). Wenn ja, stellt er diese zusammen und sendet sie.

    Nun, als ich das gelesen hab, dachte ich: "Das ist Unfug".
    Denn für die serielle Kommunikation kannst Du Events auswerten. D.h. sobald ein Zeichen angekommen ist bekommst Du das Ereignis signalisiert!
    Deshalb ist es erst recht unter Windows verpönt, mit Timer den seriellen Empfangspuffer periodisch zu pollen.

    Zweiter wichtiger Punkt: Windows ist kein Echtzeitbetriebssystem, schon gar nicht in der Größenordnung von 1ms. Eine Zeitscheibe hat, salopp ausgedrückt, nun mal nur ca. 10..17ms Dauer.

    Martin





  • Mmacher schrieb:

    Nun, als ich das gelesen hab, dachte ich: "Das ist Unfug".
    Denn für die serielle Kommunikation kannst Du Events auswerten. D.h. sobald ein Zeichen angekommen ist bekommst Du das Ereignis signalisiert!
    Deshalb ist es erst recht unter Windows verpönt, mit Timer den seriellen Empfangspuffer periodisch zu pollen.

    Ok, ich werde mir dann demnach nochmals das "D2XX_Programmer's_Guide" anschauen, ob dort irgendetwas drinn steht über Events oder Buffer löschen.
    Ich dachte mir nähmlich, dass es ebenfalls etwas mit dem Buffer des Treibers an sich zu tun haben könnte, da jedesmal ungefähr bei 10MB gesendete Daten das ganze abstürzt.

    Mmacher schrieb:

    Zweiter wichtiger Punkt: Windows ist kein Echtzeitbetriebssystem, schon gar nicht in der Größenordnung von 1ms. Eine Zeitscheibe hat, salopp ausgedrückt, nun mal nur ca. 10..17ms Dauer.

    Martin

    Deshalb habe ich auf dem MCU-Print eine RAM-Erweiterung von 64kB, à je 32kB. Heisst sobald ein RAM ausgelesen wurde, wird eine Anfrage an den PC gesendet.
    das Senden dauert in etwa 1Sekunde und die Ausgabe dann 1.5 Seukunden. Daher hat der PC bei einer Reaktions-Zeit von 10 bis 17ms längstens genügend Zeit, da eine Differenz von 500ms existiert und der MCU parallel am 2. RAM am ausgeben ist.

    Falls mir das Dokument zum Treiber nicht weiterhilft, wende ich mich halt direkt an FTDI, mal sehen, was die dazu sagen.

    Besten Dank für die Antwort.
    MFG
    P51D



  • Hi, ich bins wieder 🕶

    Eines vorneweg: Ich komme aus dem Win32-API Lager.
    D.h. ich kenne mich speziell mit CLI und .NET nicht aus, diese Techniken sind aber IMHO für Deine serielle Kommunikation nicht relevant.

    Im "D2XX Programmer's Guide" (Version 1.02 vom 19.01.2010) findest Du auf Seiten 37-39 ein Beispiel zur Ereignis-Auswertung.
    Die wichtigsten Funktionen hierfür sind:

    CreateEvent()
    FT_SetEventNotification()
    WaitForSingleObject() bzw. WaitForMultipleObject()
    

    P51D schrieb:

    Ich dachte mir nähmlich, dass es ebenfalls etwas mit dem Buffer des Treibers an sich zu tun haben könnte, da jedesmal ungefähr bei 10MB gesendete Daten das ganze abstürzt.

    Hmmm, mein Bauch sagt mir, daß das Abstürzen wohl kaum daherkommt, daß Du die Polling-Technik anwendest.
    Die Ursache ist woanders zu finden.
    Erster Ansatz: Die Speicherverwaltung? Was machst Du mit den Daten? Schreibst Du sie als Datenstrom auf die Festplatte oder "sammelst" Du sie im RAM-Speicher?

    Mit welcher Baudrate wird kommuniziert? Bei hohen Baudraten könnte der Zwischen-Buffer mit nur 20 Zeichen doch sehr eng werden.

    Doch Halt! Bevor Du das ganze umbaust, eine Prinzipfrage:
    Gibt es einen besonderen Grund, warum Du genau die Funktionen von FTD2XX.dll nutzt?
    (In meinen Applikationen verwende ich COM-Ports sowohl mit Funktionen aus FTD2XX.dll als auch mit Windows-API, kenne also die beiden Welten)

    Ich nehme mal an, hier kommt ein USB-auf-RS232 Konverter zum Einsatz, basierend auf eines der ICs von der Firma FTDI (z.B. FT232R).
    Und dieser kann dank des VCP-Treiber (VCP=Virtual COM-Port) durch Windows eigene Funktionen genau so angesprochen werden, wie wenn Du eine echte COM1-Schnittstelle am Mainboard hättest.

    Und mit den Windows-API Funktionen bist Du unabhängig von verwendeter Hardware. Man denke da nur an Remote COM-Port an einem anderen PC mittels Ethernet....

    Martin



  • Mmacher schrieb:

    Hi, ich bins wieder 🕶
    Im "D2XX Programmer's Guide" (Version 1.02 vom 19.01.2010) findest Du auf Seiten 37-39 ein Beispiel zur Ereignis-Auswertung.
    Die wichtigsten Funktionen hierfür sind:

    CreateEvent()
    FT_SetEventNotification()
    WaitForSingleObject() bzw. WaitForMultipleObject()
    

    Dies habe ich ebenfalls gefunden, nur wurde ich daraus irgendwie nicht schlau: Ich soll im Programm irgendwo auf den Event warten?? über eine while-Schlaufe oder wie? Generiert mir der FTD2xx Treiber auch so ein Event, wie zum Beispiel der Timer, damit ich nur die Funktion aufrufen muss?

    Mmacher schrieb:

    P51D schrieb:

    Ich dachte mir nähmlich, dass es ebenfalls etwas mit dem Buffer des Treibers an sich zu tun haben könnte, da jedesmal ungefähr bei 10MB gesendete Daten das ganze abstürzt.

    Hmmm, mein Bauch sagt mir, daß das Abstürzen wohl kaum daherkommt, daß Du die Polling-Technik anwendest.
    Die Ursache ist woanders zu finden.

    Rein von der Logik her hast du damit recht. Es müsste auf beide Seiten eigentlich gleich funktionieren.

    Mmacher schrieb:

    Erster Ansatz: Die Speicherverwaltung? Was machst Du mit den Daten? Schreibst Du sie als Datenstrom auf die Festplatte oder "sammelst" Du sie im RAM-Speicher?

    Über eine Button-Funktion wird eine Datei spezifiziert und dann in die listen rot, gruen, blau, x_achse und y_achse gespeichert. Diese sind global. Da aber im Ursprungsfile noch zusätzliche Informationen vorhanden sind, die für das Streamen irrelevant sind, ist dazwischen noch ein Funktions-Buffer (simple char-Array).

    Mmacher schrieb:

    Mit welcher Baudrate wird kommuniziert? Bei hohen Baudraten könnte der Zwischen-Buffer mit nur 20 Zeichen doch sehr eng werden.

    Baudraten spielen keine Rolle, da ich einen FT245 nutze. Dieser hat ein Parallel-Interface, wodurch keine RS232-Schnittstelle simuliert werden muss.

    Mmacher schrieb:

    Doch Halt! Bevor Du das ganze umbaust, eine Prinzipfrage:
    Gibt es einen besonderen Grund, warum Du genau die Funktionen von FTD2XX.dll nutzt?
    (In meinen Applikationen verwende ich COM-Ports sowohl mit Funktionen aus FTD2XX.dll als auch mit Windows-API, kenne also die beiden Welten)

    Ich nehme mal an, hier kommt ein USB-auf-RS232 Konverter zum Einsatz, basierend auf eines der ICs von der Firma FTDI (z.B. FT232R).
    Und dieser kann dank des VCP-Treiber (VCP=Virtual COM-Port) durch Windows eigene Funktionen genau so angesprochen werden, wie wenn Du eine echte COM1-Schnittstelle am Mainboard hättest.

    Und mit den Windows-API Funktionen bist Du unabhängig von verwendeter Hardware. Man denke da nur an Remote COM-Port an einem anderen PC mittels Ethernet....

    Martin

    Ich möchte eigentlich nicht auf den VCP-Treiber zugreifen, da sonst die Geschwindigkeit zu gering wäre für das Streamen. Deshalb nutze ich auch die Funktionen aus der FTD2xx.dll.
    Ursprünglich hatte ich schon mühe, überhaupt eine lauffähige dll von FTDI zu bekommen, da die meisten irgendwie nicht kompiliert werden konnten. Aber sonst gibts keien speziellen Grund für die Wahl. Com-Ports in diesem Sinne brauche ich auch nicht. Man könnte sich das ganze auch wie ein USB-Stick vorstellen, der einfach Daten empfängt, und diese auf einem LCD dann Pixelweise darstellt (daher die RGB, x und y Werte).

    Besten Dank für die Unterstützung
    MFG
    P51D



  • Hallo

    Ok, ich habe mich nochmals etwas an das ganze herangetastet. Diesmal von der MCU-Seite.
    Das Problem war dort, dass das Lesen und Schreiben der RAMs zu lange gedauert hat, im Vergleich zum Daten-Verbrauch.
    Trotzdem, dass ich auf dem PC nur jeweils ein ENQ hätte ausgewertet, wenn ich mehrere empfangen hätte, hat dies mir irgendwie alles durcheinander geworfen.

    Jetzt habe ich die Schreib und Lesezyklen für die RAM's von der Dauer her um den Faktor 2.5 verringert und sie da, es läuft.

    Jetzt ist nur noch die Unsauberheit des riesigen Arrays als TX-Buffer. Hat da jemand eine Idee, wie ich dies mit Listen oder Vectoren lösen könnte, und dies dann in den FTD2xx kompatiblem LPVOID konvertieren kann, für die TX-Funktion?

    MFG
    P51D


Anmelden zum Antworten