Forum: PC-Programmierung [Linux] Initialisierung eines Serial-Ports


von Alex44 (Gast)


Lesenswert?

Hallo,

ich versuche gerade auf meinem Linuxrechner ein kleines C-Prog zu 
schreiben, mir dem ich einen Seriellen Port auslesen kann (nur Rx und Tx 
angeschlossen!)

Ich vermute, dass ich Probleme bei der Konfiguration des Ports habe, 
denn immer wenn im Hintergrund GTK-Term läft, empfängt auch mein 
Programm. Schalte ich GTK-Term ab - steht auch mein Prog wieder.

Was macht GTK-Term, was ich nicht mache?

Hier der Code
1
/*
2
 * Im Folgenden soll versucht werden auf eine serielle Schnittstelle zu
3
 * schreiben.
4
 *
5
 * Dafür wird ein USB auf Seriell Konverterkabel verwendet, welcher über
6
 * die Gerätedatei /dev/ttyUSB0 erreichbar ist.
7
 */
8
9
 #include <stdio.h>     // Standard input/output Definitionen
10
 #include <string.h>    // String Funktionen Definitionen
11
 #include <unistd.h>    // UNIX Standard Funktionen Definitionen
12
 #include <fcntl.h>     // File control Definitionen
13
 #include <errno.h>     // Error number definitionen
14
 #include <termios.h>   // POSIX terminal control Definitionen
15
 #include <inttypes.h>  // Spezielle int-Datentypen
16
17
#define DEVICE "/dev/ttyUSB0"
18
19
/* mögliche Baudraten
20
 * 
21
 *      CBAUD      Bit mask for baud rate
22
 * 
23
 *      B0          0 baud (drop DTR)
24
 *      B50         50 baud
25
 *      B75          75 baud
26
 *      B110      110 baud
27
 *      B134      134,5 baud
28
 *      B150      150 baud
29
 *      B200      200 baud
30
 *      B300      300 baud
31
 *      B600      600 baud
32
 *      B1200      1.200 baud
33
 *      B1800      1.800 baud
34
 *      B2400      2.400 baud
35
 *      B4800      4.800 baud
36
 *      B9600      9.600 baud
37
 *      B19200      19.200 baud
38
 *      B38400      38.400 baud
39
 *      B57600      57.600 baud
40
 *      B76800      76.800 baud
41
 *      B115200     115.200 baud
42
 */
43
#define BAUDRATE_ein B115200
44
#define BAUDRATE_aus B115200
45
46
/*
47
 * Einstellungen für das Datenformat
48
 */
49
#define D_FORMAT _8N2
50
51
/*
52
 *  dazu fordefinierte Makros
53
 */
54
#if (D_FORMAT == _8N1)  // No parity (8N1):
55
  #define SETZE_FORMAT() {     \
56
    options.c_cflag &= ~PARENB;  \
57
      options.c_cflag &= ~CSTOPB;  \
58
      options.c_cflag &= ~CSIZE;  \
59
      options.c_cflag |= CS8;    \
60
     }
61
62
#elif (D_FORMAT== _8N2)  // No parity (8N2):
63
  #define SETZE_FORMAT() {     \
64
    options.c_cflag &= ~PARENB;  \
65
      options.c_cflag |= CSTOPB;  \
66
      options.c_cflag &= ~CSIZE;  \
67
      options.c_cflag |= CS8;    \
68
     }
69
70
#elif (D_FORMAT== _7E1)  // Even parity (7E1):
71
  #define SETZE_FORMAT() {     \
72
      options.c_cflag |= PARENB;  \
73
      options.c_cflag &= ~PARODD;  \
74
      options.c_cflag &= ~CSTOPB;  \
75
      options.c_cflag &= ~CSIZE;  \
76
      options.c_cflag |= CS7;    \
77
     }
78
79
#elif (D_FORMAT== _7O1)  // Odd parity (7O1):
80
  #define SETZE_FORMAT() {     \
81
      options.c_cflag |= PARENB;  \
82
      options.c_cflag |= PARODD;  \
