Linux 如何在C中从串口打开,读取和写入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6947413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 05:28:45  来源:igfitidea点击:

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

clinuxserial-port

提问by gnychis

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, it creates: /dev/ttyUSB1.

我对读取和写入串行端口有点困惑。我在 Linux 中有一个 USB 设备,它使用 FTDI USB 串行设备转换器驱动程序。当我插入它时,它会创建:/dev/ttyUSB1。

I thought itd be simple to open and read/write from it in C. I know the baud rate and parity information, but it seems like there is no standard for this?

我认为用 C 语言打开和读/写它很简单。我知道波特率和奇偶校验信息,但似乎没有标准?

Am I missing something, or can someone point me in the right direction?

我错过了什么,或者有人能指出我正确的方向吗?

采纳答案by wallyk

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.

我很久以前写过这个(从 1985 年到 1992 年,从那时起只做了一些调整),然后将所需的部分复制并粘贴到每个项目中。

You must call cfmakerawon a ttyobtained from tcgetattr. You cannot zero-out a struct termios, configure it, and then set the ttywith 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).

您必须调用cfmakeraw一个tty从获得tcgetattr。您不能将 a 置零struct termios,配置它,然后设置ttywith tcsetattr。如果您使用归零方法,那么您将遇到无法解释的间歇性故障,尤其是在 BSD 和 OS X 上。“无法解释的间歇性故障”包括挂在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 
#if defined _BSD_SOURCE || defined _SVID_SOURCE
 #define __USE_MISC     1
#endif
0 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).

对速度的值是B115200B230400B9600B19200B38400B57600B1200B2400B4800,等。用于奇偶校验的值0(意味着没有奇偶校验), PARENB|PARODD(启用奇偶并使用奇数), PARENB(启用奇偶校验并使用偶数), PARENB|PARODD|CMSPAR(标记奇偶校验),和PARENB|CMSPAR(空间奇偶校验)。

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

“阻塞”设置read()端口上的a 是否等待指定数量的字符到达。设置无阻塞意味着read()无论有多少字符都可以返回,而无需等待更多字符,直至达到缓冲区限制。



Addendum:

附录:

CMSPARis 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.henables definition of CMSPARonly if the preprocessor symbol __USE_MISCis defined. That definition occurs (in features.h) with

CMSPAR仅用于选择标记和空间奇偶校验,这是不常见的。对于大多数应用程序,它可以省略。我的头文件仅在/usr/include/bits/termios.h定义CMSPAR了预处理器符号时才启用定义__USE_MISC。该定义出现(在features.h)与

/* 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.
...
 */

The introductory comments of <features.h>says:

的介绍性评论<features.h>说:

#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 = "/dev/ttyUSB0";
    int fd;
    int wlen;

    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, "Hello!\n", 7);
    if (wlen != 7) {
        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);
}

回答by sawdust

For demo code that conforms to POSIX standard as described in Setting Terminal Modes Properlyand Serial Programming Guide for POSIX Operating Systems, the following is offered.
It's essentially derived from the other answer, but inaccurate and misleading comments have been corrected.

对于符合设置终端模式POSIX 操作系统串行编程指南中描述的 POSIX 标准的演示代码,提供以下内容。
它基本上来自另一个答案,但不准确和误导性的评论已得到纠正。

 cc -DDISPLAY_STRING demo.c

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

要使程序将接收到的数据视为 ASCII 码,请使用符号 DISPLAY_STRING 编译程序,例如

##代码##

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 answerfor a sample program.

如果接收到的数据是 ASCII 文本(而不是二进制数据),并且您希望将其作为由换行符终止的行读取,请参阅示例程序的答案