tcsetattr()使用TCSADRAIN标志阻塞时间是奇怪的

时间:2021-10-26 20:59:57

I am coding with serial port in Linux.

我正在用Linux的串口编码。

And the requirement of the communication is 5ms inter-byte time.

并且通信的要求是5ms的字节间时间。

And It requires me to change parity mode(even and odd) for each byte before write() call, according to what the byte's value is.

它要求我在write()调用之前为每个字节修改奇偶模式(偶数和奇数),根据字节的值。

So I code like below(i describe code simply)

我的代码如下(我简单地描述代码)

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);

}

But the code doesn't satisfy inter-byte time resulting in almost 20ms.

但是代码不满足字节间的时间,导致大约20ms。

So i tried print exact time between each system call like below.

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

   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*/);
    }

and This is the result

这就是结果

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

This result suddenly satisfy the 5ms inter-byte time, resulting in 1ms inter-byte time.

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

So i tried several ways.

所以我尝试了几种方法。

And finally i recognize that only when i print something right before tcsetattr(), inter-byte time is satisfied.

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

for example, if i remove printf("write1 end : %s, /*time*/); like below

例如,如果我删除printf(“write1结尾:%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*/);
    }

The result is surprisingly different, See the interval between write1 start and write2 start, It is 18ms.

结果惊人的不同,从write1开始到write2开始的间隔是18ms。

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

If i use std::cout instead of printf, the result is same.

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

Why does this weird situration happen?

为什么会发生这种奇怪的场景?

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

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

Since i see some answers, some are misunderstanding my problem.

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

I am not worrying printf() overhead.

我并不担心printf()开销。

simply saying.

简单地说。

  1. I want to call write()s with 1byte But the interval between write()s must be within 5ms
  2. 我想用1byte调用write()s,但是write()s之间的间隔必须在5ms以内
  3. And Before calling write(), I have to change parity mode using tcsetattr()
  4. 在调用write()之前,我必须使用tcsetattr()修改奇偶模式
  5. But The interval result is 18ms, being blocked at tcsetattr() almost time.
  6. 但是间隔结果是18ms,几乎在tcsetattr()时被阻塞。
  7. But If i call printf() or std::cout right before tcsetattr(), the interval reduce to 1~2ms.
  8. 但是如果我在tcsetattr()之前调用printf()或std: cout,那么间隔就会减少到1~2ms。

that is, somehow, calling printf before tcsetattr() make tcsetattr() return from blocking faster.

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

--------------------------Update----------------------------

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

I have progress on this problem.

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

I said that i had to put printf() or std::cout to make blocking time short on tcsetattr().

我说过我必须在tcsetattr()上使用printf()或std::cout来缩短阻塞时间。

But It was not printing something to affect to that problem.

但它并没有印刷什么东西来影响这个问题。

It just needed some delay, that is, if i put usleep(500) before calling tcsetattr(), it also make an affect on inter-byte-time reducing by 1~2ms, returning from tcsetattr() faster.

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

I assume, if i call tcsetattr() with TCSADRAIN flag, it wait until all data in serial buffer is transmitted, and change to the setting i want.

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

and it could make some delay.

它可能会造成一些延迟。

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.

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

this is the scenario i assume, is it possible?

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

2 个解决方案

#1


3  

this is the scenario i assume, is it possible?

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

You must be quite right. sawdust wrote:

你一定是对的。锯末写道:

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.

司机uart_wait_until_sent()/电传/串行/ serial_core。c解释了为什么在涉及“排液”时,字符之间的间隔是量化的:串行端口驱动程序使用一个只有毫秒级分辨率的延迟来轮询TIOCSER_TEMP(发送器空)状态。

That's almost right, except that the polling cycle resolution is not milliseconds, but jiffies:

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

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

So if HZ is 100 and char_time is 1 (the minimum), the status is checked every 10 ms. As you wrote:

如果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.

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

In other words, if you delay the process long enough that by the time it gets to the transmitter empty test !port->ops->tx_empty(port) the transmitter is already empty, no sleep occurs; otherwise it sleeps for at least 1 jiffy.

换句话说,如果你将进程延迟到足够长的时间,当进程到达发射机空测试时!端口->操作->tx_empty(端口)发射机已经空了,没有睡眠;否则它至少会睡1个瞬间。

#2


0  

Why don't you try sprintf / snprintf instead printf?? Because about snprintf, you may Speed the time if you shortened the string-Byte?

你为什么不试试sprintf / snprintf呢?因为关于snprintf,如果你缩短了字符串字节,你可以加快时间。

#1


3  

this is the scenario i assume, is it possible?

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

You must be quite right. sawdust wrote:

你一定是对的。锯末写道:

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.

司机uart_wait_until_sent()/电传/串行/ serial_core。c解释了为什么在涉及“排液”时,字符之间的间隔是量化的:串行端口驱动程序使用一个只有毫秒级分辨率的延迟来轮询TIOCSER_TEMP(发送器空)状态。

That's almost right, except that the polling cycle resolution is not milliseconds, but jiffies:

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

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

So if HZ is 100 and char_time is 1 (the minimum), the status is checked every 10 ms. As you wrote:

如果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.

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

In other words, if you delay the process long enough that by the time it gets to the transmitter empty test !port->ops->tx_empty(port) the transmitter is already empty, no sleep occurs; otherwise it sleeps for at least 1 jiffy.

换句话说,如果你将进程延迟到足够长的时间,当进程到达发射机空测试时!端口->操作->tx_empty(端口)发射机已经空了,没有睡眠;否则它至少会睡1个瞬间。

#2


0  

Why don't you try sprintf / snprintf instead printf?? Because about snprintf, you may Speed the time if you shortened the string-Byte?

你为什么不试试sprintf / snprintf呢?因为关于snprintf,如果你缩短了字符串字节,你可以加快时间。