Per il codice demo conforme allo standard POSIX come descritto in Impostazione corretta delle modalità del terminale e Guida alla programmazione seriale per i sistemi operativi POSIX, viene offerto quanto segue.
Questo codice dovrebbe essere eseguito correttamente utilizzando Linux su processori x86 e ARM (o anche CRIS).
Deriva essenzialmente dall'altra risposta, ma i commenti imprecisi e fuorvianti sono stati corretti.
Questo programma demo apre e inizializza un terminale seriale a 115200 baud per una modalità non canonica il più portatile possibile.
Il programma trasmette una stringa di testo codificata all'altro terminale e ritarda l'esecuzione dell'output.
Il programma quindi entra in un ciclo infinito per ricevere e visualizzare i dati dal terminale seriale.
Per impostazione predefinita, i dati ricevuti vengono visualizzati come valori di byte esadecimali.
Per fare in modo che il programma tratti i dati ricevuti come codici ASCII, compilare il programma con il simbolo DISPLAY_STRING, es.
cc -DDISPLAY_STRING demo.c
Se i dati ricevuti sono testo ASCII (anziché dati binari) e desideri leggerli come righe terminate dal carattere di nuova riga, consulta questa risposta per un programma di esempio.
#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);
}
Per un esempio di un programma efficiente che fornisce il buffering dei dati ricevuti ma consente la gestione byte per byte dell'input, guarda questa risposta.
L'ho scritto molto tempo fa (dagli anni 1985-1992, con solo poche modifiche da allora), e ho semplicemente copiato e incollato i bit necessari in ogni progetto.
Devi chiamare cfmakeraw
su un tty
ottenuto da tcgetattr
. Non puoi azzerare un struct termios
, configuralo, quindi imposta tty
con tcsetattr
. Se utilizzi il metodo zero-out, sperimenterai errori intermittenti inspiegabili, specialmente su BSD e OS X. Gli "errori intermittenti inspiegabili" includono blocchi 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
I valori per la velocità sono B115200
, B230400
, B9600
, B19200
, B38400
, B57600
, B1200
, B2400
, B4800
, ecc. I valori per la parità sono 0
(che significa nessuna parità), PARENB|PARODD
(abilita la parità e usa dispari), PARENB
(abilita la parità e usa pari), PARENB|PARODD|CMSPAR
(segna parità) e PARENB|CMSPAR
(parità spaziale).
"Blocking" imposta se un read()
sulla porta attende l'arrivo del numero specificato di caratteri. Impostazione nessun blocco significa che un read()
restituisce quanti caratteri sono disponibili senza attenderne altri, fino al limite del buffer.
Addendum:
CMSPAR
è necessario solo per scegliere la parità di segni e spazi, che è raro. Per la maggior parte delle applicazioni, può essere omesso. Il mio file di intestazione /usr/include/bits/termios.h
abilita la definizione di CMSPAR
solo se il simbolo del preprocessore __USE_MISC
è definito. Tale definizione ricorre (in features.h
) con
#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif
I commenti introduttivi di <features.h>
dice:
/* 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.
...
*/