83
      options.c_cflag &= ~CSTOPB;  \
84
      options.c_cflag &= ~CSIZE;  \
85
      options.c_cflag |= CS7;    \
86
     }
87
88
#elif (D_FORMAT== _7S1)  // Even parity (Space parity (gleich wie No parity (7S1)):
89
  #define SETZE_FORMAT() {     \
90
      options.c_cflag &= ~PARENB;  \
91
      options.c_cflag &= ~CSTOPB;  \
92
      options.c_cflag &= ~CSIZE;  \
93
      options.c_cflag |= CS8;    \
94
     }
95
#endif
96
97
98
99
/*
100
 * Das vom Sensor gesendete Paket beinhaltet 2 char-Bytes ('u''s') zur
101
 * Kalibrierung und die Höhe in der Länge von 2 Byte
102
 */
103
struct us_data
104
{
105
  char u;
106
  char s;
107
  uint16_t ush; 
108
} us_data;
109
110
111
/*
112
 * öffnet den oben angegebenen Port und gibt die Adresse zurück
113
 */
114
FILE *open_port(int *ifd)
115
{
116
    FILE *port;
117
    port = fopen(DEVICE, "rw");
118
  *ifd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
119
120
  if ((*ifd == -1)||(port == NULL))
121
  {
122
    perror("open_port: "DEVICE" konnte nicht geöffnet werden");
123
    _exit(ENODEV);
124
  }
125
    else
126
  {
127
    fcntl(*ifd, F_SETFL, 0);
128
  }
129
        
130
    return port;
131
}
132
133
/*
134
 *  Konfiguriere Port
135
 */
136
int config_port(int ifd)
137
{
138
  struct termios options;
139
140
    /*
141
     * Hohle aktuelle Optionen des Ports...
142
     */
143
    tcgetattr(ifd, &options);
144
145
    /*
146
     * Setze die Baudraten...
147
     */
148
    cfsetispeed(&options, BAUDRATE_ein);
149
    cfsetospeed(&options, BAUDRATE_aus);
150
151
    /*
152
     * Setze das Übertragungsformat...
153
     */
154
  SETZE_FORMAT()
155
156
    /*
157
     * Enable the receiver and set local mode...
158
     */
159
    options.c_cflag |= (CLOCAL | CREAD);
160
161
    /*
162
     * Setze die neuen Portoptionen...
163
     */
164
    tcsetattr(ifd, TCSANOW, &options);
165
166
  return 0;
167
}
168
169
170
/*
171
 * schreibt buf auf fd mit einer Länge von nbyte
172
 */
173
int write_n (FILE* fd, const void* buf, size_t nbyte)
174
{
175
    fwrite(buf, nbyte, 1 , fd);
176
    return 0;
177
}
178
179
/*
180
 * liest buf aus fd mit einer Länge von nbyte ein
181
 */
182
int read_n (FILE* fd, void* buf, size_t nbyte)
183
{
184
  fread(buf, nbyte, 1 , fd);
185
    return 0;
186
}
187
188
/*
189
 *  Synchronisiert den Eingangstrom indem nach dem 's' des Eingangsstroms
190
 *  gesucht und dann zwei Bytes (Höhenbytes) ausgelesen werden. So wird der
191
 *  nächste Lesevorgang vorraussichtlich bei 'u' beginnen.
192
 */
193
void read_sync (FILE* fd)
194
{
195
  char ein = 0;
196
  while (ein!='s')
197
  {
198
    read_n(fd, &ein, 1);
199
  }
200
  read_n(fd, &ein, 1);
201
  read_n(fd, &ein, 1);
202
}
203
204
/*
205
 *  liest Daten des US-Sensors vom Port ein und selektiert die Höhenwerte
206
 */
