tcsetattr()用TCSADRAIN标志阻塞时间很奇怪

时间:2014-12-16 07:21:29

标签: 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 inter-byte time

所以我尝试了几种方法。

最后我认识到只有当我在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,结果是一样的。

为什么这种奇怪的情境会发生?

------------------------------- EDIT ------------ --------------------

由于我看到了一些答案,有些人误解了我的问题。

我并不担心printf()开销。

简单地说。

  1. 我想用1byte调用write()但是write()之间的间隔必须在5ms之内
  2. 在致电write()之前,我必须使用tcsetattr()
  3. 更改奇偶校验模式
  4. 但是时间间隔结果为18ms,几乎在tcsetattr()时间被阻止。
  5. 但如果我在printf()之前拨打std::couttcsetattr(),则时间间隔缩短为1~2ms
  6. 也就是说,以某种方式,在tcsetattr()之前调用printf使tcsetattr()更快地从阻塞中返回。

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

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

    我说我必须放printf()std::cout才能在tcsetattr()上缩短拦截时间。

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

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

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

    它可能会有一些延迟。

    但如果我在调用TCSADRAIN之前调用特定延迟,则缓冲区状态已经为空(因为在延迟时间内,传输串行缓冲区中的数据),因此没有阻塞。

    这是我假设的情景,是否可能?

2 个答案:

答案 0 :(得分:3)

  

这是我假设的情景,是否可能?

你一定是对的。 sawdust写道:

  drivers/tty/serial/serial_core.c中的

uart_wait_until_sent()解释了为什么在涉及“漏极”时量化字符之间的间隔:使用TIOCSER_TEMP(发送器空)状态轮询串行端口驱动程序只有毫秒级分辨率的延迟。

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

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

因此,如果HZ为100且char_time为1(最小值),则每10 ms检查一次状态。正如你写的那样:

  

但是如果我打电话给tcsetattr()之前特别延迟,那么   缓冲区状态已经为空(因为在延迟时间内,数据   在串行缓冲区中传输),因此没有阻塞。

换句话说,如果你把这个过程延迟到发射机空测试!port->ops->tx_empty(port)时,发射机已经空了,就不会发生睡眠;否则它至少会睡1个小时。

答案 1 :(得分:0)

为什么不试试sprintf / snprintf而不是printf? 因为关于snprintf,你可以加快时间 你缩短了字符串字节?