How to open, read, and write from serial port in C?

前端 未结 2 624
说谎
说谎 2020-11-22 01:10

I am a little bit confused about reading and writing to a serial port. I have a USB device in Linux that uses the FTDI USB serial device converter driver. When I plug it in,

相关标签:
2条回答
  • 2020-11-22 01:29

    I wrote this a long time ago (from years 1985-1992, with just a few tweaks since then), and just copy and paste the bits needed into each project.

    You must call cfmakeraw on a tty obtained from tcgetattr. You cannot zero-out a struct termios, configure it, and then set the tty with tcsetattr. If you use the zero-out method, then you will experience unexplained intermittent failures, especially on the BSDs and OS X. "Unexplained intermittent failures" include hanging 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
    

    The values for speed are B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, etc. The values for parity are 0 (meaning no parity), PARENB|PARODD (enable parity and use odd), PARENB (enable parity and use even), PARENB|PARODD|CMSPAR (mark parity), and PARENB|CMSPAR (space parity).

    "Blocking" sets whether a read() on the port waits for the specified number of characters to arrive. Setting no blocking means that a read() returns however many characters are available without waiting for more, up to the buffer limit.


    Addendum:

    CMSPAR is needed only for choosing mark and space parity, which is uncommon. For most applications, it can be omitted. My header file /usr/include/bits/termios.h enables definition of CMSPAR only if the preprocessor symbol __USE_MISC is defined. That definition occurs (in features.h) with

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

    The introductory comments of <features.h> says:

    /* 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.
    ...
     */
    
    0 讨论(0)
  • 2020-11-22 01:33

    For demo code that conforms to POSIX standard as described in Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems, the following is offered.
    This code should execute correctly using Linux on x86 as well as ARM (or even CRIS) processors.
    It's essentially derived from the other answer, but inaccurate and misleading comments have been corrected.

    This demo program opens and initializes a serial terminal at 115200 baud for non-canonical mode that is as portable as possible.
    The program transmits a hardcoded text string to the other terminal, and delays while the output is performed.
    The program then enters an infinite loop to receive and display data from the serial terminal.
    By default the received data is displayed as hexadecimal byte values.

    To make the program treat the received data as ASCII codes, compile the program with the symbol DISPLAY_STRING, e.g.

     cc -DDISPLAY_STRING demo.c
    

    If the received data is ASCII text (rather than binary data) and you want to read it as lines terminated by the newline character, then see this answer for a sample program.


    #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);
    }
    
    0 讨论(0)
提交回复
热议问题