207
int read_us(FILE* fd)
208
{
209
  read_n(fd, &us_data, 4);
210
  return (int)us_data.ush;
211
}
212
213
214
int main (void)
215
{
216
217
  int ifd;
218
    FILE *fd; /* File descriptor für den Port */
219
    printf("öffne "DEVICE);
220
    fflush(stdout);
221
    fd = open_port(&ifd);
222
    printf(" - fertig\n");
223
    
224
    printf("Konfiguriere "DEVICE);
225
    fflush(stdout);
226
  config_port(ifd);
227
    printf(" - fertig\n");
228
229
    int i,n;
230
231
//  read_sync(fd);  
232
  
233
    for(i = 0 ; i <= 512 ; i++)
234
    {
235
 //       printf("\rSchicke: %3i", i);
236
 //       fflush(stdout);
237
238
//        n = write_n(fd, &i, 8);
239
//        n = read_n(fd, &us_data, 1);
240
//        printf("lese(%i): %c\n", i,us_data);
241
 
242
     n=0;
243
        printf("%i\n",read_us(fd));
244
        fflush(stdout);
245
        
246
        if(n < 0)
247
        {
248
            fputs("write() of 8 bytes failed!\n", stderr);
249
        }
250
        
251
    }
252
    
253
    printf("Schließe "DEVICE);
254
    fflush(stdout);
255
    close(ifd);
256
    fclose(fd);
257
    printf(" - fertig\n");
258
    
259
    printf("\nEnde\n");
260
    return 0;
261
}

Falls sich jemand wundert, warum ich zwei Filedeskriptoren habe - das 
tue ich auch. Denn open() kann mit FILE* nichts anfangen und read() 
nichts mit int.

Ich hoffe, mir kann einer einen Tipp geben.

Danke und Gruß
Alex

: Verschoben durch Moderator
von Klaus W. (mfgkw)


Lesenswert?

1. Deine Fehlerbehandlung ist nicht zielführend; z.B. kannst du nicht 
unterscheiden welches Öffnen nicht geklappt hat, geschweige denn warum

2. Das Device zweimal zu öffnen wird nicht klappen. Stattdessen solltwst 
du nur das low level io-device öffnen (open()) und dann zu diesem file 
descriptor ein FILE* holen mit fdopen().
Oder umgekehrt fopen(), und dann dessen fd ermitteln.

3. Das mit den Makros erscheint mir nicht geschickt. Ist deine Sache, 
aber schwer zum Fehlersuchen, damit ist die Fehlersuche halt auch deine 
Sache :-)

(evtl. noch mehr Fehler. aber so wird es sicher nichts...)

von Alex44 (Gast)


Lesenswert?

Hallo,

danke erst einmal für die Anmerkungen. Und ja, die Fehlerbehandlung muss 
noch überdacht werden.

Trotz allem lag der Fehler in der Porteinrichtung und das konnte behoben 
werden, in dem man den Quellcode offener Terminals liest. (hier war mir 
besonders cutecom eine große Hilfe)
1
/*
2
 * Im Folgenden soll versucht werden auf eine serielle Schnittstelle zu
3
 * schreiben.
4
 *
5
 * Dafür wird ein USB auf Seriell Konverterkabel verwendet, welcher über
6
 * die Gerätedatei /dev/ttyUSB0 erreichbar ist.
7
 */
8
9
 #include <stdio.h>     // Standard input/output Definitionen
10
 #include <string.h>    // String Funktionen Definitionen
11
 #include <unistd.h>    // UNIX Standard Funktionen Definitionen
12
 #include <fcntl.h>     // File control Definitionen
13
 #include <errno.h>     // Error number definitionen
14
 #include <termios.h>   // POSIX terminal control Definitionen
15
 #include <inttypes.h>  // Spezielle int-Datentypen
16
17
/*
18
 *  Korrekturwerte für die Messwerte
19
 */
20
#define KORREKTURTEILER 25600.0f  //Korrekturfaktor, der ein Teiler ist
21
#define KORREKTURAW 0.01f      // Offset
22
23
/*
24
 *  Serielle Portadresse
25
 */
26
#define DEVICE "/dev/ttyUSB0"
27
28
/*
29
 *  Handshake
30
 */
