Probleme bei Messwertabfrage über RS232 und Anzeige auf Form



  • ich möchte Messwerte von versch. Messgeräten auf meiner Form anzeigen lassen. Dabei bekomm ich immer wieder Exceptions.

    Für die Verbindung über RS232 hab ich eine Klasse comclass geschrieben:

    class comclass
    {
    private:
       HANDLE hCom;
    public:
       // Oeffnen der seriellen Schnittstelle
       bool OpenComm(char* port);
       // setzen der Einstellungen
       void SetDCB(int baud);
       // setzen der Timeout-Zeiten
       void SetReadTimeouts(int intervalTO, int readSingleTO, int writeSingleTO);
       // Daten senden
       int SendData(char Data[],int n);
       // Daten empfangen
       DWORD ReceiveData(char* Data,int n);
       // schließen der seriellen Schnittstelle
       void CloseComm(void);
    
    // Öffnet den Port und gibt ein Handle zurück
    bool comclass::OpenComm(char* port){
       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) return false;
    	else return true;
    }
    // setzen der Einstellungen
    void comclass::SetDCB(int baud){
       DCB dcb; // Device Control Block
       GetCommState(hCom, &dcb); // DCB lesen
    
       dcb.BaudRate = baud; // Baudrate z.B.:9600
       dcb.ByteSize = 8; // 8 Datenbits
       dcb.Parity = NOPARITY; // keine Parity
       dcb.StopBits = ONESTOPBIT; // 1 Stopbit
       dcb.fDtrControl = 1;
       // SetCommState konfiguriert die serielle Schnittstelle
       SetCommState(hCom, &dcb);
    }
    // setzen der Timeout-Zeiten
    void comclass::SetReadTimeouts(int intervalTO, int readSingleTO, int writeSingleTO)
    {
       COMMTIMEOUTS t;
       // Alle Wert in Millisekunden
       // Werte für ReadFile:
       t.ReadIntervalTimeout=intervalTO; // Zeit zwischen zwei Zeichen
       t.ReadTotalTimeoutMultiplier=readSingleTO; // pro Zeichen
       t.ReadTotalTimeoutConstant=readSingleTO;
       // Werte für WriteFile: wenn beide 0 sind, kein Timeout
       // beim Schreiben
       t.WriteTotalTimeoutMultiplier=writeSingleTO;
       t.WriteTotalTimeoutConstant=writeSingleTO;
       SetCommTimeouts(hCom,&t);
    }
    // Daten senden
    int comclass::SendData(char Data[],int n)
    {
       DWORD NumberOfBytesWritten; //Anzahl der gesendeten Bytes
       WriteFile(hCom, // handle des Com-Ports
    	Data, // Adresse der Daten
    	n, // Anzahl der zu sendenden Bytes
    	&NumberOfBytesWritten, // Adresse übergeben
    	0); // kein overlapped I/O
       return NumberOfBytesWritten;
    }
    // Daten empfangen
    DWORD comclass::ReceiveData(char* Data,int n)
    {
       DWORD NumberOfBytesRead; // Anzahl der gelesenen Bytes
       ReadFile(hCom, // handle des Com-Ports
    	Data, // Adresse des Datenpuffers
    	n, // Anzahl der zu lesenden Bytes
    	&NumberOfBytesRead, // Adresse übergeben
    	0); // kein overlapped I/O
       return NumberOfBytesRead;
    }
    // schließen der seriellen Schnittstelle
    void comclass::CloseComm(void) {CloseHandle(hCom);}
    

    dann gibt es eine Klasse für jedes Messgerät, hier zB c_Edu32fp:

    class c_Edu32fp
    {
    private:
        bool m_connect;
        comclass m_port;
    public:
        c_Edu32fp();
        ~c_Edu32fp();
        void PortOeffnen(AnsiString p_port);
        double ErfasseVolumen();
        bool GetConnect();
    };
    
    c_Edu32fp::c_Edu32fp() : m_connect(false) {}
    //---------------------------------------------------------------------------
    c_Edu32fp::~c_Edu32fp() {if(m_connect) m_port.CloseComm();}
    //---------------------------------------------------------------------------
    void c_Edu32fp::PortOeffnen(AnsiString p_port)
    {
    	m_connect = m_port.OpenComm(p_port.c_str());
       if(m_connect)
       {
          m_port.SetDCB(9600);
          m_port.SetReadTimeouts(100, 10, 10);
       }
       else
       {
       	ShowMessage("Gaszähler EDU32FP nicht verbunden!");
          m_port.CloseComm();
       }
    }
    //---------------------------------------------------------------------------
    double c_Edu32fp::ErfasseVolumen()
    {
    	if (m_connect)
       {
       	char rec[50] = {0};
       	unsigned char send[] = {0x16};
       	while(m_port.ReceiveData(rec,1)>0); //Buffer leeren
       	m_port.SendData(send,1);
       	m_port.ReceiveData(rec,50);
          return StrToFloat(AnsiString(rec).SubString(4,9).Trim());
       }
       else return 0;
    }
    //---------------------------------------------------------------------------
    bool c_Edu32fp::GetConnect()
    {
       return m_connect;
    }
    

    zur Anzeige auf meiner Form verwende ich einen Thread (ThreadMesswerteAnzeigen):

    class ThreadMesswerteAnzeigen : public TThread
    {
    private:
        double prozesszeit;
        LONGLONG frequenz;
        LONGLONG alt;
        LONGLONG neu;
    protected:
        void __fastcall Execute();
    public:
        __fastcall ThreadMesswerteAnzeigen(bool CreateSuspended);
        void __fastcall VCLAccess();
    };
    
    __fastcall ThreadMesswerteAnzeigen::ThreadMesswerteAnzeigen(bool CreateSuspended)
    	: TThread(CreateSuspended)
    {
       //FreeOnTerminate = true;
    	if (QueryPerformanceFrequency((LARGE_INTEGER*)&frequenz) == 0)
       	ShowMessage("kein High-Resolution-Performance-Counter gefunden");
       Priority = tpHighest;
    }
    //---------------------------------------------------------------------------
    void __fastcall ThreadMesswerteAnzeigen::Execute()
    {
       QueryPerformanceCounter((LARGE_INTEGER*)&alt;
       while(!Terminated)
       {
          QueryPerformanceCounter((LARGE_INTEGER*)&neu);
          prozesszeit = (neu-alt)/(double)frequenz;
          if (prozesszeit > 0)
          {
             Synchronize(VCLAccess);
          }
          Sleep(1);
       }
    }
    //---------------------------------------------------------------------------
    void __fastcall ThreadMesswerteAnzeigen::VCLAccess()
    {
       if (frmHO->edu.GetConnect())
       {
           frmHO->txtAnzeigeVolumen->Text = FloatToStr(frmHO->edu.ErfasseVolumen());
       }
    }
    

    ich hoffe man steigt da durch. Was meint ihr dazu?
    Problematisch ist auch das ändern des Ports während der Laufzeit (das aufrufen der Funktion PortOeffnen(AnsiString p_port). Das funktioniert leider nicht. Ist die Verbindung einmal getrennt kann man nicht wieder verbinden und muss erst das Programm neu starten.

    Das Programm läuft auch total abgehackt. Macht der Thread überhaupt Sinn oder könnt ich nicht einfach einen einfachen Timer nehmen? Die Messwerte sollen nur alle 1 sek aktualisiert werden.



  • Die Funktion SetDCB ist schon mal verdächtig. Die DCB Variable wird einfach angelegt, aber nicht alle Elemente initialisiert. Setze alle Einträge oder verwende BuildComDCB.

    Das das Programm ruckelt ist klar. Du machst die ganze Verarbeitung der Schnittstellen ja im Thread der Benutzeroberfläche.

    Dein Thread guckt nur wie spät es ist und lässt dann wieder die Oberfläche arbeiten.

    Richtig wäre es, wenn jedes Messgerät von einem eigenen Thread abgefragt würde. Dort werden die Daten ständig gelesen und in durch Critical Section geschützte Variablen gespeichert. Die Oberfläche holt sich über einen Timer die Daten zu Anzeigen aus den geschützten Variablen.

    QueryPerformanceCounter kann man nehmen um die Dauer von Funktionsaufrufen zu messen, so wie du es benutzt ist es ziemlich sinnfrei. Da reichen Sleep oder GetTickCount aus.

    Und am wichtigsten: Synchronize hat in Workerthreads mit zeitkritischen Aufgaben nichts verloren.

    Zu dem ganzen Rest sag ich lieber nicht viel, die ganzen char* sind eine Einladung für Speicherlecks und sonstige Fehler. Verwende lieber AnsiString oder std::string.

    Oder erfreue dich halt am Ruckeln und den Exceptions. 🤡



  • zu SetDCB:
    Leider kenn ich mich mit den RS232 nicht so gut aus. Ich habe im Buch "C++ mit dem Borland Builder 2007" bei den Lösungen noch was gefunden und hab das in meinem Code geändert:

    void comclass::ShowLastError(AnsiString where)
    {
    	MessageBox(NULL,SysErrorMessage(GetLastError()).c_str(),
    		where.c_str(), MB_OK|MB_ICONERROR);
    }
    
    void comclass::SetDCB(int baud)
    {
       DCB dcb; // Device Control Block
       BOOL fSuccess = GetCommState(hCom, &dcb); // DCB lesen
       if (!fSuccess) ShowLastError("GetCommState");
       dcb.BaudRate    = baud; // Baudrate z.B.:9600
       dcb.ByteSize    = 8; // 8 Datenbits
       dcb.Parity      = NOPARITY; // keine Parity
       dcb.StopBits    = ONESTOPBIT; // 1 Stopbit
    
       bool NoFlowControl  = false;
       bool HardwareFlowControl = false;
       bool SoftwareFlowControl = true;
    
       if (NoFlowControl)
       {
          // kein Hardware Flowcontrol:
          dcb.fOutxCtsFlow = false;
          dcb.fOutxDsrFlow = false;
          // kein Software Flowcontrol:
          dcb.fInX  = false;  // für Empfänger
          dcb.fOutX = false;  // für Sender
    
          dcb.fDsrSensitivity = false;
       }
    	else
      	{
          if (HardwareFlowControl)
          {
             dcb.fOutxCtsFlow = true;
             dcb.fOutxDsrFlow = true;
          }
          if (SoftwareFlowControl)
          {
             dcb.fInX  = true;  // für Empfänger
             dcb.fOutX = true; // für Sender
          }
       }
       fSuccess = SetCommState(hCom, &dcb);
       if (!fSuccess) ShowLastError("SetCommState");
    }
    

    Ist die Funktion dann so ok?



  • rudpower schrieb:

    Ist die Funktion dann so ok?

    Es werden zumindest weitere Elemente gesetzt. Im Zweifelsfall schau in der Hilfe oder im MSDN, ob es da noch andere gibt.

    Vielleicht noch mal zur Erklärung: Die Einstellungen der seriellen Schnittstelle bleiben erhalten, auch wenn dein Programm beendet ist. GetComDCB liest die Einstellungen der Schnittstelle und die sind entweder so wie sie bei Start des PCs initialisiert wurden, oder so wie das letzte Programm das die Schnittstelle benutzt hat sie hinterlassen hat.

    Je nachdem welches Programm vorher die Schnittstelle benutzt hat, liefert GetComDCB also unterschiedliche Werte. Das kann zu merkwürdigen Fehlern führen ...

    Also besser entweder sowas:

    DCB dcb;
    
      ZeroMemory(&dcb,sizeof(DCB));
      dcb.DCBlength = sizeof(DCB);
    
      // ... und jetzt alle anderen wichtigen Member im DCB zuweisen
    

    oder statt dem Zuweisen der weiteren Member eine Funktion verwenden:

    char Params[maxLen];
      sprintf(Params,"baud=38400 parity=N data=8 stop=1 dtr=on rts=on");
    
      if ( !BuildCommDCB(Params, &dcb) )
      {
        // ... Fehlerhandling
      }
    


  • hab die beiden Zeilen noch ergänzt. Leider finde ich in der Borlandhilfe nichts zu DCB. Wo kann ich bei MSDN auf http://msdn.microsoft.com/de-de/default.aspx was dazu finden? Hab die MSDN-Hilfe bisher noch nicht genutzt.

    Zu den Thread: Wenn ich für jedes Gerät einen eigenen Thread schreibe kann ich doch die Messwerte auch dort deklarieren und die Funktionen (PortOeffen, Erfasse Volumen,...) mit reinschreiben oder?

    Den Port kann der Benutzer auf der Hauptoberfläche über eine ComboBox auswählen (da hab ich eine Funktion die alle Ports aus der Registry holt und in die ComboBox einträgt). Nach einem Klick auf einen Port Öffnen - Button soll die Verbindung hergestellt werden. Kann ich die PortOeffnen-Funktion mit in den Thread schreiben und über die Hauptoberfläche aufrufen?



  • rudpower schrieb:

    Leider finde ich in der Borlandhilfe nichts zu DCB. Wo kann ich bei MSDN auf http://msdn.microsoft.com/de-de/default.aspx was dazu finden? Hab die MSDN-Hilfe bisher noch nicht genutzt.

    http://msdn.microsoft.com/en-us/library/aa363214%28v=VS.85%29.aspx

    rudpower schrieb:

    Zu den Thread: Wenn ich für jedes Gerät einen eigenen Thread schreibe kann ich doch die Messwerte auch dort deklarieren und die Funktionen (PortOeffen, Erfasse Volumen,...) mit reinschreiben oder?

    Ja, das kannst du alles in eine Klasse packen, bin nur nicht ganz sicher, ob du dafür die entsprechenden Grundlagen kennst ...





  • Hier noch mal ein Minimalbeispiel einer Klasse, die benutzt wird um Daten in Stringform von mehreren Threads zu einem Formular zu bekommen:

    class TData
    {
    private:
      TCriticalSection *pSync;
      TStringList *pData;
    public:
      TData();
      ~TData();
    
      void AddMessage(const AnsiString &s);
      void GetStrings(TStrings *ps);
    };
    
    TData::TData()
    {
      pSync = new TCriticalSection;
      pData = new TStringList;
    }
    
    TData::~TData()
    {
      delete pSync;
      delete pData;
    }
    
    void TData::AddMessage(const AnsiString &s)
    {
      pSync->Enter();
      pData->Add(s);
      pSync->Leave();
    }
    
    void TData::GetStrings(TStrings *ps)
    {
      pSync->Enter();
      ps->AddStrings(pData);
      pData->Clear();
      pSync->Leave();
    }
    

    Mehrere Threads können gleichzeitig AddMessage aufrufen, die Oberfläche holt sich die Daten mit GetStrings. Immer wenn ein Thread in AddMessage oder GetStrings ist, müssen die anderen warten, aber jeweils nur für den kurzen Moment des Funktionsaufrufs. Wenn sich ein Thread sekundenlang mit der seriellen Schnittstelle befasst, stört das die anderen nicht.



  • oh das ist Neuland für mich, aber das brauch ich doch auch gar nicht oder? Ich will es auch nicht allzu kompliziert machen.

    Hab mal für ein Gerät den Thread geschrieben:

    __fastcall Edu::Edu(bool CreateSuspended)
    	: TThread(CreateSuspended), volumen(0)
    {
    	Priority = tpHighest;
    }
    //---------------------------------------------------------------------------
    void __fastcall Edu::Execute()
    {
       while(!Terminated)
       {
    		volumen = ErfasseVolumen();
          Sleep(1000);
       }
    }
    //---------------------------------------------------------------------------
    void Edu::PortOeffnen(AnsiString port)
    {
    	status = com.OpenComm(port.c_str());
       com.SetDCB(9600);
       com.SetReadTimeouts(100, 10, 10);
    }
    //---------------------------------------------------------------------------
    double Edu::ErfasseVolumen()
    {
    	char rec[50]         = {0};
       unsigned char send[] = {0x16};
       while(com.ReceiveData(rec,1)>0); //Buffer leeren
       com.SendData(send,1);
       com.ReceiveData(rec,50);
       return StrToFloat(AnsiString(rec).SubString(4,9).Trim());
    }
    //---------------------------------------------------------------------------
    double Edu::GetVolumen()
    {
    	return volumen;
    }
    //---------------------------------------------------------------------------
    bool Edu::GetStatus()
    {
    	return status;
    }
    

    Hab es grad getestet und es funktioniert so weit erst einmal. Was meinst dazu?



  • Meistens wird es gutgehen 🙂

    Das Problem steckt hier

    while(!Terminated)
    {
        volumen = ErfasseVolumen();
        Sleep(1000);
    }
    
    double Edu::GetVolumen()
    {
        return volumen;
    }
    

    Es kann passieren, das ein Thread volumen einen neuen Wert zuweist, während ein anderer die Variable gerade liest. Dann kann es bei komplexeren Datentypen Müll beim Lesen geben ...

    Die Critical Section verhindert das, da kann immer nur ein Thread drin sein. Also besser so, auch wenn es mehr Arbeit ist:

    while(!Terminated)
    {
        double tmp = ErfasseVolumen();
    
        pSync->Enter();
        volumen = tmp;
        pSync->Leave();
    
        Sleep(1000);
    }
    
    double Edu::GetVolumen()
    {   
        double tmp;
    
        pSync->Enter();
        tmp = volumen;
        pSync->Leave();
    
        return tmp;
    }
    

    (Und das Anlegen von pSync im Konstruktor nicht vergessen.)

    So ist sichergestellt, das immer nur einer gleichzeitig auf volumen zugreift.



  • Schlau ist es mit Sicherheit auch, den Wert von hCom zu prüfen, bevor man ihn an irgendeine Funktion übergibt.



  • Es kann passieren, das ein Thread volumen einen neuen Wert zuweist, während ein anderer die Variable gerade liest.

    Welcher thread könnte volumen einen neuen Wert zuweisen? Den Wert bekommt volumen doch nur im eigenen Thread. Aber ich denke den meinst du auch. Und mit den anderen Thread der die Variable liest meinst du den Hauptthread der Anwendung? Ich hätte nicht gedacht, das dies kritisch sein könnte, dass Variablen gleichzeitig geschrieben und gelesen werden. Ich hätte gedacht, dass Variablen doch gerade durch die Zugriffsmodifizierer der Klassen (private, public) sicher sind.
    In den Büchern, die ich mir in dem letzten halben Jahr angeschafft habe, findet sich leider nichts dazu. Aber man lernt ja nie aus. Vielen Dank für den Tip. Ich werd das gleich mal ausprobieren.

    den Wert von hCom zu prüfen

    wie mach ich das am schlausten? Leider kenn ich mich mit Handles noch gar nicht aus. Ein Handle ist doch sowas wie ein Bool oder?



  • rudpower schrieb:

    Ein Handle ist doch sowas wie ein Bool oder?

    Nein, eher nicht. Ein Handle ist irgendein Dingsbums, das dein OS benutzt, um Objekte zu identifizieren. Was genau dahintersteckt braucht man nicht zu wissen. Aber es gibt gültige und ungültige Handles, schau dir mal an, was CreateFile im Fehlerfall zurückgibt.

    Das Thema Threadsynchronisation musst du dann behandeln, wenn deine Daten konsistent bleiben sollen. Angenommen, du hast einen Messwert und einen Zeitstempel zu diesem Messwert, dann kann es vorkommen, dass der Datenerfassungsthread den Messwert aktualisiert, aber vor der Aktualisierung des dazugehörigen Zeitstempels die CPU an die Datenauswertung abgibt. Diese liest den neuen Messwert aber den alten Zeitstempel -> Inkonsistenz. Durch Critical Sections oder Mutexe musst du dafür sorgen, dass alle zusammengehörige Daten in einem Aktualisierungszyklus aktualisiert werden.



  • ah ok. Im Fehlerfall wird ein INVALID_HANDLE_VALUE zurückggegeben. Die Nummer des letzten Fehlers erhält man dann mit GetLastError(). Also steht das HANDLE einfach für die Datei mit den Attributen?

    Hab das mit Critical Sections mal ausprobiert. Erhalte jetzt flogenden Fehler:

    [Linker Fataler Fehler] Fatal: Could not open C:\Programme\Borland\CBuilder5\Projects\mehrereThreads\Project1.exe (program still running?)
    

    Hab aber das Programm beendet.

    Hier meine neue Threadklasse:

    __fastcall Edu::Edu(bool CreateSuspended)
    	: TThread(CreateSuspended),
         status(false),
         volumen(0),
         volumenstrom(0),
         pSync(new TCriticalSection)
    
    {
    	Priority = tpHighest;
    }
    //---------------------------------------------------------------------------
    __fastcall Edu::~Edu()
    {
    	if(status == true) com.CloseComm();
       delete pSync;
    }
    //---------------------------------------------------------------------------
    void __fastcall Edu::Execute()
    {
       while(!Terminated)
       {
    		double tmp = ErfasseVolumen();
          pSync->Enter();
        	volumen = tmp;
        	pSync->Leave();
    
          double tmp2 = ErfasseVolumenstrom(); //oder kann ich hier wieder tmp nehmen ohne eine neue Variable tmp2 anzulegen?
          pSync->Enter();
          volumenstrom = tmp2;
          pSync->Leave();
    
          Sleep(1000);
       }
    }
    //---------------------------------------------------------------------------
    void Edu::PortOeffnen(AnsiString port)
    {
    	status = com.OpenComm(port.c_str());
       if (status == true)
       {
       	com.SetDCB(9600);
       	com.SetReadTimeouts(100, 10, 10);
       }
       else ShowMessage("Gaszähler Edu nicht verbunden!");
    
    }
    //---------------------------------------------------------------------------
    double Edu::ErfasseVolumen()
    {
    	if(status == true)
       {
          char rec[50]         = {0};
          unsigned char send[] = {0x16};
          while(com.ReceiveData(rec,1) > 0); //Buffer leeren
          com.SendData(send, 1);
          com.ReceiveData(rec, 50);
          return StrToFloat(AnsiString(rec).SubString(4,9).Trim());
       }
       else return 0;
    }
    //---------------------------------------------------------------------------
    double Edu::ErfasseVolumenstrom()
    {
       if(status == true)
       {
          char rec[50]         = {0};
          unsigned char send[] = {0x06};
          while(com.ReceiveData(rec, 1) > 0); //Buffer leeren
          com.SendData(send, 1);
          com.ReceiveData(rec, 50);
          return StrToFloat(AnsiString(rec).SubString(6,8).Trim());
    	}
       else return 0;
    }
    //---------------------------------------------------------------------------
    double Edu::GetVolumen()
    {
       double tmp;
       pSync->Enter();
    	tmp = volumen;
       pSync->Leave();
    	return tmp;
    }
    //---------------------------------------------------------------------------
    double Edu::GetVolumenstrom()
    {
    	double tmp;
       pSync->Enter();
    	tmp = volumenstrom;
       pSync->Leave();
    	return tmp;
    }
    //---------------------------------------------------------------------------
    bool Edu::GetStatus()
    {
    	return status;
    }
    

    hatte das vorher ohne den critical sections mal ausprobiert und auch für die anderen messgeräte eine seperate Threadklasse geschrieben und es funktionierte. Und ich war ganz erstaunt, dass es nun nicht mehr ruckelt. Was leider noch nicht geht, ist bei stehender Verbindung den Port zu ändern.

    Edit: Hab den Rechner neugestartet und das Programm laufen lassen. Dann konnte ich kompilieren und das Programm lief. Nach ca. 1 min. hat sich das Programm aufgehangen und ich konnte es auch nicht beenden. Dann kam eine Fehlermeldung "Fehler beim warten auf Prozessabbruch" oder so. Wo könnte der Fehler liegen?
    Hab mal alles mit CriticalSection rausgenommen. Dann bricht das Programm ebenfalls nach kurzer Zeit ab ohne Fehlermeldung.



  • nochmal zu der commclass. Ich hab mir in der MSDN die Member der DCB angeschaut. Ein paar sind ja bekannt. Nun will ich die Parameter nicht einfach ändern. Da es so ja funktioniert kann er doh einfach die Standardparameter nehmen oder nicht? Wie erfahre ich welchen Parameter ich wie setzen muss?
    Laut Handbuch des Geräts sind nur bekannt: 9600 Baud, No Parity, 8 bit, 1 stopbit. Das gilt für alle Messgeräte.

    Die Alternative einfach Deine Funktion zu nehmen wär da vielleicht einfacher. Wie sieht die dann aus?

    void comclass::SetDCB(int baud)
    {
       DCB dcb; // Device Control Block
       ZeroMemory(&dcb,sizeof(DCB));
    
       char Params[maxLen];
       sprintf(Params,"baud=9600 parity=N data=8 stop=1 dtr=on rts=on"); //dtr on? rts on?
    
       if ( !BuildCommDCB(Params, &dcb) )
       {
        //wie sieht der Code hier konkret aus?
       }
    }
    

    Edit: DTR und RTS müssen denk ich off. Hab grad in einem Handbuch eines der Geräte gesehen, dass man für dieses (Hardware Handshake) noch 2 Pins (DTR und RTS)dafür belegen müsste. Das hab ich aber nicht gemacht. Verwende aber nur RX, TX und Masse.



  • rudpower schrieb:

    Wie sieht die dann aus?

    Verwende halt sprintf oder AnsiString::sprintf mit Parametern. Dafür ist es da.

    rudpower schrieb:

    Hab grad in einem Handbuch eines der Geräte gesehen, dass man für dieses (Hardware Handshake) noch 2 Pins (DTR und RTS)dafür belegen müsste. Das hab ich aber nicht gemacht. Verwende aber nur RX, TX und Masse.

    Kann auch eine Ursache für sporadische Fehler sein.



  • das ich nur 3 Adern verwende kann ich jetzt nicht mehr ändern, da hardwareseitig bereits alles verdrahtet ist. Aber laut Handbuch der Geräte müssend DTR und RTS nicht zwingend belegt werden.

    Mein Programm läuft im Moment. Wenn ich es allerdings länger laufen lasse, bricht es mit einer Exception ab. Den Fehler konnte ich bisher leider nicht ausfindig machen.
    Was jetzt aber klappt ist das Ändern der Ports während des Programmlaufs. Dazu hab ich einfach vor dem Öffnen erst mal den Port geschlossen, falls dieser offen ist:

    bool comclass::OpenComm(char* port){
       CloseHandle(hCom);
       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) return false;
    	else return true;
    }
    

    Woran könnte das liegen, dass mein Programm ständig abschmiert?



  • Die Aussage "irgendwann schmiert mein Programm mit einer Exception ab" enthält nicht ein Mal ansatzweise irgendwelche Informationen.
    Wenn´s gar nicht anders geht bau halt Debug Ausgaben in deinen Programmcode ein, mit OutputDebugString kannst du Ausgaben in die Ereigniskonsole schreiben:

    void f()
    {
       OutputDebugString( "Betrete f()" );
    
       // mach was
    
       OutputDebugString( "Verlasse f()" );
    }
    

    Damit solltest du eingrenzen können, wo der Fehler auftritt.


Anmelden zum Antworten