C ++串口读取问题:ioctl(FIONREAD)设置错误的值吗?

时间:2016-08-30 13:49:59

标签: c++ unix serial-port ioctl

我面临一个非常奇怪的问题,我无法解决。我想读取(只是读取)微控制器通过usb在Mac OS X上使用c ++作为串行端口(FTDI)收集和发送的数据。一个完整数据序列的大小总是精确到10个字节。但是我使用以下代码来读取数据:

导入的库:

#include <iostream>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>

代码:

void init(){
    serial = open(port.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // 0_RDONLY ?
    struct termios options;
    //set opt to 115200-8n1
    cfsetspeed(&options, B115200);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    tcsetattr(serial, TCSANOW, &options);
    if (serial < 0){
        //Error
    }else{
        //run loop
    }
}

void serial_loop(){
    long bytes_read;
    int bytes_available;
    unsigned char msg[10];
    while(1){
        do{
            usleep(1000);
            ioctl(serial, FIONREAD, &bytes_available);

        }while(bytes_available < 10); //wait for the sequence to complete

        bytes_read = read(serial, msg, 10);

        //do some parsing here
    }
}

此代码几天前已经运行,但现在已经不存在了。根据终端 - &gt;数据完全到达计算机。 screen -command。我检查了仍然正确的port-file-name,并且端口也成功打开了。 我将我的问题缩小到ioctl命令FIONREAD,它没有将正确的数字写入bytes_available-var(不再)。 它确实有效,我相信,我没有改变代码中的任何内容。

您是否发现任何可能导致此问题的问题? 我的代码中有任何危险的段落吗? 谢谢你的帮助,我真的被困在这里......

编辑: 感谢您的反馈,我能够让它再次运行。这是当前的代码:

int serial;
void init(){
    serial = open(port.c_str(), O_RDWR | O_NOCTTY); //removed 0_NONBLOCK
    struct termios options;
    //set opt to 115200-8n1
    cfsetspeed(&options, B115200);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //Non-canonical
    options.c_cc[VMIN]     = 1; //block read until at least 1 byte was recieved
    options.c_lflag = 0;

    tcsetattr(serial, TCSANOW, &options);
    if (serial < 0){
        //Error
    }else{
        //run loop
    }
}

void serial_loop(){
    int datalength = 10;
    long bytes_read = 0;
    int bytes_in_msg = 0;
    unsigned char buf[datalength];
    unsigned char msg[datalength];
    do{
        bytes_read = read(serial, buf, datalength-bytes_in_msg);
        usleep(1000);
        if (bytes_read>0){
            memcpy(&msg[bytes_in_msg], &buf, bytes_read);
        }
        bytes_in_msg += bytes_read;
    }while(bytes_in_msg < datalength);

    //do some parsing here
    }
}

这有效,但是还有什么可能有问题吗?

感谢您的支持!

2 个答案:

答案 0 :(得分:1)

  

这段代码几天前就有效了,但现在已经不行了。

Fickle程序行为通常表示初始化不正确或不完整 您的 termios 初始化仅配置波特率和角色框架,其他一切都是偶然的。
Setting Terminal Modes ProperlySerial Programming Guide for POSIX Operating Systems

您的修订后的代码仍未妥善解决此问题 代码永远不会通过调用 tcgetattr()函数来初始化 termios 结构options
有关示例代码,请参阅我对how to open, read, and write from serial port in C

的回答
options.c_lflag = 0;

这不被认为是分配 termios 元素的正确方法。

options.c_cc[VMIN]     = 1;

非规范模式需要定义VMIN和VTIME条目 您的代码使用VTIME所在位置的任何垃圾值 见Linux Blocking vs. non Blocking Serial Read

  

FIONREAD,它不会将正确的数字写入bytes_available-var(不再)。

否定描述,即不会发生的描述,不如描述发生的情况那样有用或具体 那么你会得到什么样的价值呢? 为什么你认为它是&#34; 错误&#34;?

更重要的是,为什么不检查每个系统调用的返回值,尤其是您认为给您带来问题的 ioctl()

最有可能是 ioctl()失败,未更新bytes_available变量,并返回错误代码。
您的代码无条件地使用返回的参数,而不是首先检查是否有良好的返回。

批评你的代码的另一个答案是误导。可以禁用流量控制。您的代码已被破坏,因为它没有进行正确的初始化,也没有检查错误返回,而不是因为comm链接是&#34;死锁&#34;

在修改后的代码中:

    bytes_read = read(serial, buf, datalength-bytes_in_msg);
    usleep(1000);

阻塞读取之前和/或之后的睡眠或延迟是多余的。

答案 1 :(得分:0)

您的代码已损坏。没有协议可以允许读者等待作者和作者等待读者。如果确实如此,可能会导致死锁。

您的do / while循环拒绝读取任何数据,直到编写器全部写入10.然而,串行端口允许编写器拒绝写入任何更多数据,直到读取器读取它已写入的内容为止。因此,在作者写更多内容之前,它还不允许读者拒绝再读取任何数据。

您不能使用FIONREAD等待写入更多字节,因为编写者也可能在等您。相反,在字节变为可用时读取它们,从而确保您取消阻止编写器。将它们累积在一个缓冲区中,并在你拥有所需数字时中断。