【问题标题】:/dev/ttyUSB latency of 15ms/dev/ttyUSB 延迟 15 毫秒
【发布时间】:2020-06-04 10:38:33
【问题描述】:

一个字节的异步串行在 115200 波特的线上耗时

以下C程序:

#define _POSIX_C_SOURCE 199309L /* needed for CLOCK_MONOTONIC */
#include <termios.h> /* tcattr */
#include <unistd.h>
#include <string.h> /* strerror */
#include <errno.h> /* errno */
#include <fcntl.h> /* O_NDELAY, O_RDWR, O_EXCL */
#include <stdio.h> /* printf */
#include <stdint.h>
#include <stdlib.h> /* exit */
#include <inttypes.h> /* PRIu64 */
#include <time.h> /* struct timespec */

int open_ttyusb(uint8_t n)
{
    char path[] = "/dev/ttyUSBnnn"; /* long enough for all values of uint8_t */
    sprintf(path, "/dev/ttyUSB%i", n);
    int fd = open(path, O_NDELAY|O_RDWR|O_EXCL);

    if (fd < 0) {
        exit(-1);
    }

    struct termios t;

    tcgetattr(fd, &t);             /* save old one  */
    t.c_iflag = t.c_oflag = 0;
    t.c_cflag = B115200;

    t.c_cflag |= (CLOCAL | CREAD); /* always 8-bit and local (no modem controls) */
    t.c_cflag |= CS8;              /* 8 bit character size mask */

    t.c_iflag = IGNBRK;

    t.c_lflag = 0;
    t.c_cc[VMIN] = 1;
    t.c_cc[VTIME] = 0;
    tcsetattr(fd, TCSAFLUSH, &t); /* do it all and flush input */
    tcflush(fd, TCIFLUSH);

    uint8_t rubbish[4096];
    while (read(fd, rubbish, 4096) > 0); /* make sure that there is nothing left */

    return fd;
}

uint64_t now(void) {
    struct timespec spec;

    clock_gettime(CLOCK_MONOTONIC, &spec);
    return spec.tv_nsec + spec.tv_sec * 1000000000;
} 

int main(void) {
    int fd0 = open_ttyusb(0);
    int fd1 = open_ttyusb(1);

    for (int i = 0; i < 10; ++i) { /* repeat the test 10 times */
        uint64_t start = now();
        char tx = 'A' + i; /* send chars 'A' through 'J' */
        write(fd0, &tx, 1); 
        uint8_t rx;
        while (read(fd1, &rx, 1) < 1); /* loop until read succeeds */
        if (rx != 'A' + i) { /* check that we've read the right char */
            exit(-2);
        }
        uint64_t elapsed = now() - start;
        printf("time_taken: %"PRIu64".%"PRIu64"ms\n", elapsed / 1000000, elapsed % 1000000);
    }   
}

输出:

time_taken: 1.292915ms
time_taken: 15.881472ms
time_taken: 15.859872ms
time_taken: 15.926402ms
time_taken: 15.975379ms
time_taken: 15.851588ms
time_taken: 15.882685ms
time_taken: 15.944355ms
time_taken: 15.915532ms
time_taken: 15.925496ms

(每次运行时,first time_taken 的变化范围为 0-16ms。)

我有两个 USB FTDI 适配器,将 RX 和 TX 交叉连接在一起。

  • 为什么字节从一个 ttyUSB 传输到另一个需要 15 毫秒?
  • 为什么第一次传输通常要快得多?
  • 能否更改代码以减少延迟?

【问题讨论】:

    标签: linux serial-port tty ftdi


    【解决方案1】:

    跑步:

    $ echo 1 | sudo tee /sys/bus/usb-serial/devices/ttyUSB0/latency_timer
    $ echo 1 | sudo tee /sys/bus/usb-serial/devices/ttyUSB1/latency_timer
    

    事先将输出更改为:

    time_taken: 0.702836ms
    time_taken: 0.959548ms
    time_taken: 1.2687ms
    time_taken: 0.973790ms
    time_taken: 0.994789ms
    time_taken: 0.971454ms
    time_taken: 1.12738ms
    time_taken: 1.9920ms
    time_taken: 0.986125ms
    time_taken: 0.947801ms
    

    这更容易接受。

    虽然我没有证据,但我猜测这会提高通过 USB 轮询 FTDI 设备的速率。 (USB 设备无法发起通信,必须轮询它们。)

    同样,我猜(原始)更快的第一响应是由于程序在轮询 ttyUSB1 之前启动的。

    附:在latency_timer 中回显一个零可以进一步改进:

    time_taken: 0.174095ms
    time_taken: 0.350353ms
    time_taken: 0.188431ms
    time_taken: 0.175983ms
    time_taken: 0.184215ms
    time_taken: 0.197525ms
    time_taken: 0.229502ms
    time_taken: 0.201619ms
    time_taken: 0.158024ms
    time_taken: 0.150763ms
    

    波形为:

    【讨论】:

    • 抱歉,我没有真正看到您为解决问题所做的“更改”。 16 毫秒是“小”包的标准超时。至少 FTDI 的 Windows 驱动程序尝试将 USB 数据包“批处理”在一起。这可以通过确保一次始终发送足够的数据来填充数据包或将超时设置降低到最小值来解决。
    • @ChristianB.更改是将值(1 或 0)回显到 /sys/bus/usb-serial/devices/ttyUSBn/latency_timer。这提高了轮询率并减少了接收字节的延迟。
    • 如果使用的协议有一个特殊的“帧结束”字节,可以将其设置为/sys/bus/usb-serial/devices/ttyUSBn/event_char,当字节到达时,FTDI芯片将立即刷新其内部FIFO,不依赖于latency_timer
    • 另一个注意事项:一些早期的 FTDI 芯片不支持小于 2 的延迟计时器。事件字符是当时减少数据包延迟的唯一方法。
    猜你喜欢
    • 2015-07-12
    • 2012-08-03
    • 2015-11-06
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 2020-04-05
    • 2011-11-12
    相关资源
    最近更新 更多