COM Port (overlapped)
-
Hallo!
Ich versuche momentan eine asynchrone kommunikation über den COM port zu programmieren. Ich hab mich da mal in der MSDN schlau gemacht und folgendes ist dabei rausgekommen:
(Anmerkung: ich weiß das ist absolut schlechter stil usw. aber es war auch wirklich nur ein >TEST<)#include <stdio.h> #include <windows.h> #include <process.h> #include <conio.h> HANDLE com; void SendBuf(void *dummy){ char buf[]="at\r"; DWORD dwWritten; OVERLAPPED ov={0}; while(true){ printf("\n*sending data"); printf("--> %i",WriteFile(com,buf,strlen(buf),&dwWritten,&ov)); Sleep(1000); } } int main(void){ printf("\nOpening COM8..."); com=CreateFile("COM8",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0); if(com== INVALID_HANDLE_VALUE){ printf("failed!"); _getch(); } printf("OK!"); //////////////////// DCB dcb={0}; dcb.DCBlength=sizeof(DCB); printf("\nRetrieving Com settings..."); if(!GetCommState(com,&dcb)){ printf("failed!"); _getch(); } printf("OK!"); //////////////////////// dcb.BaudRate=CBR_256000; dcb.fDtrControl=DTR_CONTROL_ENABLE; dcb.fRtsControl=RTS_CONTROL_HANDSHAKE; dcb.StopBits=ONESTOPBIT; dcb.ByteSize=8; dcb.Parity=ODDPARITY; printf("\nTrying to set new settings..."); if(!SetCommState(com,&dcb)){ printf("failed!"); _getch(); } printf("OK!"); ////////////////////// COMMTIMEOUTS to; to.ReadIntervalTimeout=MAXDWORD; to.ReadTotalTimeoutMultiplier=0; to.ReadTotalTimeoutConstant=0; to.WriteTotalTimeoutConstant=0; to.WriteTotalTimeoutMultiplier=0; printf("\ntrying to set timeouts..."); if(!SetCommTimeouts(com,&to)){ printf("failed!"); _getch(); } printf("OK!"); ///////////////////// // events setzen printf("\ntrying to set EV_TXEMPTY|EV_RXCHAR event..."); if(!SetCommMask(com,EV_TXEMPTY|EV_RXCHAR)){ printf("failed!"); _getch(); } printf("OK!"); _beginthread(SendBuf,0,NULL); ///////////////////// //auf events warten while(true){ ////////////////// OVERLAPPED ov; memset(&ov,0,sizeof(ov)); ov.hEvent=CreateEvent(NULL,true,false,NULL); DWORD dwWait; DWORD event; GetCommMask(com,&event); // event loop while(true){ printf("\nWaitCommEvent()"); // warte auf event BOOL ret=WaitCommEvent(com,&event,&ov); if(ret==false){ if(GetLastError()==ERROR_IO_PENDING) printf("\nIO is pending..."); else{ printf("\nERROR: GetLastError()"); _getch(); } } // alles ok! printf("\nWaitForSingleObject()"); // warte und prüfe auf event dwWait=WaitForSingleObject(ov.hEvent,INFINITE); switch(dwWait){ case WAIT_OBJECT_0: if (event & EV_TXEMPTY) printf("\n*Data sent"); if(event & EV_RXCHAR){ printf("\n*Data received"); char buf[250]=""; ReadFile(com,buf,250,NULL,&ov); printf("--> %s",buf); } ResetEvent (ov.hEvent); break; }//switch }//while 2 }//while 1 //////////////// _getch(); CloseHandle(com); return 0; }es scheint auch zu funktionieren. ich werd schön brav informeirt sobald was gesendet und angekommen ist. aber ich wollte nur mal was fragen:
1. Is das generell so in ordnung mit WaitCommEvent und WaitForSingleObject usw?
2. Muss ich mit dem Overlapped struct von ReadFile und WriteFile wirklich weiter nichts machen? einfach nur angeben reicht?
3. Irgendwie gibt WaitCommEvent immer false zurück. und GetLastError() sagt immer IO_PENDING. ist das normal bei einem overlapped port?
Danke für Tips unf fürs durchlesen von dem schei* code.

Mister C
-
Kennt sich damit denn niemand bisschen aus?

-
Hmm, scheinbar ist wohl momentan keiner da, der was mit dem COM-Port gefuckelt hat oder fuckelt

Falls Du den noch nich kennst könnte aber evtl. der Artikel von Microsoft zum Thema nützlich sein:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
-
Hallo!
Versuche auch gerade eine serielle Kommunikation hinzukriegen, siehe
http://www.c-plusplus.net/forum/viewtopic-var-t-is-109690.html
Ich möchte in einem Thread auf ein Signal eines seriellen Gerätes warten. Zusätzlich brauche ich noch eine Möglichkeit den Thread zu steuern (suspend, resume und terminate). D.h. ich verwende WaitForMultipleObjects statt WaitForSingleObject.
Habe zwar auch noch nicht viel Erfahrung damit, hab es aber mit flenders Tips beinahe hinbekommen. Das einzige Problem, das ich jetzt noch habe ist, dass WaitCommEvent immer false (mit Errorcode ERROR_INVALID_PARAMETER) liefert, sobald der Thread einmal unterbrochen und dann wieder aufgeweckt wird.
Nun Deinen Fragen (wie gesagt, ich bin kein Profi auf dem Gebiet, habe mich aber in letzter Zeit etwas damit beschäftigt):
(1) Ja, ich denke so sollte man das machen.
(2) Ja, das sollte reichen.
(3) Bei asynchroner Kommunikation kehrt WaitCommEvent sofort wieder zurück. Wenn also beim Aufruf nicht sofort eines Deiner Events (z.b. EV_RXCHAR) eintrifft, dann kehrt WaitCommEvent zwar auch zurück, wartet aber weiterhin auf ein Ereignis des seriellen Ports. In diesem Fall kriegst Du false mit dem Errorcode IO_PENDING zurück. Das ist also eigentlich der Normalfall.
-
OK danke! Ihr habt mir geholfen. Wenn noch jemand anmerkungen oder tips hat soll er se rhig schreiben. Bin für alles dankbar.

