Forum: PC-Programmierung Daten per RS232 senden


von Michael (Gast)


Lesenswert?

Hallo zusammen,

ich versuche ein kleines Programm in C oder C++ zu schreiben das Daten 
per RS232 versendet versendet.
Es geht darum einem Messgerät einen 4 Byte langen Datenrahmen zu senden, 
wobei in jedem Byte ein Befehl in Form einer Dezimalzahl drinsteht.

Hat mir jemand einen Tipp wie ich auf die RS232-Schnittstelle zugreifen 
kann und wie man einen sollchen Datenrehmen generiert ?

Danke schonmal vorab

Schönen Samstag noch
Gruß Michael

von Klaus W. (mfgkw)


Lesenswert?

Michael schrieb:
> Hat mir jemand einen Tipp wie ich auf die RS232-Schnittstelle zugreifen
> kann

Ist google heute kaputt?

Aus einem anderen Programm zusammenkopiert sieht das etwa so aus, falls 
du von Windows sprichst:
1
#include <windows.h>
2
3
typedef HANDLE filehandle_t;
4
5
// erste serielle Schnittstelle als Defaultwert, wenn nichts anderes
6
// angegeben ist
7
// (unter Windows sind die seriellen Schnittstellen ab 1 durchnummeriert,
8
// also COM1:, COM2: etc.)
9
// 1, 2... für COM1, COM2...
10
const int DEFAULT_SERIAL = 1;
11
12
13
14
void init_comm()
15
{
16
  const char *devicename = "COM1:";
17
  DWORD dwError;
18
  BOOL fSuccess;
19
20
  SerielleSchnittstelle = CreateFile( devicename,
21
                                      GENERIC_READ | GENERIC_WRITE,
22
                                      0,
23
                                      NULL,
24
                                      OPEN_EXISTING,
25
                                      0,
26
                                      NULL
27
                                      );
28
29
  if( SerielleSchnittstelle == INVALID_HANDLE_VALUE )
30
  {
31
    dwError = GetLastError();
32
    // Fehlerbehandlung...
33
  }
34
35
  DCB dcb;  // device control block: Parameter der ser. Schnittstelle
36
37
  // aktuelle Einstellungen der seriellen Schnittstelle holen:
38
  fSuccess = GetCommState(SerielleSchnittstelle, &dcb);
39
  if( !fSuccess )
40
  {
41
    // Fehlerbehandlung...
42
  }
43
44
45
  dcb.BaudRate = CBR_9600;
46
  dcb.ByteSize = 8;
47
  dcb.Parity = NOPARITY;
48
  dcb.fParity = FALSE;
49
  dcb.StopBits =ONESTOPBIT;
50
51
  //dcb.Parity = EVENPARITY;
52
  //dcb.fParity = TRUE;
53
54
  dcb.fOutxDsrFlow = FALSE;
55
  dcb.fDtrControl  = DTR_CONTROL_ENABLE;
56
  dcb.fDsrSensitivity = FALSE;
57
58
59
  dcb.fTXContinueOnXoff  = FALSE;
60
  dcb.fOutX  = 0; // XON/XOFF aus
61
  dcb.fInX  =  0; // XON/XOFF aus
62
  dcb.fErrorChar  = FALSE;
63
  dcb.fNull  = FALSE;
64
65
  //dcb.fRtsControl = RTS_CONTROL_ENABLE;
66
  dcb.fRtsControl = RTS_CONTROL_DISABLE;
67
68
  dcb.fOutxCtsFlow = false;
69
70
  fSuccess = SetCommState( SerielleSchnittstelle, &dcb );
71
  if (!fSuccess)
72
  {
73
    // Fehlerbehandlung...
74
  }
75
76
  COMMTIMEOUTS timeout;
77
78
  timeout.ReadIntervalTimeout = MAXDWORD;
79
  timeout.ReadTotalTimeoutMultiplier = 0;
80
  timeout.ReadTotalTimeoutConstant = 0;
81
  timeout.WriteTotalTimeoutMultiplier = 100;
82
  timeout.WriteTotalTimeoutConstant = 10000;
83
84
  if( !SetCommTimeouts( SerielleSchnittstelle, &timeout ) )
85
  {
86
    dwError = GetLastError();
87
    // Fehlerbehandlung...
88
  }
89
90
}
91
92
93
94
void exit_comm()
95
{
96
  CloseHandle( SerielleSchnittstelle );
97
  SerielleSchnittstelle = INVALID_HANDLE_VALUE;
98
}
99
100
101
...
102
103
    DWORD nWritten = 0;
