COM Schnittstelle



  • Hallo,
    ich versuche ein über RS-232 / COM1 angeschlossenes Gerät anzusprechen.
    Muss ein Kommando senden und die binären Daten die zurück kommen speichern.

    Habe mir von folgender homepage

    http://www.fh-luebeck.de/content/01_33_02_10_07/6/0.html

    ein Beispiel geholt, aber ..

    Die Dateien ab Version 5 sind nicht mehr auf dem C++Builder 4 und niedriger compilierbar.

    Natürlich habe ich diesen C++Builder 4 ... Habe es probiert und bekomme immer den Fehler,
    das der HANDLE nicht OK ist?!?

    HANDLE OpenComm(char* Port) { // Öffnet den Port und gibt ein Handle zurück
    
            HANDLE hCom = CreateFile(Port,// z.B. "COM1",
            GENERIC_READ|GENERIC_WRITE,//zum Senden und Empfangen
            0, // Für comm devices exclusive-access notwendig
            0, // Keine security attributes
            OPEN_EXISTING, // Für comm devices notwendig
            0, // Kein overlapped I/O
            0); // Für comm devices muss hTemplate NULL sein
    
         if (hCom == INVALID_HANDLE_VALUE)
           ShowLastError("CreateFile: "+AnsiString(Port));
         return hCom;
    }
    

    Kann mir da jemand helfen?

    Besten Dank!



  • was sagt denn GetLastError? Anzeigbar zum Beispiel so:

    void ShowLastError()
    {
      LPVOID lpMsgBuf;
    
      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                    FORMAT_MESSAGE_FROM_SYSTEM,
                    0,
                    GetLastError(),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    (LPTSTR) &lpMsgBuf,
                    0,
                    0);
    
      ShowMessage((LPCTSTR) lpMsgBuf);
    
      // Speicher freigeben
      LocalFree(lpMsgBuf);
    }
    


  • Hallo,

    auch ich habe Probleme mit dem COM Port...
    Obwohl ich nun schon alle Beiträge dazu gelesen habe, besteht mein Problem noch immer.

    HyperTerminal reicht mir nicht 😉 , somit soll mein Programm folgende können:
    - lesen an einem COM Port und ausgeben der Daten in ein Memo (Die Daten werden mit einem Dollarzeichen 0x24 eingeleitet und enden mit einem Carriage Return 0x0d)
    dazu will ich folgende Funktionalitäten implementieren:
    - setzen des COM Ports 1..9
    - setzen der Baudrate 1200...57600
    - setzen des Parityflag
    - setzen der Stopbits

    Nach der M$ Platform Reference bzw. M$ Visualstudio Express Hilfe und dem BCB Buch von Herrn Kaiser habe ich Createfile verwendet. (die Reference war nur Nachschlagewerk, ich nutze BCB!)

    Problem ist, ich kann das Handle nicht schließen. Dies versuche ich in meiner Funktion Receive(). BCB 6 gibt mir den Fehler "unresolved external" zum CloseHandle(). Ohne das CloseHandle() kann ich den COM Port ja nur ein mal ansprechen (sicher wird er Programmende autom. geschlossen, aber ich will nicht für jede Einstellung das Programm neu starten).
    Ein weiterer Fehler ist, dass die zu empfangenden Zeichen in unregelmäßigen Zeitabständen in das Memo geschrieben werden, so als gäbe es Probleme mit dem Puffer.
    Die Funktion ReceiveData() versuche ich mit einem Timer zu steuern.
    Den Timer1 habe ich schon versucht mit Werten zw. 10ms und 1000ms zu betrieben. Das es an meiner Abfrage der o.g. Steuerzeichen liegt kann ich ausschließen. Auch wenn ich sie weg lasse bleibt das Ergebnis im Grunde gleich, nur die Anzeige ändert sich (es wird nicht der gewünschte Datensatz ausgegeben, sondern Teile von ihm, wahlos zerstückelt). Die Länge der Daten zw. den Steuerzeichen liegt immer unter 100 Byte.
    Eigentlich möchte ich fortwärend Daten an der RS-232 lesen. Also immer wenn ein neues Paket im Eingangspuffer landet würde ich es gern holen. Dies mit einem Timer zu lösen gefällt mir dabei nicht wirklich gut.

    Im folgenden ist mein Quellcode dagestellt, u.U. bin ich schon erblindet und jem. von euch sieht meinen Fehler.

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "UnitRS232win.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    
    TFormRS232win *FormRS232win;
    HANDLE hComReceive;
    int len;
    char *reccom;
    
    //---------------------------------------------------------------------------
    __fastcall TFormRS232win::TFormRS232win(TComponent* Owner)
      : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    void __fastcall TFormRS232win::FormCreate(TObject *Sender)
    {
    reccom="COM1";
    }
    
    void ShowLastError(AnsiString where)
    {
    MessageBox(NULL,SysErrorMessage(GetLastError()).c_str(),
    where.c_str(),MB_OK|MB_ICONERROR);
    }
    
    HANDLE OpenComm(char* Port)
    { // Öffnet den Port und gibt ein Handle zurück
      HANDLE hCom;
      hCom = CreateFile(
        Port,                          // z.B. "COM1",
        GENERIC_READ|GENERIC_WRITE,    //zum Senden und Empfangen
        0,                             // Für comm devices exclusive-access notwendig
        NULL,                          // Keine security attributes
        OPEN_EXISTING,                 // Für comm devices notwendig
        0,                             // Kein overlapped I/O
        NULL                           // Für comm devices muss hTemplate NULL sein
        );
    
    if (hCom == INVALID_HANDLE_VALUE)
           ShowLastError("CreateFile: "+AnsiString(Port));
    return hCom;
    }
    
    DWORD ReceiveData(char* Data, int n)
    {
    DWORD NumberOfBytesRead; // Anzahl der gelesenen Bytes
       bool b=ReadFile(hComReceive, // handle des Com-Ports
         Data, // Adresse des Datenpuffers
         n, // Anzahl der zu lesenden Bytes
         &NumberOfBytesRead, // Adresse übergeben
         0); // kein overlapped I/O
       if (!b) ShowLastError("ReadFile");
    return NumberOfBytesRead;
    }
    
    void SetDCB(HANDLE hCom)
    {
      int port = FormRS232win->CB_Port->ItemIndex;
      switch (port)
      {
        case 0 :reccom = "COM1"; break;
        case 1 :reccom = "COM2"; break;
        case 2 :reccom = "COM3"; break;
        case 3 :reccom = "COM4"; break;
        case 4 :reccom = "COM5"; break;
        case 5 :reccom = "COM6"; break;
        case 6 :reccom = "COM7"; break;
        case 7 :reccom = "COM8"; break;
        case 8 :reccom = "COM9"; break;
      }
    
      DCB dcb; // Device Control Block
      BOOL fSuccess = GetCommState(hCom, &dcb); // DCB lesen
       if (!fSuccess) ShowLastError("GetCommState");
         dcb.BaudRate = StrToInt(FormRS232win->CB_Baud->Text);    //Baudrate
         dcb.ByteSize = StrToInt(FormRS232win->CB_Data->Text); //Datenbits
    
         int parity = FormRS232win->CB_Parity->ItemIndex;
         switch (parity)
         {
           case 0 :dcb.Parity = NOPARITY ;break;
           case 1 :dcb.Parity = EVENPARITY ;break;
           case 2 :dcb.Parity = MARKPARITY ;break;
           case 3 :dcb.Parity = ODDPARITY ;break;
         }
    
         int stop = FormRS232win->CB_Stop->ItemIndex;
         switch (stop)
         {
           case 0 :dcb.StopBits = ONESTOPBIT; break;
           case 1 :dcb.StopBits = TWOSTOPBITS; break;
         }
      //setzen der datenpuffer
      SetupComm(hCom, // Handle der Schnittstelle
      2048,           // Größe des Eingabepuffers
      2048);          // Größe des Ausgabepuffers
    
    // SetCommState konfiguriert die serielle Schnittstelle
      fSuccess = SetCommState(hCom, &dcb);
      if (!fSuccess) ShowLastError("SetCommState");
    }
    
    void SetReadTimeouts(HANDLE hCom)
    {
    COMMTIMEOUTS t;
    // Alle Wert in Millisekunden
    // Werte für ReadFile:
       t.ReadIntervalTimeout=100; // Zeit zwischen zwei Zeichen
       t.ReadTotalTimeoutMultiplier=0; // pro Zeichen
       t.ReadTotalTimeoutConstant=1;
         // Werte für WriteFile: wenn beide 0 sind, kein Timeout
         // beim Schreiben
       t.WriteTotalTimeoutMultiplier=0;
       t.WriteTotalTimeoutConstant=0;
     if (!SetCommTimeouts(hCom,&t)) ShowLastError("SetCommTimeouts");
    }
    
    void __fastcall TFormRS232win::ConnectRS232winClick(TObject *Sender)
    {
      Receive();
      if (Timer1->Enabled)
      {
       ConnectRS232win->Caption = "Disconnect";
       ConnectRS232win->Hint = "Close COM port";
      }
      else
      {
       ConnectRS232win->Caption = "Connect";
       ConnectRS232win->Hint = "Open COM port";
      }
    }
    
    void __fastcall TFormRS232win::Receive()
    { //open COM and start timer1
    
       if (Timer1->Enabled)
       {
         CloseHandle(hComReceive); //Schließen des Handles <<<--- FEHLER (unresolved external
         if (!CloseHandle(hComReceive)) ShowLastError(AnsiString(reccom));
         Timer1->Enabled=false;
        }
       else
       {
         hComReceive =OpenComm(reccom);
         SetReadTimeouts(hComReceive);
         SetDCB(hComReceive);
         Timer1->Enabled=true;
       }
    }
    
    void __fastcall TFormRS232win::Timer1Timer(TObject *Sender)
    {
    const int BufSize = 2048;
    int i, len;
    bool enable = false;
    char Buffer[BufSize]; // Puffer für die empfangenen Daten
    
      DWORD NumberOfBytesRead=ReceiveData(Buffer,BufSize);
      if (NumberOfBytesRead > 20)
      {
        AnsiString s;
        for (DWORD i = 0; i < NumberOfBytesRead; i++)
        {
          if (Buffer[i] == 0x24) //start with $
            enable = true;
          if (Buffer[i] == 0x0d) //end with CR
            enable = false;
    
          if (enable == true)
            s=s+Buffer[i];
    
          if (enable == false && s != "")      //auf Enter testen
          {
            FormMain->Memo_Text->Lines->Add(s);
            s="";
          }
        }
      }
      /*else
        FormMain->Memo_Text->Lines->Add("error"); */
    }
    

    Danke für eure Hilfe.

    Wenn gar nichts mehr hilft versuche ich mal die Einbindung von Ramon de Klein's Serial-Klasse (http://www.codeproject.com/system/serial.asp).

    MfG M



  • 1. bevor du weitermachst, erstelle dir eine eigene klasse zum handle der schnittstelle
    2. dort fügst du alle jetzigen globalen funktionen wie (OpenComm, Receive, usw. als Member ein)
    3. die globalen variablen sind dann die membervariablen deiner klasse.
    4. lass den timer weg.



  • Hallo,

    ich habs nicht so mit Klassen...
    Vor so zwei Jahren hab ich da noch was gekonnt und mich später immer schön drum gedrückt.
    Also ich habe jetzt mal was anderes ausprobiert und rufe dabei die Empfangsprozedur in einer do-while Schleife mit Events auf (hier im Forum abgeschaut).

    do // in Endlos-Schleife auf Empfangssignale warten:
      {
        WaitCommEvent (hCom, &dwEvtMask, &o); // Event mit Empfangssignalen verknüpfen
        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:
            ShowLastError("Error: ReadFile"+AnsiString(Port));
            }
            else
            { // Ausgabe (oder Verarbeitung) der empfangenen Bytes:
              for (DWORD i=0;i<dwRead;i++)
              {
                if (InString[i] == 0x24) //start with $
                  enable = true;
                if (InString[i] == 0x0d) //end with CR
                  enable = false;
    
                if (enable == true)
                  s = s + InString[i];
    
                if (enable == false && s != "")      //fill Memo
                {
                  FormMain->Memo_Text->Lines->Add(s);
                  s="";
                }
              }
            }
          }
          if (dwEvtMask & EV_ERR)
          {
            MessageBox (NULL, "Error empfangen", "Error: ReadFile", MB_OK);
            break; // Schleifen-Abbruch
          }
        }
      }
      while (1);
    

    Prinzipiell funktioniert das jetzt prima und ich erhalte alle Daten ohne Fehler, allerdings würde ich das Programm gern weiter laufen lassen... Die Schleife ist endlos und stoppt den Rest des Programms.
    Sie im Hintergrund laufen zu lassen geht ja nicht.
    Irgend wie habe ich mich jetzt verrant und seh momentan keine ordentliche Lösung des Problems. 😞

    MfG M


  • Mod

    Hallo

    zB versuchs mit "Thread"

    Mfg
    Klaus


Anmelden zum Antworten