¿Cómo abrir, leer y escribir desde el puerto serie en C?

Resuelto gnychis asked hace 13 años • 2 respuestas

Estoy un poco confundido acerca de leer y escribir en un puerto serie. Tengo un dispositivo USB en Linux que utiliza el controlador convertidor de dispositivo serie USB FTDI. Cuando lo conecto, crea: /dev/ttyUSB1.

Pensé que sería sencillo abrirlo y leerlo/escribirlo en C. Conozco la velocidad en baudios y la información de paridad, pero parece que no existe un estándar para esto.

¿Me estoy perdiendo algo o alguien puede indicarme la dirección correcta?

gnychis avatar Aug 05 '11 02:08 gnychis
Aceptado

Escribí esto hace mucho tiempo ( entre los años 1985 y 1992, con solo algunos ajustes desde entonces ), y simplemente copié y pegué los bits necesarios en cada proyecto.

Debes llamar cfmakerawa un ttyobtenido de tcgetattr. No puede poner a cero un struct termios, configurarlo y luego configurarlo ttycon tcsetattr. Si utiliza el método de puesta a cero, experimentará fallas intermitentes inexplicables, especialmente en BSD y OS X. Las "fallas intermitentes inexplicables" incluyen colgar archivos 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

Los valores de velocidad son B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800etc. Los valores de paridad son 0(es decir, sin paridad), PARENB|PARODD(habilitar paridad y usar impar), PARENB(habilitar paridad y usar par), PARENB|PARODD|CMSPAR(marcar paridad) y PARENB|CMSPAR( paridad espacial).

"Bloqueo" establece si un read()puerto espera a que llegue el número especificado de caracteres. Establecer sin bloqueo significa que read()devuelve tantos caracteres como estén disponibles sin esperar más, hasta el límite del búfer.


Apéndice:

CMSPARsolo es necesario para elegir la paridad de marca y espacio, lo cual es poco común. Para la mayoría de las aplicaciones, se puede omitir. Mi archivo de encabezado /usr/include/bits/termios.hpermite la definición CMSPARsolo si el símbolo del preprocesador __USE_MISCestá definido. Esa definición ocurre (en features.h) con

#if defined _BSD_SOURCE || defined _SVID_SOURCE
 #define __USE_MISC     1
#endif

Los comentarios introductorios de <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.
...
 */
wallyk avatar Aug 04 '2011 19:08 wallyk