104
    WriteFile( SerielleSchnittstelle,
105
               "abcd",
106
               strlen( "abcd" ),
107
               &nWritten,
108
               NULL
109
               );
110
    if( nWritten!=( 4 ) )
111
    {
112
        // Fehlerbehandlung...
113
    }

> und wie man einen sollchen Datenrehmen generiert ?

char-Array anlegen, füllen und mit WriteFile() wegschicken; siehe oben.

von Klaus W. (mfgkw)


Lesenswert?

PS:
Das
  const int DEFAULT_SERIAL = 1;
kann weg; dafür fehlt am ANfang noch ein
  filehandle_t  SerielleSchnittstelle;

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> const char *devicename = "COM1:";

woher kommt immer der ":" in der MSDN finde ich ihn nicht?

von Klaus W. (mfgkw)


Lesenswert?

Das mit COM1: habe ich aus dem Gedächtnis geschrieben, kann auch sein, 
daß der Doppelpunkt weg muß.
Wenn ich mich recht entsinne, gehört er aber hin - sonst könnte ja auch 
eine Datei mit dem Namen COM1 gemeint sein.

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Wenn ich mich recht entsinne, gehört er aber hin - sonst könnte ja auch
> eine Datei mit dem Namen COM1 gemeint sein.
und mit : könnte ein laufwerk gemeint sein, nein er muss weg.

von Klaus W. (mfgkw)


Lesenswert?

Laufwerke haben doch nur einen Buchstaben; zumindest unter DOS waren die 
Devices COM1:, COM2; LPT: etc..
Nachdem Windows doch eine DOS-GUI ist ...

Ich mache aber nicht viel mit Windows, insofern überlasse ich es 
anderen, das zu probieren.

von Klaus W. (mfgkw)


Lesenswert?

Es gibt übrigens noch weitere Namen mit ein paar Backslashes drin für 
die Geräte in neueren Win-Versionen, irgendwas in der Art \\dev\com1 
oder so; die würden sicher auch gehen.
(Habe aber gerade keine Lust, die genauen Namen herauszusuchen; ich 
sitze vor einem Napf mit leckerstem Essen.)

von Peter II (Gast)


Lesenswert?

@Klaus Wachtler
unter windows trennt der ":" hinter einen Dateiname den Streamnamen von 
Dateinamen. Eine Datei kann mehrere Inhalte haben Datei:1 Datei:2. Keine 
Ahnung für was das sinnvoll sein soll.
Bin mir aber sicher das es COM1 ohne : sein muss.

Besser ist gleich "\\.\COMx" zu verwenden, dann geht es auch mit x > 10

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> Bin mir aber sicher das es COM1 ohne : sein muss.

kann gut sein!

>
> Besser ist gleich "\\.\COMx" zu verwenden, dann geht es auch mit x > 10

Genau das meinte ich; statt dev muß man natürlich einen Punkt nehmen, 
damit es nicht zu sehr nach Unix aussieht.

Nur zur Sicherheit: die \ in den Namen müssen in einem C-String natrlich 
gedoppelt werden, also z.B. "\\\\.\\COM1" im Quelltext.

von Klaus W. (mfgkw)


Lesenswert?

Im Umkehrschluß heißt das aber dann doch, daß man keine Datei mit dem 
schönen Namen COM1 erzeugen und verwenden kann?
Vielleicht gibt es dann in Windows10SP325 einen Workaround dafür...

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Im Umkehrschluß heißt das aber dann doch, daß man keine Datei mit dem
> schönen Namen COM1 erzeugen und verwenden kann?
ja so ist es. (ebenso wie NUL, LPT1 ..
http://msdn.microsoft.com/en-us/library/aa365247.aspx

aber immerhin ist es gut dokumentiert.

> Vielleicht gibt es dann in Windows10SP325 einen Workaround dafür...
das es bis jetzt noch niemand wirklich gestört hat, glaube ich kaum das 
sie auf diesen Feature verzichten.

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> aber immerhin ist es gut dokumentiert.

Das hilft einem durchschnittlichen Endanwender, der nach einem 
Dateinamen gefragt wird, nur bedingt :-)

Naja, mich soll es nicht stören.

von Lars (Gast)


Lesenswert?