31
#define SOFTWAREHANDSHAKE 0
32
#define HARDWAREHANDSHAKE 0
33
34
/* mögliche Baudraten
35
 * 
36
 *      CBAUD      Bit mask for baud rate
37
 * 
38
 *      B0          0 baud (drop DTR)
39
 *      B50         50 baud
40
 *      B75          75 baud
41
 *      B110      110 baud
42
 *      B134      134,5 baud
43
 *      B150      150 baud
44
 *      B200      200 baud
45
 *      B300      300 baud
46
 *      B600      600 baud
47
 *      B1200      1.200 baud
48
 *      B1800      1.800 baud
49
 *      B2400      2.400 baud
50
 *      B4800      4.800 baud
51
 *      B9600      9.600 baud
52
 *      B19200      19.200 baud
53
 *      B38400      38.400 baud
54
 *      B57600      57.600 baud
55
 *      B76800      76.800 baud
56
 *      B115200     115.200 baud
57
 */
58
#define BAUDRATE_ein B115200
59
#define BAUDRATE_aus B115200
60
61
/*
62
 * Einstellungen für das Datenformat
63
 */
64
#define D_FORMAT _8N2
65
66
/*
67
 *  dazu fordefinierte Makros
68
 */
69
#if (D_FORMAT == _8N1)  // No parity (8N1):
70
  #define SETZE_FORMAT() {     \
71
    optionen.c_cflag &= ~PARENB;  \
72
      optionen.c_cflag &= ~CSTOPB;  \
73
      optionen.c_cflag &= ~CSIZE;  \
74
      optionen.c_cflag |= CS8;    \
75
     }
76
77
#elif (D_FORMAT== _8N2)  // No parity (8N2):
78
  #define SETZE_FORMAT() {     \
79
    optionen.c_cflag &= ~PARENB;  \
80
      optionen.c_cflag |= CSTOPB;  \
81
      optionen.c_cflag &= ~CSIZE;  \
82
      optionen.c_cflag |= CS8;    \
83
     }
84
85
#elif (D_FORMAT== _7E1)  // Even parity (7E1):
86
  #define SETZE_FORMAT() {     \
87
      optionen.c_cflag |= PARENB;  \
88
      optionen.c_cflag &= ~PARODD;  \
89
      optionen.c_cflag &= ~CSTOPB;  \
90
      optionen.c_cflag &= ~CSIZE;  \
91
      optionen.c_cflag |= CS7;    \
92
     }
93
94
#elif (D_FORMAT== _7O1)  // Odd parity (7O1):
95
  #define SETZE_FORMAT() {     \
96
      optionen.c_cflag |= PARENB;  \
97
      optionen.c_cflag |= PARODD;  \
98
      optionen.c_cflag &= ~CSTOPB;  \
99
      optionen.c_cflag &= ~CSIZE;  \
100
      optionen.c_cflag |= CS7;    \
101
     }
102
103
#elif (D_FORMAT== _7S1)  // Even parity (Space parity (gleich wie No parity (7S1)):
104
  #define SETZE_FORMAT() {     \
105
      optionen.c_cflag &= ~PARENB;  \
106
      optionen.c_cflag &= ~CSTOPB;  \
107
      optionen.c_cflag &= ~CSIZE;  \
108
      optionen.c_cflag |= CS8;    \
109
     }
110
#endif
111
112
113
114
/*
115
 * Das vom Sensor gesendete Paket beinhaltet 2 char-Bytes ('u''s') zur
116
 * Kalibrierung und die Höhe in der Länge von 2 Byte
117
 */
118
struct us_data
119
{
120
  char u;
121
  char s;
122
  uint16_t ush; 
123
} us_data,   alt_us_data;
124
125
126
/*
127
 * öffnet den oben angegebenen Port und gibt die Adresse zurück
128
 */
129
FILE *open_port(int *ifd)
130
{
131
    FILE *port;
132
    port = fopen(DEVICE, "rw");
133
  *ifd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
134
  
135
  if ((*ifd == -1)||(port == NULL))
136
  {
137
    perror("open_port: "DEVICE" konnte nicht geöffnet werden");
138
    _exit(ENODEV);
139
  }
140
    else
141
  {
142
    fcntl(*ifd, F_SETFL, 0);
143
  }
144
        
145
    return port;
146
}
147
148
/*
149
 *  Konfiguriere Port
150
 */
