Hallo zusammen, ich habe gerade eine Anwendung geschrieben, wo ich Daten über die RS232 sende und empfangen muss. Eigentlich sollte das kein Problem sein - dennoch scheint es so zu sein, als ob die RS232 ab und zu blockiert und dann keine weiteren Sende- Empfangsroutinen mehr funktionieren. Ich hab im DCB u.a. das Flag dcb.fAbortOnError auf false gesetzt. Ich benutze kein Handshake (ist auch alles im DCB abgestellt). Nun meine Vermutung: Das externe Gerät, von dem ich Daten empfangen muss, sendet ununterbrochen - d.h. ohne Aufforderung. Mein Empfangspuffer hat eine Größe von 4k (8k hab ich auch schon getestet - ohne Erfolg). Was passiert, wenn der Empfangspuffer voll ist und ich die Daten jedoch nicht abhole (OK - alle weiteren Daten gehen verloren ist aber nicht weiter schlimm!)??? Anscheinend bekomme ich danach beim Lesen per ReadFile immer ein FALSE als Returnwert zurückgeliefert. Erst nach einem Reset des externen Geräts funktioniert der Verbindungsaufbau eher - wenn ich schnell genug abfrage. In diesem Verbindungsaufbau sende ich eine bestimmte Sequenz an das externe Gerät, das daraufhin den Sendevorgang einstellt und ab dann nur noch auf Anforderung bestimmte Daten senden soll. Ich hab keine Timeouts definiert. Meine Empfangsroutine pollt die Daten (keine Overlapped Struktur). Dort habe ich selbst einen Timeout laufen - so um die 500ms. Also nochmal - wenn ich einen Verbindungsaufbau erfolgreich durchführen kann, funktioniert alles wie es soll! Wenn ich jedoch längere Zeit warte (das Gerät sendet andauernd) und dann einen Verbindungsaufbau durchführe, scheint die RS232 zu blockieren. Wer kennt einen Lösungsansatz für das Problem?? Bis bald (hoffentlich) huibuh07
1)mit einem separaten PC und zwei Schnittstellen mithoeren was die Teilnehmer denn senden. 2)Mit einem Oszilloscope einen Pin toggeln wenn das Programm in einem bestimmten zustand ist.
"... Wenn ich jedoch längere Zeit warte (das Gerät sendet andauernd) und dann einen Verbindungsaufbau durchführe, scheint die RS232 zu blockieren." Die "RS232" blockiert bestimmt nicht, das wird Dein Programm machen. Beim Pollen mußt Du sicherstellen, dass Dein Programm unter allen Umständen (Task-Wechsel, Ethernet-Timeouts, usw.) immer alle Daten abholen kann und keine Puffer überlaufen können. Das scheint hier nicht der Fall zu sein. Außerdem ist die Startbedingung für Deine Poll-Routine ungünstig: Du weißt ja nicht, was vorher mit der schon geöffneten Schnittstelle passiert ist. Eventgesteuerte Datenübernahme wäre bei einem ununterbrochenem Datenstrom die bessere Lösung. Denn wenn der Event kommt, ist auch Dein Programm dran. Blackbird
Ich hab den DEBUG-Modus von Borland C++ mal aktiviert (mit dem das Programm auch geschrieben ist). Also das Senden geht (WriteFile sendet TRUE zurück). Damit wird dem ext. Gerät die Aufforderung gesendet seinerseits das Senden einzustellen, was auch funktioniert. Gleichzeitig soll das ext. Gerät danach bestimmte Statusdaten senden. Auch dies funktioniert - bloss erkennt ReadFile 0Bytes im Buffer bzw. kommt mit FALSE zurück. Oszi sagt, dass alle Daten fein säuberlich hin und her gesendet werden. huibuh07
@Blackbird Danke für den Hinweis: Jedoch interessieren mich die dauerhaft gesendeten Daten gar nicht!!! Ich möchte nur bestimmte Infos vom Gerät erhalten. Dazu muss ich dem Gerät eine Anforderungssequenz senden. Danach unterbricht das ext. Gerät seinen Sendevorgang und reagiert nur noch auf meine Aufforderung best. Daten zu senden. Richtig ist, dass ich nicht wissen kann, wie lange der Dauerstrom von Daten schon anliegt, bevor ich mit meinem Programm die Anforderungsseqenz starte. Daher auch meine Frage, ob die RS232 blockiert, wenn der Eingangspuffer voll ist. Hinweis: Bevor ich die Anforderungssequenz sende, lösche ich den Empfangspuffer über PURGE_RXCLEAR (Funktionsname fällt mir gerade nicht mehr ein - sitzt nämlich an einem anderen Rechner) huibuh07
Nein, die Hardware blockiert nicht, wenn irgendein Puffer voll ist. Ohne Dein Programm zu kennen, kann man nur schwer sagen, wo die Verzögerung auftritt. Warum öffnest (initialisierst) Du die Schnittstelle nicht erst dann, wenn Du sie brauchst? Im Beispiel-Code weiter unten müßte nur vor der while-Schleife im Thread das Senden der Anforderungssequenz mit WriteFile erfolgen. Ist die Kommunikation fertig, so wird einfach der Thread verlassen und die Schnittstelle damit geschlossen. Satt Zurücksenden der empfangenen Bytes kommt die Verarbeitung usw. Der Start des Threads kann auch von anderswo erfolgen. In "PARAMS" können noch weitere Daten übergeben werden. Vorteil ist noch, dass der Thread die Daten eventgesteuert einliest. Die Trennung der Nutzdaten von den letzten Dauerstrom-Daten muß Deine Software erledigen. Damit die while-Schleife nicht zulange dauert, sollten die empfangenen Daten unbearbeitet gleich zum Hauptprogramm gesendet (mit PostThreadMessage) und dort verarbeitet werden.
1 | ///////////////////////////////////////////////////////////////////////
|
2 | // SerComThread - Consolen-Application
|
3 | // Daten senden und empfangen mit Thread
|
4 | // Ein Thread liest Daten an RxD ein und sendet sie zurück
|
5 | ///////////////////////////////////////////////////////////////////////
|
6 | #include <windows.h> |
7 | #include <stdio.h> |
8 | |
9 | #define PORTNAME "COM1"
|
10 | #define BAUDRATE 9600
|
11 | #define BYTESIZE 8
|
12 | #define PARITY NOPARITY
|
13 | #define STOPBITS ONESTOPBIT
|
14 | |
15 | #define COM_BUFFER_SIZE 256 // Read- und Write-Buffer-Size
|
16 | #define QEVENT "QueueEvent"
|
17 | #define TIMELIMIT 150000 // ms
|
18 | |
19 | // global struc contains parameter for threads
|
20 | typedef struct |
21 | {
|
22 | volatile BOOL ThreadStoppen; |
23 | volatile int iComPortNum; |
24 | volatile DWORD dwMonitorThreadId; |
25 | } PARAMS, *PPARAMS; |
26 | |
27 | HANDLE hCom = INVALID_HANDLE_VALUE; |
28 | HANDLE hEvent; |
29 | char COMMAND1[] = {(0x42), (0x49), (0x45), (0x42), (0x43)}; |
30 | |
31 | // prototyping
|
32 | DWORD WINAPI ComThread (LPVOID lpParam); |
33 | |
34 | |
35 | //--------------------------------------------------------------------
|
36 | // Erstellt einen Empfangs-Thread und schreibt Daten nach COMx
|
37 | //--------------------------------------------------------------------
|
38 | int main (void) |
39 | {
|
40 | PARAMS p; |
41 | DWORD dwComThreadId; |
42 | HANDLE hComThread = NULL; |
43 | DWORD dwState; |
44 | DWORD iBytesWritten; |
45 | BOOL bRet; |
46 | |
47 | |
48 | printf ("\r\nCOM %s-Port read data on RxD for %d sec.", |
49 | PORTNAME, TIMELIMIT / 1000); |
50 | printf ("\r\nrunning .... \r\n"); |
51 | |
52 | // "alten" Thread stoppen
|
53 | p.ThreadStoppen = TRUE; |
54 | CloseHandle (hComThread); // "alten" Thread stoppen |
55 | p.iComPortNum = (int)PORTNAME[3] - 0x30; |
56 | // ComThread: Starten und Parameterstruct übergeben
|
57 | p.ThreadStoppen = FALSE; |
58 | hComThread = CreateThread ( // Handle des Threads |
59 | NULL, // no security attributes |
60 | 0, // use default stack size |
61 | ComThread, // thread function |
62 | &p, // argument to thread function |
63 | 0, // use default creation flags |
64 | &dwComThreadId); // returns the thread identifier |
65 | |
66 | if (hComThread == NULL) |
67 | printf ("\r\nCreateThread (ComThread) fehlgeschlagen"); |
68 | |
69 | Sleep (1000); |
70 | |
71 | bRet = WriteFile (hCom, &COMMAND1, 5, &iBytesWritten, NULL); // Senden der Bytes |
72 | if (bRet) |
73 | { // Fehlerausgabe: |
74 | LPVOID lpMsgBuf; |
75 | FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
76 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), |
77 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); |
78 | MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: WriteFile", MB_OK | MB_ICONINFORMATION); |
79 | LocalFree (lpMsgBuf); |
80 | }
|
81 | else
|
82 | printf ("\r\n%d Bytes sent.", iBytesWritten); |
83 | |
84 | Sleep (TIMELIMIT); // der Thread und die Appl. leben nur TIMELIMIT sec.! |
85 | |
86 | printf ("\r\nReady\r\n"); |
87 | p.ThreadStoppen = TRUE; |
88 | |
89 | CloseHandle (hComThread); // ComThread beenden |
90 | |
91 | return 0; |
92 | }
|
93 | |
94 | |
95 | //--------------------------------------------------------------------
|
96 | // Thread Funktion
|
97 | // Liest ununterbrochen die Daten an RxD ein
|
98 | //--------------------------------------------------------------------
|
99 | DWORD WINAPI ComThread (LPVOID lpParam) |
100 | {
|
101 | long lTime; |
102 | volatile PPARAMS pparams; // struct-Instanz |
103 | pparams = (PPARAMS) lpParam; // Zuweisung zu lpParam |
104 | int iPortNum = pparams->iComPortNum; // ... |
105 | DWORD dwMonitorThreadId = pparams->dwMonitorThreadId; // ... |
106 | char szPort[15]; |
107 | static OVERLAPPED o; |
108 | // Maske für SetCommMask, die bestimmt, welche Ereignisse auftreten können
|
109 | DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR | |
110 | EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY; |
111 | DWORD dwEvtMask = 0; // Maske, in die WaitCommEvent aktuelle Werte schreibt |
112 | BOOL bRet; |
113 | DCB dcb; |
114 | COMMTIMEOUTS ct; |
115 | unsigned char InString[COM_BUFFER_SIZE + 1]; |
116 | DWORD dwRead = 0; |
117 | DWORD iBytesWritten; |
118 | |
119 | |
120 | CloseHandle (hCom); // "alten" COM-Port schließen |
121 | |
122 | // Com Port öffnen
|
123 | wsprintf (szPort, "COM%d", iPortNum); |
124 | hCom = CreateFile (szPort, |
125 | GENERIC_READ | GENERIC_WRITE, |
126 | 0, // exclusive access |
127 | NULL, // no security attributes |
128 | OPEN_EXISTING, |
129 | FILE_FLAG_OVERLAPPED, |
130 | NULL); |
131 | |
132 | if (hCom == INVALID_HANDLE_VALUE) |
133 | {
|
134 | LPVOID lpMsgBuf; |
135 | FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | |
136 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
137 | NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), |
138 | (LPTSTR) &lpMsgBuf, 0, NULL); |
139 | MessageBox (NULL, (LPCTSTR)lpMsgBuf, TEXT("ComThread: error: CreateFile()"), |
140 | MB_OK | MB_ICONINFORMATION); |
141 | LocalFree (lpMsgBuf); |
142 | pparams->ThreadStoppen = TRUE; |
143 | }
|
144 | else
|
145 | {
|
146 | if (!SetCommMask (hCom, dwEvtMaskIn)) |
147 | MessageBox (NULL, "SetCommMask fehlgeschlagen", "COM Port:", MB_OK | MB_ICONSTOP); |
148 | |
149 | // Create an event object for use in WaitCommEvent.
|
150 | o.hEvent = CreateEvent (NULL, // no security attributes |
151 | FALSE, // auto reset event |
152 | FALSE, // not signaled |
153 | NULL); // no name |
154 | }
|
155 | |
156 | dcb.DCBlength = sizeof(DCB); // Laenge des Blockes MUSS gesetzt sein! |
157 | GetCommState (hCom, &dcb); // COM-Einstellungen holen und aendern |
158 | dcb.BaudRate = BAUDRATE; // Baudrate |
159 | dcb.ByteSize = BYTESIZE; // Datenbits |
160 | dcb.Parity = PARITY; // Parität |
161 | dcb.StopBits = STOPBITS; // Stopbits |
162 | SetCommState (hCom, &dcb); // COM-Einstellungen speichern |
163 | |
164 | GetCommTimeouts (hCom, &ct); |
165 | // Warte-Zeit [ms] vom Beginn eines Bytes bis zum Beginn des nächsten Bytes
|
166 | ct.ReadIntervalTimeout = 1000 / BAUDRATE * (dcb.ByteSize + |
167 | (dcb.Parity == PARITY ? 0 : 1) + |
168 | (dcb.StopBits == STOPBITS ? 1 : 2)) * 2; |
169 | ct.ReadTotalTimeoutMultiplier = 0; // [ms] wird mit Read-Buffer-Size multipliziert |
170 | ct.ReadTotalTimeoutConstant = 50; // wird an ReadTotalTimeoutMultiplier angehängt |
171 | ct.WriteTotalTimeoutMultiplier = 0; |
172 | ct.WriteTotalTimeoutConstant = 0; |
173 | SetCommTimeouts (hCom, &ct); |
174 | |
175 | // Zwischenspeicher des serial-Drivers einstellen (für read und write):
|
176 | SetupComm (hCom, COM_BUFFER_SIZE, COM_BUFFER_SIZE); |
177 | printf ("\r\nComThread: %s geöffnet, warte auf Events ...", szPort); |
178 | |
179 | // Auf Events warten:
|
180 | while (!pparams->ThreadStoppen) // solange weitermachen bis TRUE |
181 | {
|
182 | WaitCommEvent (hCom, &dwEvtMask, &o); // EventMask "scharf machen" |
183 | // kommt der Event, ist auch die dwEvtMask geladen und es kann weitergehen
|
184 | if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // warten bis Event |
185 | {
|
186 | if (dwEvtMask & EV_RXCHAR) // Zeichen an RxD empfangen: |
187 | {
|
188 | bRet = ReadFile (hCom, &InString, sizeof (InString), &dwRead, NULL); |
189 | |
190 | if (!bRet) |
191 | { // Fehlerausgabe: |
192 | LPVOID lpMsgBuf; |
193 | FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
194 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), |
195 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
196 | (LPTSTR) &lpMsgBuf, 0, NULL); |
197 | MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: ReadFile", |
198 | MB_OK | MB_ICONINFORMATION); |
199 | LocalFree (lpMsgBuf); |
200 | }
|
201 | else
|
202 | { // Ausgabe (oder Verarbeitung) der empfangenen Bytes: |
203 | InString[dwRead] = '\0'; // in "zero-ended"-String verwandeln |
204 | printf (TEXT("\r\n\tRxD (%d Byte(s)): %s"), dwRead, InString); |
205 | WriteFile (hCom, &InString, dwRead, &iBytesWritten, NULL); // Senden der Bytes |
206 | }
|
207 | }
|
208 | |
209 | if (dwEvtMask & EV_ERR) |
210 | {
|
211 | MessageBox (NULL, "Error empfangen", "Error: ReadFile", MB_OK); |
212 | break; // Schleifen-Abbruch |
213 | }
|
214 | } // end of: if (WAIT_OBJECT_0 == ... |
215 | } // end of: while (... |
216 | |
217 | CloseHandle (o.hEvent); |
218 | CloseHandle (hCom); // COM-Port schließen |
219 | |
220 | printf ("\r\nComThread: %s geschlossen", szPort); |
221 | |
222 | return (0); |
223 | }
|
Blackbird
@Blackbird Danke erst mal für dein Programm. Habe es noch nicht getestet. Im Gegenzug hab ich mal Ausschnitte von meinem Code gepostet. Variablendeklarationen sind zumeist weggelassen - bzw. in der Header-Datei definiert. Wie du siehst, schalte ich die Schnittstelle erst frei, nachdem ich auf einen Button gedrückt habe. Bis zu dieser Zeit laufen aber schon Daten vom ext. Gerät auf (werden aber vom Programm nicht berücksichtigt, da Schnittstelle noch nicht aktiv ist). Nach dem erfolgreichen Aufruf von 'GetItemStatus' hört das ext. Gerät auf zu senden und antwortet nur noch auf meine Anforderungen! So sollte es sein - ist es auch - wenn es denn mal läuft. Aber - wie gesagt - in den meisten Fällen geht's eben nicht. Was auch interessant ist, ist die Tatsache, dass beim Setzen eines Breakpoint im Debugger an der Position in der Funktion 'GetItemStatus' nach CommRead() die Wahrscheinlichkeit eines funktionierenden Aufbaus sehr viel höher ist. 2 von 3 Versuchen klappen dann. Ist das noch ein Timingproblem?? Also die Daten vom Gerät werden auf jeden Fall auch zurückgesendet. huibuh07
Kann ich jetzt nichts erkennen, müßte mich da selber mit Debugger und return codes durchhangeln. Versuche mal die Read-Puffergröße am Anfang auf 1 zu setzen (Du willst ja nur senden) und setze die Timeouts beim Empfang höher (siehe meinen Code). XON/XOFF sollte aber ausgeschaltet sein, oder verwendest Du dieses Protokoll? Dann wartet die Schnittstelle auf diese Daten im Empfang. Blackbird
@Blackbird War 2 Tage nicht Online - Sorry Habe das Problem aber in der Zwischenzeit gefunden - kann mir aber nicht erklären, wieso das was damit zu tun hat. Also - als ich mir Deinen Code mal angesehen habe ist mir aufgefallen, dass du vor dem WriteFile eine SLEEP Anweisung eingefügt hast. Nachdem ich einfach mal eine SLEEP Anweisung bei mir implementiert hatte Sleep(100) scheint wohl zu reichen, hat der Verbindungsaufbau sofort geklappt. 95-98% aller Versuche ein Anforderungstelegramm ans ext. Gerät zu senden, haben sofort funktioniert, der Rest beim 2. oder 3. Versuch. Als ich den Sleep testweise wieder rausgenommen habe - siehe da - gleiches Problem - nichts geht mehr. Kann mir aber nicht erklären, wieso. Na ja auf jeden Fall scheint damit das Problem gelöst (hat mich 3 Tage der Verzweifelung gekostet). Danke nochmal für deine Unterstützung huibuh07
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.