Hallo zusammen,
ich versuch im Moment das selbe und habe schon beim initialisieren des 
Prots Probleme..
Ich programmiere unter Visual Studio mit C++

bei diesem Code

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string>

HANDLE SerielleSchnittstelle;

void main(){

 SerielleSchnittstelle = CreateFile(gszPort, GENERIC_READ | 
GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (SerielleSchnittstelle == INVALID_HANDLE_VALUE);
   // error opening port; abort

}

kommen die Fehler:

error C2065: 'gszPort': nichtdeklarierter Bezeichner
warning C4390: ';': Leere kontrollierte Anweisung aufgetreten; ist dies 
beabsichtigt
IntelliSense: Der Bezeichner ""gszPort"" ist nicht definiert.

diesen Teil des Codes habe ich von MSDN..

Danke schonmal im Voraus
Gruß Lars

von Peter II (Gast)


Lesenswert?

Lars schrieb:
> SerielleSchnittstelle = CreateFile(gszPort, GENERIC_READ |
> GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

Das sind aber erstmal C Grundlagen die da fehlern.
du verwendest eine variable gszPort die du überhaupt nicht deklarier 
hast.

Wenn man schon kopiert, dann sollte man alles kopieren oder oder man 
muss wirklich auch mal selber denken.

von Lars (Gast)


Lesenswert?

Bei Microsoft steht da auch nicht mehr.. also kopiert habe ist alles..

Ich habe auch schon zu deklarieren z.B. mit const char *gszPort = 
"portname";
Darauf folgt die Meldung:


error C2664: 'CreateFileW': Konvertierung des Parameters 1 von 'const 
char *' in 'LPCWSTR' nicht möglich

von Peter II (Gast)


Lesenswert?

Lars schrieb:
> Ich habe auch schon zu deklarieren z.B. mit const char *gszPort =
>
> "portname";
> error C2664: 'CreateFileW': Konvertierung des Parameters 1 von 'const
> char *' in 'LPCWSTR' nicht möglich
das ist doch schon mal der richtige ansatz.

das LPCWSTR zeigt dir das der compiler hier unicode will und ein char* 
ist nun mal kein unicode. Stell das Projekt auf nicht unicode ein und 
schon sollte es gehen. Wenn du unicode brachst dann darst du nicht char* 
verwenden sonder musst wchar* (oder so ähnlich) verwenden.

von Jean P. (fubu1000)


Lesenswert?

Hi,
falls du char* verwenden willst , brauchst du CreateFileA
1
bool SerialPort::SerialOpen(const std::string port, _PortSettings* set)
2
{
3
  bool status;
4
  hComm = CreateFileA(port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0,
5
                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
6
  if(hComm == INVALID_HANDLE_VALUE) {status = false;}
7
  else                              { ... ; status = true;}
8
9
  return status;
10
}

wobei hComm ein HANDLE ist.

Gruß

von Lars (Gast)


Lesenswert?

Danke für eure Tipps hat mir sehr geholfen..
Werde mich jetzt erstmal einwenig mit den DCB-Einstellungen beschäftigen 
müssen.. Wie kann ich den festlegen welcher COM-Port angesprochen werden 
soll ?
Könnt ihr mir einen gutes Programm empfehlen mit dem ich die 
Kommunikation auf der RS232 Schnittstelle mitschreiben kann ?
Ist vielleicht ganz Praktisch wenn es ans senden und empfangen geht.

Diesen Code habe ich bis jetzt zustande gebracht und er compiliert 
schonmal ohne fehler.. ;)

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string>

using namespace std;

HANDLE SerielleSchnittstelle;
DWORD dwError;
BOOL fSuccess;

const char *gszPort = "portname";

void open(){

 SerielleSchnittstelle = CreateFileA(gszPort, GENERIC_READ | 
GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

  //if (SerielleSchnittstelle == INVALID_HANDLE_VALUE) {
    //Handle the error.
    //return;
    //}

  //COM-Port config

  DCB dcb;  // device control block: Parameter der ser. Schnittstelle

/*
  // aktuelle Einstellungen der seriellen Schnittstelle holen:
  fSuccess = GetCommState(SerielleSchnittstelle, &dcb);
  if( !fSuccess )
  {
    // Fehlerbehandlung...
  }
*/

  dcb.BaudRate = CBR_9600;
  dcb.ByteSize = 8;
  dcb.Parity = NOPARITY;
  dcb.fParity = FALSE;
  dcb.StopBits =ONESTOPBIT;

  //dcb.Parity = EVENPARITY;
  //dcb.fParity = TRUE;

  dcb.fOutxDsrFlow = FALSE;
  dcb.fDtrControl  = DTR_CONTROL_ENABLE;
  dcb.fDsrSensitivity = FALSE;


  dcb.fTXContinueOnXoff  = FALSE;
  dcb.fOutX  = 0; // XON/XOFF aus
  dcb.fInX  =  0; // XON/XOFF aus
  dcb.fErrorChar  = FALSE;
  dcb.fNull  = FALSE;

  //dcb.fRtsControl = RTS_CONTROL_ENABLE;
  dcb.fRtsControl = RTS_CONTROL_DISABLE;

  dcb.fOutxCtsFlow = false;

/*
  fSuccess = SetCommState( SerielleSchnittstelle, &dcb );
  if (!fSuccess)
  {
    // Fehlerbehandlung...
  }
*/

  COMMTIMEOUTS timeout;

  timeout.ReadIntervalTimeout = MAXDWORD;
  timeout.ReadTotalTimeoutMultiplier = 0;
  timeout.ReadTotalTimeoutConstant = 0;
  timeout.WriteTotalTimeoutMultiplier = 100;
  timeout.WriteTotalTimeoutConstant = 10000;


  if( !SetCommTimeouts( SerielleSchnittstelle, &timeout ) )
  {
    dwError = GetLastError();
    // Fehlerbehandlung...
  }
}


void exit()
{
  CloseHandle( SerielleSchnittstelle );
  SerielleSchnittstelle = INVALID_HANDLE_VALUE;
}

void write(){
DWORD nWritten = 0;
    WriteFile( SerielleSchnittstelle,
               "abcd",
               strlen( "abcd" ),
               &nWritten,
               NULL
               );
    if( nWritten!=( 4 ) )
    {
        // Fehlerbehandlung...
    }
}

Gute Nacht dann erstmal..

von ... (Gast)


Lesenswert?

Das ist schon mal Mist:
1
const char *gszPort = "portname";
Du mußt portname schon durch den korrekten Namen der seriellen 
Schnittstelle ersetzen. Und damit sollte dann auch diese Frage 
beantwortet sein
Lars schrieb:
> Wie kann ich den festlegen welcher COM-Port angesprochen werden soll?

Wie die Namen aussehen steht im schon geposteten Link:
http://msdn.microsoft.com/en-us/library/aa365247.aspx

von Peter II (Gast)


Lesenswert?

Jean Player schrieb:
> Hi,
> falls du char* verwenden willst , brauchst du CreateFileA

NEIN! man sollte immer CreateFile verwenden, der compiler entscheidet 
welches für diesen Projekt das richtige ist. Man sollte nicht Unicode 
und nicht unicode mischen.
also Projekt richtig einstellen dann geht es auch ohne das A.

von Jean P. (fubu1000)


Lesenswert?

Hi,

Peter II schrieb:
> NEIN! man sollte immer CreateFile verwenden, der compiler entscheidet
> welches für diesen Projekt das richtige ist.
Grundsätzlich schon richtig. Aber wenn ich den Namen des COM Ports als 
UTF8 string bekommen hätte(aus welchem Grund auch immmer, sei jetzt 
dahin gestellt) und UNICODE wäre an. Würde ich es mir auch schenken und 
ein converting zu UTF16 machen und stattdessen CreateFileA benutzen 
(solange man weiss was man macht ).

>Man sollte nicht Unicode und nicht unicode mischen.
Wie du schon sagst, man sollte nicht aber kann :-)

> also Projekt richtig einstellen dann geht es auch ohne das A.
Also im Visual Studio Menu :
-->Project
-->Properties
-->Configuration Properties
-->C/C++
-->Preprocessor
-->Preprocessor Definitions
Unicode abstellen.

GRUß

von Lars (Gast)


Lesenswert?

Hallo zusammen,

habe bei der Einstellung nun

WIN32
_DEBUG
_CONSOLE
%(PreprocessorDefinitions)
UNICODE, _UNICODE

drinstehen.
Leider nimmt er den Befehl ohne A immernochnicht an..

von Lars (Gast)


Lesenswert?

Ok Kommando zurück ;)
hab jetzt unter den Projekteinstellungen -> Konfigurationseigenschaften 
-> Zeichensatz -> Multibyte Zeichensatz verwenden eingestellt und jetzt 
gehts auch ohne A

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.