文件描述符处于阻止模式,但read()未阻止

时间:2019-07-07 21:06:35

标签: linux serial-port debian beagleboneblack termios

我正在编写一些软件来处理Beaglebone系统的串行端口读/写。操作系统是Debian9。我正在用--std=gnu99用C编写代码。

这是我的代码:

// reference
// https://www.cmrr.umn.edu/~strupp/serial.html

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>

int open_port(void)
{    
    int fd;

    fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
    // removing O_NDELAY no difference
    if(fd == -1)
    {
        perror("open_port: Unable to open /dev/ttyS1 - ");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
    }

    return fd;
}

int main()
{

    // open fd for serial port
    int fd = open_port();


    // set baud rate
    struct termios options;
    // get current options
    tcgetattr(fd, &options);
    // set input and output baud
    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    // enable reciever and set local mode
    // CLOCAL: ignore modem control lines
    // CREAD: enable reciever
    options.c_cflag |= (CLOCAL | CREAD);

    // set partity bit
    //options.c_cflag &= PARENB;
    options.c_cflag &= ~PARENB;
    // use even parity
    //options.c_cflag &= ~PARODD;
    // use only 1 stop bit
    options.c_cflag &= ~CSTOPB;
    // set character size to 8 bit
    options.c_cflag &= ~CSIZE;
    options.c_cflag &= CS8;
    // disable flow control
    //options.c_cflag &= ~CNEW_RTSCTS; // does not work?

    // note: check local options, some may be required
    // disable canonical input (use raw)
    // disable echoing of characters
    // disable echoing of erase characters
    // disable signals SIGINTR SIGSUSP SIGDSUSP, SIGQUIT
    // input characters are passed through exactly as recieved
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    // disable parity check
    options.c_iflag &= IGNPAR;

    // disable flow control (software)
    options.c_iflag &= ~(IXON | IXOFF | IXANY);

    // set raw output, no output processing
    options.c_oflag &= ~OPOST;

    // set options
    tcsetattr(fd, TCSANOW, &options);

    char data[] = {0x01, 0x03, 0x00, 0x00};
    int n = write(fd, data, 4);
    if(n =! 4)
    {
        printf("write fail %d\n", n);
    }

    char buffer[40];
    n = read(fd, buffer, 2);
    if(n != 2)
    {
        printf("read fail %d\n", n);
    }
    if(buffer[0] == 0x03 && buffer[1] == 0x00)
    {

    }
    else
    {
        printf("uart error\n");
    }

    char data2[] = {0x00, 0x00, 0x00, 0x01};
    n = write(fd, data2, 4);
    if(n != 4)
    {
        printf("write fail %d\n", n);
    }

    n = read(fd, buffer, 2);
    if(n != 2)
    {
        printf("read fail %d\n", n);
    }
    if(buffer[0] == 0x03 && buffer[1] == 0x00)
    {

    }
    else
    {
        printf("uart error\n");
    }

    printf("process complete\n");

    // to close
    close(fd);
}

我遇到的问题是致电read() 请勿阻止。这不是期望的行为。这些调用应阻塞,直到接收到2个字节的数据为止。

我的猜测是我在某个地方配置了错误的选项。但是我不知道错误在哪里,根据我的研究,这应该是在阻塞模式下阅读。 (fcntl(fd, F_SETFL, 0);

1 个答案:

答案 0 :(得分:1)

  

我遇到的问题是致电read() 请勿阻止

这实际上是结论,而不是观察结果。
大概您的程序报告读取的字节数为零,从技术上讲这不是错误。

  

...根据我的研究,这应该是在阻止模式下阅读。 (fcntl(fd, F_SETFL, 0);

正确,您已将文件描述符配置为阻止模式。

  

我的猜测是我在某个地方配置了错误的选项。

是的,您的termios配置不完整。 (它使用正确的按位运算,但不幸的是它不完整并且有错误,请参阅附录。)
在配置为非规范模式时,还必须指定 VMIN VTIME 参数。
如果未指定(如您的代码中所示),则默认值 VMIN = 0和 VTIME = 0可能有效,这等效于非阻塞读。


请注意, read()系统调用中的字节长度对完成行为(除了限制要返回的字节数上限)影响很小。
man 页面上进行 read(2)

  

read()尝试从文件描述符count读取多达fd个字节到从buf开始的缓冲区中。

因此,count参数实际上是最大值。
读取的实际字节数可以在零和count之间,具体取决于为 VMIN VTIME 配置的值(以及可用的实际数据)。

有关更多详细信息,请参见Linux Blocking vs. non Blocking Serial Read


要在每个系统调用中读取至少两个字节(没有时间限制),请在termios配置中包括以下内容:

options.c_cc[VMIN] = 2;
options.c_cc[VTIME] = 0;

在系统调用 tcsetattr()之前插入到程序中的上述两个语句将导致您的程序永远阻塞,直到每个read(fd, buffer, 2)语句中可以恰好返回两个字节为止。
read(fd, buffer, 4)语句也将永远阻塞,并返回2、3或4个字节的数据(取决于与数据接收和缓冲有关的程序时序)。


附录

发现termios初始化中存在一些错误。
发布后,您的程序无法正确发送或接收任何数据,并且您未能检测到这些错误或忽略了提及它们。

程序中的以下语句消除了之前的所有c_cflag操作,并且无意中将波特率重新配置为 B0 ,并将字符大小重新配置为 CS5

    options.c_cflag &= CS8;

正确的语法是:

    options.c_cflag |= CS8;

声明

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

应使用更多属性进行扩展,以匹配 cfmakeraw()

    options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

程序中的以下语句会禁用c_iflag中除 IGNPAR 之外的所有属性,并使 IGNPAR 属性处于未知/模糊状态:

    // disable parity check
    options.c_iflag &= IGNPAR;

要匹配 cfmakeraw(),应将其更改为:

    options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                       | INLCR | IGNCR | ICRNL);

在提到了所有更正后,我可以使您的程序按预期执行。