Virtueller COM-Port über USB



  • Ein Gerät schickt immer dieselbe Nachricht (0xAA0000AA) über USB. Diese wird dann über einen virtuellen COM-Port gelesen. Ich kann mit Realterm den COM-Port öffnen, als hex anzeigen lassen und sehe dort meine 0xAA0000AA-Nachricht. Das Gerät scheint also zu funktionieren.
    Nun öffne ich den COM-Port in meinem Programm:

    struct {
    	char buffer[32];
    	OVERLAPPED ovr;
    	HANDLE comhandle;
    }cp = {};
    
    cp.ovr.hEvent = CreateEvent(0, true, 0, 0);
    
    cp->comhandle = CreateFileA(comportname, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
    
    bool reading = false;
    for (;;){
    	unsigned long int transactionsize = 0;
    	if (!reading){
    		if (!ReadFile(cp->comhandle, cp->buffer, sizeof cp->buffer - 1, 0, &cp->ovr)){
    			if (GetLastError() != ERROR_IO_PENDING)
    				return 0;
    		}
    		reading = true;
    	}
    	if (GetOverlappedResult(cp->comhandle, &cp->ovr, &transactionsize, 1))
    		reading = false;
    	for (int i = 0; i < transactionsize; i++)
    		printf("[%02x] ", (unsigned char)cp->buffer[i]);
    }
    

    Ausgabe: [98] [e6] [00] [00] [98] [e6]

    Wieso wurde aus dem 0xAA ein 0x98E6 und meine 4 Byte lange Nachricht auf einmal 6 Byte lang?
    Andere Nachrichten (mit anderen Werten für die Nullen aber gleichen 0xAA am Anfang und Ende) werden ähnlich verwurschtelt, aber nicht immer wird das 0xAA am Ende in ein 0x98E6 umgewandelt.

    Bei einem anderen sehr ähnlichen Gerät funktioniert mein Code, dieses benutzt allerdings nur ASCII.

    Ich vermute, dass irgendwie ein Textmodus aktiviert ist, der die nicht-ASCII-Zeichen irgendwie interpretiert, aber ich habe nichts dazu in der Dokumentation von ReadFile, GetOverlappedResult und CreateFile gefunden.



  • Habs nicht getestet, aber mit CreateFileA() und einem char Puffer hätte ich
    konsequent auch ReadFileA() verwendet.

    Da es sich ausserdem um einen virtuellen COM-Port handelt würde ich auch
    Baudrate, Stopbits u.s.w. einstellen und mich nicht darauf verlassen, das
    das schon passt.



  • Versuch mal die diversen Parameter ala Baudrate, Byte-Länge, Anzahl Stop-Bits etc. einzustellen ( GetCommState , SetCommState ).
    Und deine Empfangsschleife ist falsch.
    Lies nochmal die Doku zu ReadFile und GetOverlappedResult durch, es gibt da einige Fälle die nicht abgedeckt sind.



  • merano schrieb:

    Habs nicht getestet, aber mit CreateFileA() und einem char Puffer hätte ich konsequent auch ReadFileA() verwendet.

    ReadFileA gibt es bloss nicht.

    Da es sich ausserdem um einen virtuellen COM-Port handelt würde ich auch
    Baudrate, Stopbits u.s.w. einstellen und mich nicht darauf verlassen, das
    das schon passt.

    Komische Logik.
    Wieso sollten diese Einstellungen bei einem echten COM-Port schon passen, bei einem virtuellen dann aber nicht?



  • hustbaer schrieb:

    Komische Logik.
    Wieso sollten diese Einstellungen bei einem echten COM-Port schon passen, bei einem virtuellen dann aber nicht?

    Ich würde es bei jeder Art COM-Port konfigurieren ...

    Es kann natürlich auch "zufällig" schon passen 🙂



  • Ein virtueller COM-Port über USB hat weniger Einstellmöglichkeiten als ein echter, das habe ich schon herausgefunden. Es ist zum Beispiel egal welche Baudrate man einstellt, da immer die USB-Geschwindigkeit benutzt wird.

    Dennoch habe ich bereits alles eingestellt, denke ich:

    //HANDLE comporthandle = CreateFileA(...);
    	SetupComm(comporthandle, 1024*1024, 16384); //buffergrößen werden ignoriert :(
    	{ //get COM port info
    		DCB portInfo; //Device Control Block
    		if (!GetCommState(comporthandle, &portInfo)){
    			RAISEERROR(L"Failed getting information for com port");
    			CloseHandle(comporthandle);
    			return INVALID_HANDLE_VALUE;
    		}
    
    		//set COM port info
    		portInfo.ByteSize = 8;
    		portInfo.Parity = NOPARITY;
    		portInfo.StopBits = ONESTOPBIT;
    		portInfo.fDtrControl = DTR_CONTROL_ENABLE;
    		portInfo.XoffLim = MAXWORD;
    		portInfo.XonLim = 0;
    		portInfo.XonChar = 0x11;
    		portInfo.BaudRate = CBR_256000;
    		portInfo.ErrorChar = 0x0;
    		portInfo.EofChar = 0x0;
    		portInfo.EvtChar = 0x0;
    
    		portInfo.fDsrSensitivity = false;
    		portInfo.fTXContinueOnXoff = true;
    		portInfo.fOutX = false;
    		portInfo.fInX = false;
    		portInfo.fErrorChar = false;
    		portInfo.fNull = false;
    		portInfo.fAbortOnError = false;
    
    		if (!SetCommState(comporthandle, &portInfo)){
    			RAISEERROR(L"Failed setting COM port");
    			CloseHandle(comporthandle);
    			return INVALID_HANDLE_VALUE;
    		}
    	}
    	{ //configure the port
    		COMMTIMEOUTS timeouts;
    		if (!GetCommTimeouts(comporthandle, &timeouts)){
    			RAISEERROR(L"Failed getting COM timeouts");
    			CloseHandle(comporthandle);
    			return INVALID_HANDLE_VALUE;
    		}
    		timeouts.ReadIntervalTimeout = MAXDWORD;
    		timeouts.ReadTotalTimeoutConstant = 10;
    		timeouts.ReadTotalTimeoutMultiplier = 0;
    		timeouts.WriteTotalTimeoutConstant = 100;
    
    		if (!SetCommTimeouts(comporthandle, &timeouts)){
    			RAISEERROR(L"SetCommTimeouts");
    			CloseHandle(comporthandle);
    			return INVALID_HANDLE_VALUE;
    		}
    	}
    	return comporthandle;
    

    Uhm.... Jetzt fällt mir gerade Zeile 15 auf, da sollte DTR_CONTROL_DISABLE stehen, daran liegts wahrscheinlich. Ich kann es aber erst morgen ausprobieren.



  • DTR sollte egal sein, so lange das Gerät nicht irgendwie auf den Zustand der DTR Leitung reagiert. Was normale Geräte nicht machen wenn sie keinen Hardware-Handshake unterstützen. Und virtuelle COM Ports erst recht nicht.

    Aber ist das Ding auch sicher ein virtueller COM Port? Könnte auch ein USB -> RS232 Adapter sein an den ein normales serielles Gerät angeschlossen ist.
    Dann würden Bitrate etc. ne Rolle spielen.

    Und wie gesagt, deine Empfangsschleife ist falsch. Das würde ich mal als erstes fixen.
    Beispiel: ReadFile kann gleich TRUE zurückliefern, auch mit OVERLAPPED , und zwar wenn die Operation sofort abgeschlossen werden konnte. Was durchaus sein kann, wenn die Daten bereits komplett im Empfangspuffer stehen.
    Den Fall berücksichtigst du überhaupt nicht. In dem Fall rufst du trotzdem GetOverlappedResult auf, was mMn. ein Fehler ist.
    Und was GetOverlappedResult angeht... der Code macht für mich überhaupt keinen Sinn.
    Sieht so aus als ob du pollen wolltest ( bWait = 1 statt bWait = TRUE , das reading = false ), nur tust du das nicht. Wenn du pollen willst musst du WaitForSingleObject nehmen oder bWait = 0 (bzw. FALSE ) an GetOverlappedResult übergeben.



  • Nach einigem Experimentieren stellt sich heraus, dass die Baudrate doch einen Einfluss hat, mit dem richtigen Wert kommen die richtigen Nachrichten an. Bei den anderen Geräten machte das keinen Unterschied.
    Ich sehe mir das mit dem ReadFile und GetOverlappedResult nochmal an. Ich wollte so halb pollen, also auf das Event warten, aber nach einem Timeout abbrechen, da ich prüfen möchte, ob zum Beispiel der Stecker gezogen wurde oder das Programm terminieren soll. Wahrscheinlich muss ich WaitForMultipleObjects benutzen.

    Danke.



  • nwp3 schrieb:

    Nach einigem Experimentieren stellt sich heraus, dass die Baudrate doch einen Einfluss hat, ...

    👍


Log in to reply