Serial Communication
-
So,ich habe mal wieder Probleme mit meiner Kommunication per rs-232.
Nachdem nicht alles so 'rund' lief, habe ich mir den tollen Artikel Serial Communication in Win32
durchgelesen und will nun einige der Sachen meinem Projekt hinzufügen.Nun stehe ich ersteinmal vor dem Problem, ob es sinnvoll ist, overlapped oder nonoverlapped communication zu betreiben.
Mein Aufbau sieht so aus: PC sendet und empfängt von einem Mikrocontroller Daten über RS-232.Ich poste hier mal meine Read- und Write-Funktionen, welche sich in einer Klasse namens CSerialInterface befinden.
BOOL CSerialInterface::WriteData (BYTE data, DWORD dwBytesToWrite) { OVERLAPPED osWrite = {0}; DWORD dwRes; BOOL fRes; osWrite.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if (osWrite.hEvent == NULL) //error creating overlapped event handle return FALSE; // issue writing if (!WriteFile(hInterface, &data, dwBytesToWrite, &_dwDataWritten,&osWrite)) { if (GetLastError() != ERROR_IO_PENDING) // WriteFile failed, but isn't delayed. Report error and abort. fRes = FALSE; else { //Write is pending dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE); switch (dwRes) { // OVERLAPPED structure's event has been signaled. case WAIT_OBJECT_0: if (!GetOverlappedResult(hInterface,&osWrite,&m_dwDataWritten,FALSE)) dwRes = FALSE; else { if ( m_dwDataWritten != dwBytesToWrite) fRes = FALSE; else // Write operation completed successfully. fRes = TRUE; } break; default: // An error has occured in WaitForSingleObject. // This usually indicates a problem with the // OVERLAPPED structure's event handle. fRes = FALSE; break; } } } else { // WriteFile completed immediately. if (m_dwDataWritten != dwBytesToWrite) // The write operation timed out. fRes = FALSE; else fRes = TRUE; } CloseHandle(osWrite.hEvent); return fRes; };
BOOL CSerialInterface::ReadData (BYTE &data, DWORD dwBytesToRead) { OVERLAPPED osRead = {0}; DWORD dwRes; BOOL fRes; m_dwDataRead; osRead.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (osRead.hEvent == NULL) // error creating event; abort return FALSE; if(!ReadFile (hInterface, &data, dwBytesToRead, &m_dwDataRead, &osRead)) { if (GetLastError() != ERROR_IO_PENDING) // ReadFile failed, but isn't delayed. Report error and abort. fRes = FALSE; else { // Read is pending dwRes = WaitForSingleObject(osRead.hEvent, INFINITE); switch (dwRes) { // OVERLAPPED structure's event has been signaled. case WAIT_OBJECT_0: if (!GetOverlappedResult(hInterface, &osRead, &m_dwDataRead,FALSE)) dwRes = FALSE; else { if (m_dwDataRead != dwBytesToRead) // The read operation timed out. fRes = FALSE; else // Read operation completed successfully. fRes = TRUE; } break; /* case WAIT_TIMEOUT: // only use, if using event timeouts // Operation isn't complete yet. // This is a good time to do some background work break; */ default: // An error has occured in WaitForSingleObject. // This usually indicates a problem with the // OVERLAPPED structure's event handle. fRes = FALSE; break; } } } else { // ReadFile completed immediately. if (m_dwDataRead != dwBytesToRead) // The read operation timed out. fRes = FALSE; else fRes = TRUE; } CloseHandle(osRead.hEvent); return fRes; };
Eingentlich will ich über einen Thread überprüfen, wann ich senden und schreiben soll bzw. kann.
Dieser Thread lautet momentan:UINT HandleData(LPVOID pParam) { CMYPROJECT* pObject = (CMYPROJECT*)pParam; // Zeiger auf mein Projekt DWORD dwRes; DWORD dwCommEvent; OVERLAPPED osStatus = {0}; osStatus.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); if (osStatus.hEvent == NULL) // error in creating event handle osStatus.hEvent return FALSE; pObject->s_Data = 0x34; //s_Data wird mit Testdaten gefüllt pObject->rs232.WriteData(pObject->s_Data,1);// rs232 ist ein Objekt von CSerialInterface while (pObject->m_bConnected) // wenn zum Mikrokontroller verbunden wurde { if (!SetCommMask(pObject->rs232.hInterface, EV_RXCHAR)) // Error setting communications event mask return FALSE; for (;;) // übernommen aus dem MSDN Artikel { if (!WaitCommEvent(pObject->rs232.hInterface, &dwCommEvent, &osStatus)) { //if (dwCommEvent & EV_RXCHAR) // das funktioniert irgendwie nicht!?!? //{ do { if (pObject->rs232.ReadData(pObject->Data,1)) // A byte has been read; process it. ; //else // An error occurred in the ReadFile call. //break; }while (pObject->rs232.m_dwDataRead != 0); //} }else // Error in WaitCommEvent break; } } CloseHandle(osStatus.hEvent); return 0; }
Ich kann zwar mit dem WaitCommEvent darauf warten, dass ich einen Character erhalte und dann anfange zu lesen, aber wie mache ich das, dass ich sage, wann ich senden kann?
Ausserdem frage ich mich, ob nicht das WaitForSingleObject() in der Read-Funktion nicht zuviel ist?!?
Ich fand den Artikel in der MSDN zwar informativ, jedoch blicke ich noch nicht ganz durch, wo ich was und wann nutzen soll.
Oder gibt es eine elegantere Lösung mit WairForMultipleObjects()?
Jemand irgendwelche Ratschläge? Oder hat das vielleicht schon jemand gemacht und kann mir Hilfestellung geben?
-
also ich bin auch nicht so der serielle Checker, doch werde ich mal mein Wissen und meine Vermutungen hier posten. Ich bin mir gar nicht sicher ob stimmt was ich sag, aber IMHO solltest du nonOverlapped machen, wenn es dir nicht allzusehr auf die Performance ankommt. Denn wenn ich mich nicht irre, kannst du viel einfacher gesendete und empfangene Bytes kontrollieren. (Da du so in EINEM Thread arbeiten kannst und nicht mit mehreren) wenn du overlapped arbeitest musst du fast mehrere Threads machen. (glaub ich)
Paul_C. schrieb:
..ich sage, wann ich senden kann?..
Na senden einfach die Writefkt aufrufen. Sinnvoll ist es davor zu checken ob gerade die leitung verwendet wird oder nicht.
Paul_C. schrieb:
Ausserdem frage ich mich, ob nicht das WaitForSingleObject() in der Read-Funktion nicht zuviel ist?!?
Du fragst ob das nicht zu viel ist und willst dann auf multipleObjects warten?? Du weißt aber schon dass das dann "noch mehr" ist. Naja also das wait for Single Object muss IMHO schon sein, da du warten musst bis die einzelnen Bytes da sind, denn wenn du nicht wartest, kann es sein dir gehen Bytes verloren und die sind dann halt weg, die kriegst du dann nimmer!!!
So viel mal von meiner Seite.
-
Ich poste einfach mal ein paar Ausschnitte aus meinen Klassen, die sich als relativ stabil erwiesen haben (auch wenn ich im wesentlichen keine Ahnung habe, was MS da nun wirklich macht...).
Habe die Programmteile einfach so lange getestet, bis ich auf zwei Rechnern Megabyte-lange Files mit 19.2 kb fehlerfrei gleichzeitig senden und empfangen konnte.Einen wesentlichen Unterschied sehe ich schon: die wesentlichen CreateEvent habe ich nur ein einziges Mal bei der Initialisierung erzeugt.
Zum Verständnis: Ich habe Ein- und Ausgabe noch über einen Ringpuffer laufen.
Geposteter Code umfaßt alles, was mit den MS-Funktionen zu tun hat.void V24::install(int baudrate) { if (!isinstalled) { char* portname[] = { "COM1", "COM2", "COM3", "COM4" }; switch (id) { case 0: case 1: case 2: case 3: break; default: hCom = INVALID_HANDLE_VALUE; return; } hCom = CreateFile(portname[id], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (hCom == INVALID_HANDLE_VALUE) { SetLastError(ERR_PORT_INIT); isinstalled = true; return; } memset((void*)&dcb, 0, sizeof(dcb)); dcb.DCBlength = sizeof(DCB); hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); gOverLapped1.Offset=0; gOverLapped1.hEvent=hEvent1; gOverLapped2.Offset=0; gOverLapped2.hEvent=hEvent2; GetCommState(hCom, &olddcb); GetCommMask(hCom, &oldEvtMask); GetCommTimeouts(hCom, &oldtim); COMMTIMEOUTS tim; tim.ReadIntervalTimeout=0; tim.ReadTotalTimeoutMultiplier=0; tim.ReadTotalTimeoutConstant=0; tim.WriteTotalTimeoutMultiplier=0; tim.WriteTotalTimeoutConstant=0; SetCommTimeouts(hCom, &tim); SetCommMask(hCom, EV_RXCHAR); SetupComm(hCom, FIFOPUFFERSIZE-1, FIFOPUFFERSIZE-1); ovflag = false; if (hCom == INVALID_HANDLE_VALUE) return; switch (baudrate) { case 110: dcb.BaudRate = CBR_110; break; case 300: dcb.BaudRate = CBR_300; break; case 600: dcb.BaudRate = CBR_600; break; case 1200: dcb.BaudRate = CBR_1200; break; case 2400: dcb.BaudRate = CBR_2400; break; case 4800: dcb.BaudRate = CBR_4800; break; case 9600: dcb.BaudRate = CBR_9600; break; case 14400: dcb.BaudRate = CBR_14400; break; case 19200: dcb.BaudRate = CBR_19200; break; case 38400: dcb.BaudRate = CBR_38400; break; case 56000: dcb.BaudRate = CBR_56000; break; case 115200: dcb.BaudRate = CBR_115200; break; case 128000: dcb.BaudRate = CBR_128000; break; case 256000: dcb.BaudRate = CBR_256000; break; default: return; } dcb.Parity = Parity[com_p]; // läuft eigentlich indirekt über Formular dcb.ByteSize = Dbits[com_d]; dcb.StopBits = Sbits[com_s]; dcb.fBinary = true; dcb.fParity = true; // ggf. auf false zurückschalten SetCommState(hCom, &dcb); Flushbuffer(); // meine eigenen Ringpuffer putzen isinstalled = true; // eigenes Installations-Flag } void V24::uninstall(void) { if (isinstalled) { if(hCom != INVALID_HANDLE_VALUE) { SetCommState(hCom, &olddcb); SetCommMask(hCom, oldEvtMask); SetCommTimeouts(hCom, &oldtim); CloseHandle(hEvent2); CloseHandle(hEvent1); CloseHandle(hCom); } isinstalled=false; } } // read wird zyklisch gerufen!!! bool V24::read(void) // true: liegt vor false: kein Zeichen { BOOL bresult; if (!isinstalled) return false; DWORD recBytes; DWORD nrBytes = 1; int maxchars=200; if (!ovflag) { loop: // hält nix länger als ein Provisorium, *grins* recBytes=0; bresult = ReadFile(hCom, (void*)inpuffer, nrBytes, &recBytes, &gOverLapped1); if (/*!bresult &&*/ !recBytes) { // GetOverlappedResult nötig // if (GetLastError()==ERROR_IO_PENDING) ovflag=true; // perform GetOverlappedResult // else // "ReadFile: no bresult/recBytes" } else /*if (bresult && recBytes)*/ // sauber angekommen { //ovflag=false; int pos=0; //while (--recBytes >= 0) sio_in->Put((char)inpuffer[pos++]); // ab in den Ringpuffer if (--maxchars>0) goto loop; } // else if (bresult && !recBytes) // "ReadFile: END OF FILE" // else // "ReadFile: Byte angekommen, aber fehlerhaftes bresult" } if (ovflag) { recBytes = 0; bresult = GetOverlappedResult(hCom, &gOverLapped1, &recBytes, FALSE); /* if (!bresult) { // Fehler aufgetreten int dwError = GetLastError(); ... } else */ { int pos = 0; //while (--recBytes >= 0) if (recBytes) { ovflag=false; sio_in->Put((char)inpuffer[pos++]); if (--maxchars>0) goto loop; } } } return (!sio_in->Empty()); } // Zeichen aus dem Inputringpuffer holen char V24::get(void) { char x; if (!isinstalled) return '\0'; sio_in->Get(x); return x; } // ggf. warten, bis v24_zyklus alles abgeschickt hat. bool V24::outstat(void) // true: frei false: warten { if (isinstalled) return sio_out->Empty(); else return true; } void V24::write(char c) // schreibt ein Zeichen in meinen Ringpuffer { // direct_out schickt auch nur in den Puffer, jedoch ohne Protokolldinge if (activated()) sio_out->Put(c); } // Puffer abschicken. Wird zyklisch gerufen!!! // Diese Routine ist die einzige, die Daten aus dem Puffer tatsächlich // abschickt. void V24::v24_zyklus(void) { char x; if (isinstalled) // wenn die Schnittstelle freigegeben ist { while (!sio_out->Empty()) // wenn was im Ausgangs-Puffer ist { sio_out->Get(x); // holt das Zeichen aus dem Ringpuffer DWORD nrBytes = 1; DWORD wrBytes; outpuffer[0]=x; // und versendet es. BOOL echo = WriteFile(hCom, (void*)outpuffer, nrBytes, &wrBytes, &gOverLapped2); if (wrBytes!=nrBytes) { do { int bResult = GetOverlappedResult(hCom, &gOverLapped2, &wrBytes, FALSE); } while (wrBytes!=nrBytes); } } } }
-
Danke erstmal für die Hilfe, werde micht dann melden, wenn ich fragen und Ergebnisse habe.
Bin wirklich dankbar.