c++ - 带有 TCSADRAIN 标志的 tcsetattr() 阻塞时间很奇怪

标签 c++ c linux serial-port

我在 Linux 中使用串行端口进行编码。

并且通信的要求是5ms inter-byte time

而且它要求我在 write() 调用之前根据字节的值更改每个字节的奇偶校验模式(偶数和奇数)。

所以我的代码如下(我简单描述了代码)

void setWakeupMode(int fd, bool mode) {
    struct termios tio;

    bzero(&tio, sizeof(tio));
    tcgetattr(fd, &tio);


    if (mode == false) {
        tio.c_cflag &= ~PARODD;
    } else if (mode == true) {
        tio.c_cflag |= PARODD;
    }

    if(tcsetattr(fd, TCSADRAIN, &tio) < 0){
        perror("tcsetattr Error");
    }
}

int main(){
    unsigned char a[2] = {0x01, 0x0F};

    write(fd, a, 1);

    setWakeupMode(fd, true);

    write(fd, a+1, 1);

}

但是代码不满足字节间时间导致将近20ms。

所以我尝试打印每个系统调用之间的准确时间,如下所示。

   int main(){
        unsigned char a[2] = {0x01, 0x0F};

        printf("write1 start : %s", /*time*/);
        write(fd, a, 1);

        printf("write1 end  : %s", /*time*/); 
        setWakeupMode(fd, true);

        printf("write2 start : %s", /*time*/);
        write(fd, a+1, 1);

        printf("write2 end : %s, /*time*/);
    }

这是结果

write1 start : 34.755201
write1 end   : 34.756046
write2 start : 34.756587  
write2 end   : 34.757349  

这个结果突然满足5ms字节间时间,导致1ms字节间时间

所以我尝试了几种方法。

最后我认识到,只有当我在 tcsetattr() 之前打印一些内容时,字节间时间才能满足。

例如,如果我删除 printf("write1 end : %s,/*time*/); 如下所示

   int main(){
        unsigned char a[2] = {0x01, 0x0F};

        printf("write1 start : %s", /*time*/);
        write(fd, a, 1);

        // printf("write1 end  : %s", /*time*/);  //remove this 
        setWakeupMode(fd, true);

        printf("write2 start : %s", /*time*/);
        write(fd, a+1, 1);

        printf("write2 end : %s", /*time*/);
    }

结果出奇的不同,看write1 startwrite2 start的间隔, 它是 18ms

write1 start : 40.210111
write2 start : 40.228332
write2 end   : 40.229187

如果我使用 std::cout 而不是 printf,结果是一样的。

为什么会出现这种奇怪的情况?

----------------------------编辑------------ --------------------

因为我看到了一些答案,所以有些人误解了我的问题。

我不担心 printf() 开销。

简单的说。

  1. 我想用1byte调用write() 但是write()的间隔必须在5ms以内
  2. 并且在调用 write() 之前,我必须使用 tcsetattr()
  3. 更改奇偶校验模式
  4. 但是间隔结果是18ms,几乎在tcsetattr()处被阻塞。
  5. 但是如果我在 tcsetattr() 之前调用 printf()std::cout,间隔会减少到 1 ~2 毫秒

也就是说,不知何故,在 tcsetattr() 之前调用 printf 会使 tcsetattr() 更快地从阻塞中返回。

------------------------更新---------------- ----------

我在这个问题上取得了进展。

我说过我必须放置 printf()std::cout 来缩短 tcsetattr() 上的阻塞时间。

但它并没有打印出影响那个问题的东西。

它只是需要一些延迟,也就是说,如果我在调用 tcsetattr() 之前放置 usleep(500),它也会影响字节间时间减少 1~2ms,从 tcsetattr() 返回更快。

我假设,如果我用 TCSADRAIN 标志调用 tcsetattr(),它会等到串行缓冲区中的所有数据都已传输,然后更改为我想要的设置。

这可能会造成一些延迟。

但是如果我专门调用delay,在我调用tcsetattr()之前,buffer状态已经为空(因为在delay期间,串口buffer中的数据被传输),所以有没有阻塞。

这是我假设的情况,可能吗?

最佳答案

this is the scenario i assume, is it possible?

你一定是对的。木屑写道:

uart_wait_until_sent() in drivers/tty/serial/serial_core.c explains why the interval between characters is quantized when a "drain" is involved: the serial port driver is polled for the TIOCSER_TEMP (transmitter empty) status using a delay with only millisecond resolution.

这几乎是正确的,除了轮询周期分辨率不是毫秒,而是jiffies:

         while (!port->ops->tx_empty(port)) {
                 msleep_interruptible(jiffies_to_msecs(char_time));
                 …

因此,如果 HZ 为 100 且 char_time 为 1(最小值),则每 10 毫秒检查一次状态。如您所写:

but if i calling specifically delay, before i call tcsetattr(), the buffer state is already empty(because during the delay time, the data in serial buffer is transmitted), so that there is no blocking.

换句话说,如果你延迟这个过程足够长的时间,当它到达发射器空测试时 !port->ops->tx_empty(port) 发射器已经是空的,不 sleep 发生;否则它至少会休眠 1 分钟。

关于c++ - 带有 TCSADRAIN 标志的 tcsetattr() 阻塞时间很奇怪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27499501/

相关文章:

c++ - 如何将 STL 容器作为参数传递给 BOOST_CHECK_EQUAL?

c++ - 找到一个整数,其平方值具有一定的模式

c++ - 将 main.cpp 更改为 main.c 并停止在 Clion 上使用 CMake 进行构建

linux - Qt 应用程序 GUI -- 自动启动 -- linux

linux - 在 buildroot 中使用构建后脚本

linux - 将长输入拆分为多个文本文件

c++ - 破坏二叉搜索树时发生读取访问冲突

c++ - 在 std::deque 上并行化 std::replace

c - 将 9 位值的流作为字节写入 C 中的文件

c - 使用 argv 获取命令行?