Ich bin ein bisschen verwirrt über das Lesen und Schreiben an einer seriellen Schnittstelle. Ich habe ein USB-Gerät in Linux, das den FTDI USB Serial Device Converter-Treiber verwendet. Wenn ich es einstecke, erstellt es:/dev/ttyUSB1.
Ich dachte, es wäre einfach, es in C zu öffnen und zu lesen/schreiben. Ich kenne die Baudrate und die Paritätsinformationen, aber es scheint, als gäbe es keinen Standard dafür?
Vermisse ich etwas oder kann mich jemand in die richtige Richtung lenken?
Ich habe das vor langer Zeit geschrieben ( von 1985 bis 1992, mit nur ein paar Änderungen seitdem ) und einfach die benötigten Bits in jedes Projekt kopiert und eingefügt.
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}
...
char *portname = "/dev/ttyUSB1"
...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
write (fd, "hello!\n", 7); // send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
Die Werte für die Geschwindigkeit sind B115200
, B230400
, B9600
, B19200
, B38400
, B57600
, B1200
, B2400
, B4800
Usw. Die Werte für die Parität sind 0
(Dh keine Parität), PARENB|PARODD
(Parität aktivieren und ungerade verwenden). , PARENB
(Parität aktivieren und gerade verwenden), PARENB|PARODD|CMSPAR
(Parität markieren) und PARENB|CMSPAR
(Space-Parität).
"Blocking" legt fest, ob eine read()
am Port auf das Eintreffen der angegebenen Anzahl von Zeichen wartet. Das Setzen von no blocking bedeutet, dass eine read()
zurückgibt, obwohl bis zur Puffergrenze viele Zeichen verfügbar sind, ohne auf mehr zu warten.
Nachtrag:
CMSPAR
wird nur für die Auswahl von Markierungs- und Leerzeichenparitäten benötigt, was ungewöhnlich ist. Bei den meisten Anwendungen kann darauf verzichtet werden. Meine Header-Datei /usr/include/bits/termios.h
Ermöglicht die Definition von CMSPAR
nur, wenn das Präprozessorsymbol __USE_MISC
Definiert ist. Diese Definition erfolgt (in features.h
) Mit
#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif
In den einleitenden Kommentaren von <features.h>
Heißt es:
/* These are defined by the user (or the compiler)
to specify the desired environment:
...
_BSD_SOURCE ISO C, POSIX, and 4.3BSD things.
_SVID_SOURCE ISO C, POSIX, and SVID things.
...
*/
Für Demo-Code, der dem POSIX-Standard entspricht, wie in Festlegen der Terminalmodi und Serielles Programmierhandbuch für POSIX-Betriebssysteme beschrieben, wird Folgendes angeboten.
Es ist im Wesentlichen von der anderen Antwort abgeleitet, aber ungenaue und irreführende Kommentare wurden korrigiert.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void set_mincount(int fd, int mcount)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error tcgetattr: %s\n", strerror(errno));
return;
}
tty.c_cc[VMIN] = mcount ? 1 : 0;
tty.c_cc[VTIME] = 5; /* half second timer */
if (tcsetattr(fd, TCSANOW, &tty) < 0)
printf("Error tcsetattr: %s\n", strerror(errno));
}
int main()
{
char *portname = "/dev/ttyUSB0";
int fd;
int wlen;
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd, B115200);
//set_mincount(fd, 0); /* set to pure timed read */
/* simple output */
wlen = write(fd, "Hello!\n", 7);
if (wlen != 7) {
printf("Error from write: %d, %d\n", wlen, errno);
}
tcdrain(fd); /* delay for output */
/* simple noncanonical input */
do {
unsigned char buf[80];
int rdlen;
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
#ifdef DISPLAY_STRING
buf[rdlen] = 0;
printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
#endif
} else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
} else { /* rdlen == 0 */
printf("Timeout from read\n");
}
/* repeat read to get full message */
} while (1);
}
Um die empfangenen Daten vom Programm als ASCII - Code behandeln zu lassen, kompilieren Sie das Programm mit dem Symbol DISPLAY_STRING, z.
cc -DDISPLAY_STRING demo.c
Handelt es sich bei den empfangenen Daten um ASCII - Text (und nicht um Binärdaten), und möchten Sie diese als Zeilen lesen, die mit dem Zeilenvorschub abgeschlossen sind, lesen Sie diese Antwort für ein Beispielprogramm.