代码之家  ›  专栏  ›  技术社区  ›  fadedbee

/dev/ttyUSB延迟15ms

  •  0
  • fadedbee  · 技术社区  · 5 年前

    异步串行的一个字节需要<0.1ms,在115200波特的线上。(10位周期:开始+8+停止。)

    以下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
    

    (每次跑步的第一次拍摄时间为0-16ms。)

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

    • 为什么字节从一个ttyUSB到另一个需要15毫秒?
    • 为什么第一次传输通常要快得多?
    • 可以更改代码以减少延迟吗?
    0 回复  |  直到 5 年前
        1
  •  2
  •   fadedbee    5 年前

    正在运行:

    $ 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
    

    波形为:

    serial waveform