Probleme mit empfangen von Daten (Serielle Schnittstelle)



  • Ich schreibe ein Programm, wo der Rechner mit einem Microcontroller über eine serielle Schnittstelle kommunizieren soll. Dabei sollen zwei Funktionen realisiert werden:
    - ich schicke ihm Daten auf die er dann antwortet
    - er sendet mir daten, auf welche ich dann reagiere (z.B. ein Timer ist im Microcontroller abgelaufen und er schickt mir zu: Timer End)

    Den "Warteteil" (WaitCommEvent) hab ich mit einem Thread realisiert:

    //Thread um auf ein Event/eine Nachricht vom Microcontroller zu warten, währrend das Programm normal weiter laufen kann und der PC weiter mit dem MC kommunizieren kann	
    DWORD WINAPI WaitForEventThread(PVOID lpParam)
    {					
    	while (TRUE) {
    			dwEventMask = NULL;
    			//Wait for the character to be received
    			SetCommMask(hComm, EV_RXCHAR);
    			dataReceived = WaitCommEvent(hComm, &dwEventMask, NULL); // Es wird auf Daten vom Microcontroller gewartet
    			if (dataReceived) {
    				if (dwEventMask && EV_RXCHAR) {
    					ReadBuffer = ComRead();
    
    					if (...)
    						//Vergleiche empfangene Nachricht in ReadBuffer mit eigenen Werten/eigener Nachricht und mache damit etwas...
    					else ...
    				}
    			}
    		}
    }
    

    Das Senden funktioniert eigentlich ganz gut

    bool ComWrite(char* TxBuffer, int telegramSize)
    ...
    	overlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    	// Issue write.
    	if (!WriteFile(hComm, TxBuffer, telegramSize, &dNoOfBytesWritten, &overlappedWrite)) {
    		if (GetLastError() != ERROR_IO_PENDING) 
    			// WriteFile failed, but isn't delayed. Report error and abort.
    			writeSuccess = FALSE;
    
    		else 
    			// Write is pending.
    			dwRes = WaitForSingleObject(overlappedWrite.hEvent, INFINITE);
    
    		switch (dwRes)
    		{
    			// OVERLAPPED structure's event has been signaled. 
    		case WAIT_OBJECT_0:
    			if (!GetOverlappedResult(hComm, &overlappedWrite, &dwWritten, FALSE))
    				writeSuccess = FALSE;
    			else 
    				writeSuccess = TRUE;
    
    			break;
    
    		default:
    			// An error has occurred in WaitForSingleObject.
    			// This usually indicates a problem with the
    			// OVERLAPPED structure's event handle.
    			writeSuccess = FALSE;
    			break;
    		}
    	}
    ...
    

    Allerdings das Empfangen bereitet mir Kopfzerbrechen 😕 , da er mir ein paar Daten "abschneidet oder vergisst zu senden".

    char* ComRead()
    {
    ...
    	overlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    	do {
    			Status = ReadFile(hComm, &TempChar, sizeof(TempChar), &NoBytesRead, &overlappedRead);
    			SerialBuffer[recvSize] = TempChar;
    			recvSize++;
    
    	} while (NoBytesRead > 0);
    
    	if (fWaitingOnRead) {
    		dwRes = WaitForSingleObject(overlappedRead.hEvent, READ_TIMEOUT);
    		switch (dwRes)
    		{
    			case WAIT_OBJECT_0:
    				if (!GetOverlappedResult(hComm, &overlappedRead, &NoBytesRead, FALSE))
    					ReadSuccess = FALSE;
    				else
    					ReadSuccess = TRUE;
    					fWaitingOnRead = FALSE;
    				break;
    
    			default:
    				ReadSuccess = FALSE;
    				break;
    		}
    	}
    	return SerialBuffer
    }
    

    Ich stell die Verbindung beim Programmstart in einer Dialog-Hauptfunktion her:

    //=========== Dialog-Hauptfunktion ==============
    ...
    /*---------------------------------- Opening the Serial Port -------------------------------------------*/		
    	switch (msg) {
    
    		case WM_INITDIALOG:
    
    			hComm = CreateFile(ComPortName,   // Name of the Port to be Opened
    				GENERIC_READ | GENERIC_WRITE, // Read/Write Access
    				0,                            // No Sharing, ports cant be shared
    				NULL,                         // No Security
    				OPEN_EXISTING,                // Open existing port only
    				FILE_FLAG_OVERLAPPED,         // Non Overlapped I/O
    				NULL);                        // Null for Comm Devices
    
    			/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
    			if (hComm != INVALID_HANDLE_VALUE) {
    				DCB dcbSerialParams = { 0 };                         // Initializing DCB structure
    				dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    
    				Status = GetCommState(hComm, &dcbSerialParams);      //retreives  the current settings
    				dcbSerialParams.BaudRate = CBR_9600;      // Setting BaudRate = 9600
    				dcbSerialParams.ByteSize = 8;             // Setting ByteSize = 8
    				dcbSerialParams.StopBits = ONESTOPBIT;    // Setting StopBits = 1
    				dcbSerialParams.Parity = NOPARITY;        // Setting Parity = None 
    				Status = SetCommState(hComm, &dcbSerialParams);  //Configuring the port according to settings in DCB 
    
    				/*------------------------------------ Setting Timeouts --------------------------------------------------*/
    
    				COMMTIMEOUTS timeouts = { 0 };
    				timeouts.ReadIntervalTimeout = 50;
    				timeouts.ReadTotalTimeoutConstant = 50;
    				timeouts.ReadTotalTimeoutMultiplier = 10;
    				timeouts.WriteTotalTimeoutConstant = 50;
    				timeouts.WriteTotalTimeoutMultiplier = 10;
    
    				/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
    
    				SetCommMask(hComm, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
    
    				// ==== einmal Status abfragen, damit MC angesprochen wird
    				writeSuccess = ComWrite(NachrichtAnMicrocontroller, sizeof(NachrichtAnMicrocontroller));
    				WaitForEventHwnd = CreateThread(NULL, 0L, WaitForEventThread, NULL, 0L, &dwWaitForEvent);
    
    ...
    

    Mein Problem genauer:
    Beim Programmstart sende ich eine Nachricht an den Microcontroller um diesen anzusprechen. Wenn ich danach wieder eine Nachricht an Ihn sende, bekomme ich die Antwort nicht vollständig von ihm.
    Als Beispiel:
    ich sende ihm: 0x01 0x02 0x03 0x04 0x05
    und erwarte als Antwort: 0x05 0x04 0x03 0x02 0x01.
    Allerdings bekomme ich von ihm: 0x03 0x02 0x01. 😮

    Was mich jetzt verwirrt ist, dass er beim Programmstart wenn es zum ersten mal ausgeführt wird (Siehe im Code->Hauptdialog

    ComWrite(NachrichtAnMicrocontroller, sizeof(NachrichtAnMicrocontroller));
    

    mir die richtige Anwort/Nachricht zusendet und jede weitere Kommunikation danach nicht mehr stimmt. Das Senden funktioniert, allerdings beim Empfangen hab ich ein wenig Probleme 🙄
    Hatte schon jemand mal so ein Problem? Kann mir da jemand helfen? 😃

    p.s. ich hoffe das ist jetzt nichz zu viel code auf einmal 🙄



  • Deine ComRead-Funktion ist ein klein wenig zu sehr reduziert. So ist mir z.B. nicht klar, wann und wie Du das Flag fWaitingOnRead setzt. Wenn ich aber davon ausgehe, dass Du dieses dann auf TRUE setzt, wenn "Status" von ReadFile auf FALSE gesetzt wird und GetLastError den Wert ERROR_IO_PENDING liefert.

    Wenn dem so ist, hat ReadFile noch nichts in den übergebenen Buffer geschrieben (Parameter 2, &TempChar). Das passiert dann, wenn GetOverlappedResult mit TRUE zurückgekehrt ist. Allerdings kopierst Du das gelesene Byte in diesem Falle nicht in Deinen SerialBuffer.

    Kann das schon das Problem sein?



  • Hallo Mox,

    danke für die schnelle Antwort. Leider hat das auch nicht geklappt.
    Ich habe jetzt einfach noch eine "reduzierte" Variante versucht, die wieder den Buffer ließt aber nicht vollständig.
    Der Fehler ist der gleiche, dass einige Zeichen aus dem Buffer nicht aufgenommen werden 😕

    Ich habe es jetzt so versucht:

    overlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    	do {
    			// Issue read operation.
    			if (!ReadFile(hComm, &TempChar, sizeof(TempChar), &NoBytesRead, &overlappedRead)) {
    				if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
    					MessageBox(NULL, "FAIL Read", "ERROR_IO_PENDING", MB_OK); // Error in communications; report it.
    				//else
    				//	dwRes = WaitForSingleObject(overlappedRead.hEvent, READ_TIMEOUT);
    			}
    			else 
    				writeInSerialBuf = TRUE;
    
    			dwRes = WaitForSingleObject(overlappedRead.hEvent, READ_TIMEOUT);
    			switch (dwRes)
    			{
    				// Read completed.
    			case WAIT_OBJECT_0:
    				if (!GetOverlappedResult(hComm, &overlappedRead, &NoBytesRead, FALSE))
    					MessageBox(NULL, "FAIL Read", "GetOverlappedResult", MB_OK); 
    				else
    					writeInSerialBuf = TRUE;
    
    				break;
    
    			case WAIT_TIMEOUT :
    				Sleep(1); //Sleep nur drinnen damit "etwas getan wird"
    				break;
    
    			default:
    				writeInSerialBuf = FALSE;
    				break;
    			}
    
    			if (writeInSerialBuf)
    			{
    				writeInSerialBuf = FALSE;
    				SerialBuffer[recvSize] = TempChar;
    				recvSize++;
    			}
    	} while (NoBytesRead > 0);
    

    Bin leider noch ein Laie in dem Gebiet der seriellen Kommunikation 🙄



  • Ich würde dir empfehlen statt WaitForSingleObject mit Timeout SetCommTimeouts zu verwenden. Und kein Overlapped IO. Dein Overlapped-Handling sieht nämlich etwas ... funky aus.



  • Danke hustbaer,

    Dein Tipp mit dem SetCommTimeouts hat geholfen 🙂
    Hab das vergessen zu setzen. Auch mit meiner Funky Overlapped Lösung funktioniert es dank dem SetCommTimeouts 😃