Für Democode, der dem POSIX-Standard entspricht, wie in Setting Terminal Modes Properly und Serial Programming Guide for POSIX Operating Systems beschrieben, wird Folgendes angeboten.
Dieser Code sollte mit Linux auf x86- sowie ARM- (oder sogar CRIS-) Prozessoren korrekt ausgeführt werden.
Es ist im Wesentlichen von der anderen Antwort abgeleitet, aber ungenaue und irreführende Kommentare wurden korrigiert.
Dieses Demoprogramm öffnet und initialisiert ein serielles Terminal mit 115200 Baud für den nicht-kanonischen Modus, das so portabel wie möglich ist.
Das Programm überträgt eine hartcodierte Textzeichenfolge an das andere Terminal und verzögert, während die Ausgabe durchgeführt wird.
Das Programm tritt dann in eine Endlosschleife ein, um Daten vom seriellen Terminal zu empfangen und anzuzeigen.
Standardmäßig werden die empfangenen Daten als hexadezimale Bytewerte angezeigt.
Damit das Programm die empfangenen Daten als ASCII-Code behandelt, kompilieren Sie das Programm mit dem Symbol DISPLAY_STRING, z. B.
cc -DDISPLAY_STRING demo.c
Wenn es sich bei den empfangenen Daten um ASCII-Text (und nicht um Binärdaten) handelt und Sie sie als Zeilen lesen möchten, die durch das Zeilenumbruchzeichen abgeschlossen werden, finden Sie in dieser Antwort ein Beispielprogramm.
#define TERMINAL "/dev/ttyUSB0"
#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 = TERMINAL;
int fd;
int wlen;
char *xstr = "Hello!\n";
int xlen = strlen(xstr);
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, xstr, xlen);
if (wlen != xlen) {
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);
}
Ein Beispiel für ein effizientes Programm, das empfangene Daten puffert und dennoch eine Byte-für-Byte-Weitergabe der Eingabe ermöglicht, finden Sie in dieser Antwort.
Ich habe dies vor langer Zeit geschrieben (aus den Jahren 1985-1992, mit nur wenigen Änderungen seitdem), und kopiere einfach die benötigten Bits und füge sie in jedes Projekt ein.
Sie müssen cfmakeraw
anrufen auf einem tty
erhalten von tcgetattr
. Sie können eine struct termios
nicht auf Null setzen , konfigurieren Sie es und legen Sie dann tty
fest mit tcsetattr
. Wenn Sie die Zero-out-Methode verwenden, werden Sie unerklärliche zeitweilige Fehler erleben, insbesondere auf den BSDs und OS X. „Unerklärte zeitweilige Fehler“ beinhalten Hängenbleiben in read(3)
.
#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;
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
(bedeutet 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
(Leerzeichenparität).
"Blocking" legt fest, ob ein read()
am Port wartet auf das Eintreffen der angegebenen Anzahl von Zeichen. Einstellung keine Blockierung bedeutet, dass ein read()
gibt zurück, wie viele Zeichen verfügbar sind, ohne auf weitere zu warten, bis zur Puffergrenze.
Nachtrag:
CMSPAR
wird nur für die Auswahl der Markierungs- und Leerzeichenparität benötigt, was ungewöhnlich ist. Für die meisten Anwendungen kann es weggelassen werden. Meine Header-Datei /usr/include/bits/termios.h
ermöglicht die Definition von CMSPAR
nur wenn das Präprozessorsymbol __USE_MISC
ist definiert. Diese Definition kommt vor (in features.h
) mit
#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif
Die einleitenden Kommentare von <features.h>
sagt:
/* 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.
...
*/