@gast: wieso unterbrichst du denn den thread? Du kannst ihn doch dei ganze zeit laufen lassen und eben mit WaitForSingleObject warten...
-
@gast: wegen dem ERROR_INVALID_PARAMETER: du musst ja auch erstmal Mit mit SetCommMask die events setzen, von denen du benachrichtigt werden willst. Und mit GetCOmmMask dass dann in ein DWORD reinpacken udn den pointer an WaitCommEvent geben... so wie bei mir oben
-
Hallo Mister C,
ich habe SetCommMask verwendet, um das Event zu setzen (sieht man aber nicht in meinem Thread, da ich die Initialisierung weggelassen habe :D).
Du brauchst in Deinem Code das GetCommMask übrigens auch nicht, weil der Event-Parameter in WaitCommEvent ein "out"-Parameter ist. D.h. WaitCommEvent liefert Dir in diesem Parameter den Typ des Ereignisses das aufgetreten ist, damit Du, falls Du mit SetCommMask mehrere Ereignisse gesetzt hast, hinterher feststellen kannst, welches dieser Ereignisse aufgetreten ist.Den Thread möchte ich unterbrechen, weil ich im wesentlichen 2 Arten der Kommunikation mit der Schnittstelle habe. Erstens das Warten auf einen bestimmten Befehl im Hintergrund und zweitens einige Write/Read - Kommandos (die sozusagen zusammengehören) und ausgeführt werden, sobald der Befehl empfangen wurde. Für die Dauer dieser Write/Read Blöcke möchte ich den im Hintergrund lauschenden Thread gerne "schlafen legen", damit er mir nicht in die Quere kommt.
Mein Code sieht jetzt übrigens so aus:
Initialisierung im Hauptprogramm:
hComm = CreateFile("COM1", GENERIC_READ |GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hComm == INVALID_HANDLE_VALUE) return false; DCB dcb; if (!GetCommState(hComm, &dcb))return false; dcb.BaudRate = CBR_4800; dcb.ByteSize = 8; dcb.Parity = NOPARITY; if (!SetCommState(hComm, &dcb)) return false;Der Handle hComm wird danach an den Konstruktor des Threads übergeben. Dort wird dann auch die OVERLAPPED-Struktur initialisiert und die Events kreiert, die ich zum Unterbrechen und Terminieren des Threads brauche:
this->hComm = hComm; if ( !SetCommMask(hComm, EV_RXCHAR) ) { ShowMessage("Thread: SetCommMask failed!\nTerminating..."); Terminate(); } memset(&ov, 0, sizeof(ov)); ov.hEvent = CreateEvent(NULL, true, false, NULL); eventHandles[0] = ov.hEvent; // Serial com event eventHandles[1] = CreateEvent(NULL, true, false, NULL); // Terminate event eventHandles[2] = CreateEvent(NULL, true, false, NULL); // Suspend eventDie Execute-Methode des Threads sieht jetzt so aus:
void __fastcall SerialReader::Execute() { DWORD event; bool wceRet; DWORD wfmoRet; while (!Terminated) { wceRet = WaitCommEvent(hComm, &event, &ov); if ( wceRet || (!wceRet && GetLastError() == ERROR_IO_PENDING) ) { wfmoRet = WaitForMultipleObjects(3, eventHandles, false, INFINITE); switch (wfmoRet) { case WAIT_OBJECT_0: msg = "Received some data"; Synchronize(DisplayMessage); ResetEvent(ov.hEvent); Sleep(1000); break; case WAIT_OBJECT_0 + 1: ResetEvent(eventHandles[1]); Terminate(); break; case WAIT_OBJECT_0 + 2: msg = "Suspend SerialReader"; Synchronize(DisplayMessage); ResetEvent(eventHandles[2]); Suspend(); PurgeComm(hComm, PURGE_RXABORT); PurgeComm(hComm, PURGE_RXCLEAR); break; default: msg = "WaitForMultipleResults Error " + GetLastError(); Synchronize(DisplayMessage); Sleep(1000); break; } } else { msg = "WaitCommEventError: " + AnsiString(GetLastError()); Synchronize(DisplayMessage); Sleep(1000); } } }Um den Thread von aussen zu unterbrechen bzw. zu terminieren, hat das Thread-Objekt noch zwei public Methoden, die die entsprechenden Events setzen:
void SerialReader::TerminateSerialReader() { SetEvent(eventHandles[1]); } void SerialReader::SuspendSerialReader() { SetEvent(eventHandles[2]); }P.S.:
Bin für alle Kommentare dankbar, die helfen könnten, meine Probleme mit dem Suspendieren des Threads zu lösen ;).
-
hm... setz vllt lieber ne globale bool variable auf true oder so und des prüfst du dann auch noch vorher. und solange die eben true is macht der thread nix mitden sachen die ankommen.