GNU/Linux >> Tutoriales Linux >  >> Linux

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

Para el código de demostración que cumple con el estándar POSIX como se describe en Configuración adecuada de los modos de terminal y Guía de programación en serie para sistemas operativos POSIX, se ofrece lo siguiente.
Este código debería ejecutarse correctamente utilizando Linux en x86, así como procesadores ARM (o incluso CRIS).
Se deriva esencialmente de la otra respuesta, pero se han corregido los comentarios inexactos y engañosos.

Este programa de demostración abre e inicializa un terminal serial a 115200 baudios para el modo no canónico que es lo más portátil posible.
El programa transmite una cadena de texto codificada a la otra terminal y se demora mientras se realiza la salida.
Luego, el programa ingresa en un bucle infinito para recibir y mostrar datos del terminal serial.
De forma predeterminada, los datos recibidos se muestran como valores de bytes hexadecimales.

Para hacer que el programa trate los datos recibidos como códigos ASCII, compile el programa con el símbolo DISPLAY_STRING, por ejemplo,

 cc -DDISPLAY_STRING demo.c

Si los datos recibidos son texto ASCII (en lugar de datos binarios) y desea leerlos como líneas terminadas con el carácter de nueva línea, consulte esta respuesta para obtener un programa de muestra.

#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);
}

Para ver un ejemplo de un programa eficiente que proporciona almacenamiento en búfer de los datos recibidos pero que permite el manejo byte a byte de la entrada, consulte esta respuesta.


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

Debes llamar al cfmakeraw en un tty obtenido de tcgetattr . No puede poner a cero un struct termios , configúrelo y luego configure el tty con tcsetattr . Si usa el método de puesta a cero, entonces experimentará fallas intermitentes inexplicables, especialmente en los BSD y OS X. Las "fallas intermitentes inexplicables" incluyen bloqueos en 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 , B4800 , etc. Los valores de paridad son 0 (sin paridad), PARENB|PARODD (habilitar paridad y usar impar), PARENB (habilitar paridad y usar incluso), PARENB|PARODD|CMSPAR (marcar paridad) y PARENB|CMSPAR (paridad espacial).

"Bloqueo" establece si un read() en el puerto espera que llegue el número especificado de caracteres. Configuración sin bloqueo significa que un read() devuelve la cantidad de caracteres disponibles sin esperar más, hasta el límite del búfer.

Anexo:

CMSPAR solo se necesita 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.h habilita la definición de CMSPAR solo si el símbolo del preprocesador __USE_MISC se define. 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.
...
 */

Linux
  1. Cómo abrir los puertos 80 y 443 en FirewallD

  2. Cómo leer y escribir en unidades NTFS de Windows como cualquier usuario

  3. ¿Cómo conectar y enviar datos a un puerto serie Bluetooth en Linux?

  4. ¿Cómo encontrar qué puerto serie está en uso?

  5. ¿Cómo abrir el puerto serie en Linux sin cambiar ningún pin?

Cómo usar el comando Netcat para leer y escribir datos en la red

Cómo encontrar y cerrar puertos abiertos en Linux

Cómo cambiar el puerto de WordPress en Apache y Nginx

Cómo escribir y ejecutar un programa C en Linux

Journalctl:Cómo leer y editar registros de Systemd

¿Por qué está abierto el puerto 1111 y es seguro estarlo?