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 werdenIch 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 TimeoutSetCommTimeouts
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 demSetCommTimeouts