【问题标题】:flush linux serial buffer in C在C中刷新linux串行缓冲区
【发布时间】:2018-02-12 18:45:43
【问题描述】:

我有一个蓝牙 IMU(在 /dev/rfcomm0 上被视为串行设备),它以四元数每秒 50 次返回当前角度。

我做了一个测试程序来读取数据并且它可以工作,但是我必须在一段时间内集成它,在读取完成后,创建一个线程并完成计算量大的细化,所以我无法读取使用 IMU 数据保持速率。

IMU 向我发送一个 54 字节的数据包,前两个字节始终设置为 Ascii "tt"。

这是我正在使用的 C 代码:

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] = 54;
        tty.c_cc[VTIME] = 1;

        if (tcsetattr(fd, TCSANOW, &tty) != 0) {
           printf("Error from tcsetattr: %s\n", strerror(errno));
           return -1;
        }
        return 0;
}

int start_serial()
{
    char *portname = "/dev/rfcomm0";
    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;
    }
    set_interface_attribs(fd, B115200);
    wlen = fwrite(fd, "?!CALIB050!?\n", 14);
    if (wlen != 14) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcflush(fd,TCIFLUSH);
    tcdrain(fd);    /* delay for output */
    return fd;
}

int read_serial(int fd)
{
    char buf[54];
    int rdlen;
    rdlen = read(fd, buf, 54);
    if (rdlen > 0)
    {
        struct CPacketCalib cc = CPacketCalibConvert(buf); 
        printf("%f %f %f %f\n",cc.q.I, cc.q.J, cc.q.K,cc.q.W);
    }
    else if (rdlen < 0)
    {
        printf("Error from read: %d: %s\r\n", rdlen, strerror(errno));
    }
    return 1;
}

int stop_serial(int fd)
{
    int wlen = write(fd, "?!STOPSEND!?\n", 14);
    if (wlen != 14)
    {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    fclose(fd);
    return 1;
}

如何在每个周期刷新串行缓冲区并开始正确获取我的数据包?

感谢您的帮助。

【问题讨论】:

  • 数据是否可以包含代表“tt”的字节?
  • 一个健壮的程序不会依赖非规范的读取系统调用来提供字节对齐的消息。断开连接和/或丢弃字节可能导致需要随时重新系统。程序应该解析所有字节并验证每条消息。

标签: c linux serial-port buffer


【解决方案1】:

您的问题听起来像是从数据包的中间开始读取并获得一个数据包的一半和另一个数据包的一半。在这种情况下,您需要阅读直到您到达数据包的开头,然后才将其提供给您的解析函数。所以是这样的:

char ch;
char[52] buf;
while(1){
    if(read(fd, &ch, sizeof(ch)) > 0 && ch == 't'){  
         if(read(fd, &ch, sizeof(ch)) > 0 && ch == 't'){
             if(read(fd, buf, sizeof(buf)) == sizeof(buf)){
                  struct CPacketCalib cc = CPacketCalibConvert(buf);
             }
         }
    }
}

所以CPacketCalibConvert 只会被调用一个完整的数据包(减去两个't',也许将它们添加回最里面的块内?)。

【讨论】:

  • 非常感谢,您的回答对我来说很宝贵。现在数据是正确的,但每 10 秒输出数据是空白几秒钟。我使用了像锯末建议的规范阅读,但问题仍然存在
  • “我使用了像锯末建议的规范阅读...” -- 不,这是对我实际写的内容的误解。您需要使用中间缓冲区,以便程序可以解析所有字节并验证每条消息。见stackoverflow.com/questions/54392470/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2015-08-10
  • 1970-01-01
相关资源
最近更新 更多