Forum: PC-Programmierung RS232 blockiert!?!


von TK (Gast)


Lesenswert?

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

von Zacc (Gast)


Lesenswert?

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.

von Blackbird (Gast)


Lesenswert?

"... 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

von TK (Gast)


Lesenswert?

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

von TK (Gast)


Lesenswert?

@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

von Blackbird (Gast)


Lesenswert?

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

von TK (Gast)


Angehängte Dateien:

Lesenswert?

@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

von Blackbird (Gast)


Lesenswert?

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

von TK (Gast)


Lesenswert?

@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
Noch kein Account? Hier anmelden.