www.mikrocontroller.net

Forum: PC-Programmierung RS232 blockiert!?!


Autor: TK (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Zacc (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Blackbird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: TK (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: TK (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Blackbird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
///////////////////////////////////////////////////////////////////////
// SerComThread          -          Consolen-Application
//     Daten senden und empfangen mit Thread
// Ein Thread liest Daten an RxD ein und sendet sie zurück
///////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>

#define PORTNAME  "COM1" 
#define BAUDRATE  9600 
#define BYTESIZE  8 
#define PARITY    NOPARITY 
#define STOPBITS  ONESTOPBIT 

#define COM_BUFFER_SIZE 256       // Read- und Write-Buffer-Size
#define QEVENT     "QueueEvent"
#define TIMELIMIT 150000        // ms

// global struc contains parameter for threads
typedef struct
{
  volatile BOOL ThreadStoppen;
  volatile int iComPortNum;
  volatile DWORD  dwMonitorThreadId;
} PARAMS, *PPARAMS;

HANDLE hCom = INVALID_HANDLE_VALUE;
HANDLE hEvent; 
char COMMAND1[] = {(0x42), (0x49), (0x45), (0x42), (0x43)};

//  prototyping
DWORD WINAPI ComThread (LPVOID lpParam);


//--------------------------------------------------------------------
// Erstellt einen Empfangs-Thread und schreibt Daten nach COMx
//--------------------------------------------------------------------
int main (void)
{
  PARAMS p;
  DWORD  dwComThreadId;
  HANDLE hComThread = NULL;
  DWORD  dwState;
  DWORD  iBytesWritten;
  BOOL   bRet;


  printf ("\r\nCOM %s-Port read data on RxD for %d sec.", 
          PORTNAME, TIMELIMIT / 1000);
  printf ("\r\nrunning .... \r\n");

  // "alten" Thread stoppen  
  p.ThreadStoppen = TRUE;
  CloseHandle (hComThread); // "alten" Thread stoppen
  p.iComPortNum = (int)PORTNAME[3] - 0x30;
  // ComThread: Starten und Parameterstruct übergeben
  p.ThreadStoppen = FALSE;
  hComThread = CreateThread (    // Handle des Threads
    NULL,                        // no security attributes 
    0,                           // use default stack size  
    ComThread,                   // thread function 
    &p,                          // argument to thread function 
    0,                           // use default creation flags 
    &dwComThreadId);             // returns the thread identifier 

  if (hComThread == NULL) 
    printf ("\r\nCreateThread (ComThread) fehlgeschlagen");

  Sleep (1000);

  bRet = WriteFile (hCom, &COMMAND1, 5, &iBytesWritten, NULL); // Senden der Bytes
  if (bRet)
  { // Fehlerausgabe:
    LPVOID lpMsgBuf;
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
                    FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
    MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: WriteFile", MB_OK | MB_ICONINFORMATION);
    LocalFree (lpMsgBuf);
  }
  else
    printf ("\r\n%d Bytes sent.", iBytesWritten);

  Sleep (TIMELIMIT);  // der Thread und die Appl. leben nur TIMELIMIT sec.!
  
  printf ("\r\nReady\r\n");
  p.ThreadStoppen = TRUE;

  CloseHandle (hComThread);    // ComThread beenden
  
  return 0;
}


//--------------------------------------------------------------------
// Thread Funktion
// Liest ununterbrochen die Daten an RxD ein
//--------------------------------------------------------------------
DWORD WINAPI ComThread (LPVOID lpParam) 
{
  long lTime;
  volatile PPARAMS pparams;                     // struct-Instanz
  pparams = (PPARAMS) lpParam;                  // Zuweisung zu lpParam
  int iPortNum = pparams->iComPortNum;          // ...
  DWORD dwMonitorThreadId = pparams->dwMonitorThreadId; // ...
  char szPort[15];
  static OVERLAPPED o;
  // Maske für SetCommMask, die bestimmt, welche Ereignisse auftreten können
  DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR | 
                      EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY;
  DWORD dwEvtMask = 0;  // Maske, in die WaitCommEvent aktuelle Werte schreibt
  BOOL bRet;
  DCB  dcb;    
  COMMTIMEOUTS  ct;
  unsigned char InString[COM_BUFFER_SIZE + 1];
  DWORD         dwRead    = 0;
  DWORD  iBytesWritten;


  CloseHandle (hCom);       // "alten" COM-Port schließen

  // Com Port öffnen
  wsprintf (szPort, "COM%d", iPortNum);
  hCom = CreateFile (szPort, 
    GENERIC_READ | GENERIC_WRITE,
    0,    // exclusive access 
    NULL, // no security attributes 
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL);

  if (hCom == INVALID_HANDLE_VALUE)
  {
    LPVOID lpMsgBuf;
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR) &lpMsgBuf, 0, NULL);
    MessageBox (NULL, (LPCTSTR)lpMsgBuf, TEXT("ComThread: error: CreateFile()"), 
      MB_OK | MB_ICONINFORMATION);
    LocalFree (lpMsgBuf);
    pparams->ThreadStoppen = TRUE;
  }
  else
  {
    if (!SetCommMask (hCom, dwEvtMaskIn)) 
      MessageBox (NULL, "SetCommMask fehlgeschlagen", "COM Port:", MB_OK | MB_ICONSTOP);

    // Create an event object for use in WaitCommEvent. 
    o.hEvent = CreateEvent (NULL,   // no security attributes 
        FALSE,  // auto reset event 
        FALSE,  // not signaled 
        NULL);  // no name 
  }

  dcb.DCBlength = sizeof(DCB);  // Laenge des Blockes MUSS gesetzt sein!
  GetCommState (hCom, &dcb);    // COM-Einstellungen holen und aendern
  dcb.BaudRate  = BAUDRATE;     // Baudrate
  dcb.ByteSize  = BYTESIZE;     // Datenbits
  dcb.Parity    = PARITY;       // Parität
  dcb.StopBits  = STOPBITS;     // Stopbits
  SetCommState (hCom, &dcb);    // COM-Einstellungen speichern
  
  GetCommTimeouts (hCom, &ct);
  // Warte-Zeit [ms] vom Beginn eines Bytes bis zum Beginn des nächsten Bytes 
  ct.ReadIntervalTimeout         = 1000 / BAUDRATE * (dcb.ByteSize + 
                                                     (dcb.Parity == PARITY ? 0 : 1) + 
                                                     (dcb.StopBits == STOPBITS ? 1 : 2)) * 2;
  ct.ReadTotalTimeoutMultiplier  = 0;  // [ms] wird mit Read-Buffer-Size multipliziert
  ct.ReadTotalTimeoutConstant    = 50; // wird an ReadTotalTimeoutMultiplier angehängt
  ct.WriteTotalTimeoutMultiplier = 0;
  ct.WriteTotalTimeoutConstant   = 0;
  SetCommTimeouts (hCom, &ct);

  // Zwischenspeicher des serial-Drivers einstellen (für read und write):
  SetupComm (hCom, COM_BUFFER_SIZE, COM_BUFFER_SIZE);
  printf ("\r\nComThread: %s geöffnet, warte auf Events ...", szPort);

  // Auf Events warten:
  while (!pparams->ThreadStoppen) // solange weitermachen bis TRUE
  {
    WaitCommEvent (hCom, &dwEvtMask, &o);  // EventMask "scharf machen"
    // kommt der Event, ist auch die dwEvtMask geladen und es kann weitergehen
    if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // warten bis Event
    {
      if (dwEvtMask & EV_RXCHAR) // Zeichen an RxD empfangen:
      {
        bRet = ReadFile (hCom, &InString, sizeof (InString), &dwRead, NULL);

        if (!bRet)
        { // Fehlerausgabe:
          LPVOID lpMsgBuf;
          FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
                          FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                          (LPTSTR) &lpMsgBuf, 0, NULL);
          MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: ReadFile", 
                      MB_OK | MB_ICONINFORMATION);
          LocalFree (lpMsgBuf);
        }
        else
        { // Ausgabe (oder Verarbeitung) der empfangenen Bytes:
          InString[dwRead] = '\0'; // in "zero-ended"-String verwandeln
          printf (TEXT("\r\n\tRxD (%d Byte(s)): %s"), dwRead, InString);
          WriteFile (hCom, &InString, dwRead, &iBytesWritten, NULL); // Senden der Bytes
        }
      }

      if (dwEvtMask & EV_ERR) 
      {
        MessageBox (NULL, "Error empfangen", "Error: ReadFile", MB_OK);
        break; // Schleifen-Abbruch
      }
    }    // end of: if (WAIT_OBJECT_0 == ...
  }      // end of: while (...

  CloseHandle (o.hEvent);
  CloseHandle (hCom);          // COM-Port schließen

  printf ("\r\nComThread: %s geschlossen", szPort);

  return (0);
}

Blackbird

Autor: TK (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Blackbird (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: TK (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.