151
 
152
int config_port(int ifd)
153
{
154
  struct termios optionen;
155
156
    // Hohle aktuelle Optionen des Ports...
157
  if (tcgetattr(ifd, &optionen)!=0)
158
  {
159
    perror("tcgetattr() 3 failed\n");
160
  }
161
162
    // Setze die Baudraten...
163
    cfsetispeed(&optionen, BAUDRATE_ein);
164
    cfsetospeed(&optionen, BAUDRATE_aus);
165
166
    // Setze das Übertragungsformat...
167
  SETZE_FORMAT()
168
169
    // Enable the receiver and set local mode...
170
    optionen.c_cflag |= (CLOCAL | CREAD);
171
    
172
    //hardware handshake
173
  optionen.c_cflag &= ~CRTSCTS;
174
  
175
//  optionen.c_iflag=IGNPAR | IGNBRK;
176
  optionen.c_iflag=IGNBRK;
177
//  optionen.c_iflag=IGNPAR;
178
179
  if (SOFTWAREHANDSHAKE)
180
  {
181
    optionen.c_iflag |= IXON | IXOFF;
182
  }
183
  else
184
  {
185
    optionen.c_iflag &= ~(IXON|IXOFF|IXANY);
186
  }
187
  
188
  //hardware handshake
189
  if (HARDWAREHANDSHAKE)
190
  {
191
    optionen.c_cflag |= CRTSCTS;
192
  }
193
  else
194
  {
195
    optionen.c_cflag &= ~CRTSCTS;
196
  }
197
198
  optionen.c_lflag=0;
199
  optionen.c_oflag=0;
200
201
  optionen.c_cc[VTIME]=1;
202
  optionen.c_cc[VMIN]=60;
203
204
    // Setze die neuen Portoptionen...
205
  if (tcsetattr(ifd, TCSANOW, &optionen)!=0)
206
  {
207
    perror("tcsetattr() 1 failed\n");
208
  }
209
210
  return 0;
211
}
212
213
214
/*
215
 * schreibt buf auf fd mit einer Länge von nbyte
216
 */
217
int write_n (FILE* fd, const void* buf, size_t nbyte)
218
{
219
    fwrite(buf, nbyte, 1 , fd);
220
    return 0;
221
}
222
223
/*
224
 * liest buf aus fd mit einer Länge von nbyte ein
225
 */
226
int read_n (FILE* fd, void* buf, size_t nbyte)
227
{
228
  fread(buf, nbyte, 1 , fd);
229
    return 0;
230
}
231
232
/*
233
 *  Synchronisiert den Eingangstrom indem nach dem 's' des Eingangsstroms
234
 *  gesucht und dann zwei Bytes (Höhenbytes) ausgelesen werden. So wird der
235
 *  nächste Lesevorgang vorraussichtlich bei 'u' beginnen.
236
 */
237
void read_sync (FILE* fd)
238
{
239
  char ein = 0;
240
  while (ein!='s')
241
  {
242
    read_n(fd, &ein, 1);
243
  }
244
  read_n(fd, &ein, 1);
245
  read_n(fd, &ein, 1);
246
}
247
248
/*
249
 *  liest Daten des US-Sensors vom Port ein und selektiert die Höhenwerte
250
 */
251
float read_us(FILE* fd)
252
{
253
//  alt_us_data = us_data;
254
  read_n(fd, &us_data, 4);
255
  if (us_data.s!='s')
256
  {  
257
    read_sync(fd);
258
    return (int)alt_us_data.ush;
259
  }
260
  else
261
  {  
262
    return (float)( ((int)us_data.ush)/KORREKTURTEILER )+KORREKTURAW;
263
  }
264
}
265
266
267
int main (void)
268
{
269
  alt_us_data.ush = -1;
270
  int ifd;
271
    FILE *fd; /* File descriptor für den Port */
272
//    printf("öffne "DEVICE);
273
//    fflush(stdout);
274
    fd = open_port(&ifd);
275
//    printf(" - fertig\n");
276
    
277
//    printf("Konfiguriere "DEVICE);
278
    fflush(stdout);
279
//  config_port(ifd);
280
//    printf(" - fertig\n");
281
282
    int i,n;
283
284
  read_sync(fd);  
285
  
286
    for(i = 0 ; i <= 512 ; i++)
287
    {
288
 //       printf("\rSchicke: %3i", i);
289
 //       fflush(stdout);
290
291
//        n = write_n(fd, &i, 8);
292
//        n = read_n(fd, &us_data, 1);
293
//        printf("lese(%i): %c\n", i,us_data);
294
 
295
     n=0;
296
        printf("%f\n",read_us(fd));
297
        fflush(stdout);
298
        
299
        if(n < 0)
300
        {
301
            fputs("write() of 8 bytes failed!\n", stderr);
302
        }
303
        
304
    }
305
    
306
//    printf("\n");
307
//    printf("Schließe "DEVICE);
308
//    fflush(stdout);
309
    close(ifd);
310
    fclose(fd);
311
//    printf(" - fertig\n");
312
    
313
//    printf("\nEnde\n");
314
    return 0;
315
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Alex44 schrieb:
> Was macht GTK-Term, was ich nicht mache?

Möglicherweise den Port in den "raw" mode schalten.  Normalerweise
arbeitet er im "cooked" mode, d. h. die Editierfunktionen für eine
Eingabezeile sind aktiviert (Backspace und dergleichen), und nur
komplette Zeilen werden dann (am Stück) übertragen.

Schau dir mal die man page zur Funktion cfmakeraw(3) an.  Die ist
zwar nicht Posix, aber Linux hat sie offenbar von BSD übernommen.
Das ist eine Helper-Funktion, die alle notwendigen Einstellungen
dafür vornimmt.

von 900ss (900ss)


Lesenswert?

Hier steht alles gut beschrieben.

http://tldp.org/HOWTO/Serial-Programming-HOWTO/index.html

http://www.easysw.com/~mike/serial/serial.html

Und der Tip von Jörg könnte schon passen.

von Joerg W. (joergwolfram)


Lesenswert?

So verwende ich den seriellen Port (bei mir /dev/ttyACM0 für einen 
AVR-CDC) bei meinem R8C Programmer, das O_NONBLOCK brauche ich um einen 
Timeout beim Device zu erkennen:
1
static struct termios portstate;
2
3
int open_serial(char *devicename)
4
{
5
  int fd;
6
  
7
  struct termios my_port;
8
9
  if ((fd = open(devicename, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1)
10
  {
11
    perror("ERROR: open()");
12
    return 0;
13
  }
14
  if (tcgetattr(fd, &portstate) == -1)
15
  {
16
    perror("ERROR: tcgetattr()");
17
    return 0;
18
  }
19
20
  my_port = portstate;
21
22
  cfsetispeed(&my_port, B9600);
23
  cfsetospeed(&my_port, B9600);
24
25
  my_port.c_cflag |= CLOCAL | CREAD | CS8;
26
  my_port.c_cflag &= ~PARENB;
27
  my_port.c_cflag &= ~CSTOPB;
28
  my_port.c_oflag = 0;
29
30
  if (tcflush(fd, TCIOFLUSH) == -1)
31
  {
32
    perror("ERROR: tcflush()");
33
    return 0;
34
  }
35
  if (tcsetattr(fd, TCSANOW, &my_port) == -1)
36
  {
37
    perror("ERROR: tcsetattr()");
38
    return 0;
39
  }
40
  return fd;
41
}
42
43
int close_serial(int fd)
44
{
45
  tcsetattr(fd, TCSANOW, &portstate);
46
  if (close(fd) < 0)
47
  {
48
    perror("ERROR: close()");
49
    return 0;
50
  }
51
  return 1;
52
}

Jörg

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.