首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >FTDI D2XX窗口到Linux

FTDI D2XX窗口到Linux
EN

Stack Overflow用户
提问于 2022-11-18 20:37:05
回答 1查看 45关注 0票数 0

对于串行端口,我有一个简单的轮询循环,它接收连续的位邦数据,而且我看到性能从Windows到Linux的巨大损失。

这是一个非常简单的应用程序,使用FTDI的D2XX驱动程序运行UART,在异步bit模式下连续读取传入数据。

它只是总是轮询一个最小数量的传入字节,如果数量满足,读取整个接收缓冲区进入内存,重复。

我的问题是,我最初是用Windows编写的。每个回路在2毫秒或更短的时间内完成,约0.01%的环路偏离范围进入2-2.5毫秒区域。这对于我的应用程序来说是非常完美的。

但是,我在我的服务器上使用Ubuntu,这就是这个应用程序要运行的地方。问题是,使用完全相同的代码,Linux要慢得多。实际上,1%的循环超过2毫秒,我看到峰值高达10毫秒。更奇怪的是,当这些高峰发生时,通常的模式如下(以微秒计):

1500 1490 1550 5000 500 430 450 50 1500 1400

因此,一个循环平均需要1.5毫秒,但有时一个样本会出现很大的滞后峰值,随后的几个样本在恢复正常之前比平均速度要快得多。

我目前正在使用内核。我尝试了存储库中的低延迟内核,但它似乎没有产生明显的效果。它可能已经将超时的循环总数从1%减少到0.7%,但还不清楚。我正在为我正在使用的Intel CPU构建一个经过修改的内核,并对其进行了一些优化,但我并不乐观。

我已经读过,您可以将一个ASYNC_LOW_LATENCY标志插入到串行端口上以提高性能,但是在使用D2XX驱动程序时,FTDI没有/dev/ttyUSB设备,所以我不知道如何处理。

有谁知道Linux是什么让它比Windows更慢读这个UART的,我能做些什么吗?尽量减少超时对我来说是至关重要的任务。我甚至为在专用内核上运行这段代码的线程设置了实时优先级,我没有看到任何改进。

任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

发布于 2022-11-19 01:01:33

  1. 第一个问题是在VM上还是在专用CPU上运行Ubuntu?虚拟机明显地减缓了阅读过程。
  2. 在串口的线程循环中,多长时间读一次?在C语言中,我使用以下设置打开端口:
代码语言:javascript
复制
    int iResult;
    int iParity_flag;
    int RTS_flag;
    int DTR_flag;
    int fd;                             // File descriptor
    struct termios PortSettings;

    // Open port
    fd = open(in_DeviceName, O_RDWR | O_NOCTTY | O_NDELAY);
// Save File Descriptor
    int= fd;
    *out_FileDescriptor = fd;

    // Read in existing settings, and handle any error
    // NOTE: This is important! POSIX states that the struct passed to tcsetattr()
    // must have been initialized with a call to tcgetattr() overwise behaviour
    // is undefined
    if(tcgetattr(fd, &PortSettings) != 0) {
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
    }

    // Open the device in nonblocking mode
    fcntl(fd, F_SETFL, FNDELAY);

    // Set parameters   
    bzero(&PortSettings, sizeof(PortSettings));    // Clear all the options

    // [♦] BAUDRATE [♦]
   speed_t  Speed;
    switch (in_Baudrate)
    {
        case 110  :     Speed=B110; break;
        case 300  :     Speed=B300; break;
        case 600  :     Speed=B600; break;
        case 1200 :     Speed=B1200; break;
        case 2400 :     Speed=B2400; break;
        case 4800 :     Speed=B4800; break;
        case 9600 :     Speed=B9600; break;
        case 19200 :    Speed=B19200; break;
        case 38400 :    Speed=B38400; break;
        case 57600 :    Speed=B57600; break;
        case 115200 :   Speed=B115200; break;
        default     :   m_bOpened = false; return(m_bOpened);
    }

    cfsetispeed(&PortSettings, Speed);                   // Set INPUT the baud rate at 115200 bauds
    cfsetospeed(&PortSettings, Speed);                   // Set OUTPUT the baud rate at 115200 bauds

    //  [♦] BITS (c_cflag) [♦]
        // PortSettings.c_cflag |= ( CLOCAL | CREAD |  CS8);    // Configure the device : 8 bits, no parity, no control
        PortSettings.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below

        switch (in_Bits)
        {
            case 8  :
                PortSettings.c_cflag |= CS8;
                break;
            case 7  :
                PortSettings.c_cflag |= CS7;
                break;
            case 6  :
                PortSettings.c_cflag |= CS6;
                break;
            case 5  :
                PortSettings.c_cflag |= CS5;
                break;

            default:
                m_bOpened = false;
                return(m_bOpened);
        }

    //  [♦] PARITY (c_cflag) [♦]
        iParity_flag = 0;
        // Check NONE
        iResult = memcmp (in_Parity, "N", strlen("N"));
        if (iResult == 0){
            PortSettings.c_cflag &= ~PARENB;
            iParity_flag++;
        }
        // Check EVEN
        iResult = memcmp (in_Parity, "E", strlen("E"));
        if (iResult == 0){
            PortSettings.c_cflag |= PARENB;
            PortSettings.c_cflag &= ~PARODD;
            iParity_flag++;
        }
        // Check ODD
        iResult = memcmp (in_Parity, "O", strlen("O"));
        if (iResult == 0){
            PortSettings.c_cflag |= PARENB;
            PortSettings.c_cflag |= PARODD;
            iParity_flag++;
        }
        // Check if we found the Parity
        if (iParity_flag != 1){
            m_bOpened = false;
            return(m_bOpened);
        }


    //  [♦] STOP BITS (c_cflag) [♦]
        switch (in_StopBits)
        {
            case 1  :
                PortSettings.c_cflag &= ~CSTOPB; // Clear stop field, only 1 stop bit used in communication
                break;
            case 2  :
                PortSettings.c_cflag |= CSTOPB;  // Set stop field, 2 stop bits used in communication
                break;
            default:
                m_bOpened = false;
                return(m_bOpened);
        }

    //  [♦] DTR [♦]
        DTR_flag = TIOCM_DTR;
        if (in_DTR == 0){
            ioctl(fd, TIOCMBIC, &DTR_flag); //Clear RTS pin
        }
        else{
            ioctl(fd,TIOCMBIS, &DTR_flag);   //Set RTS pin
        }

    //  [♦] RTS [♦]
        RTS_flag = TIOCM_RTS;
        if (in_RTS == 0){
            ioctl(fd, TIOCMBIC, &RTS_flag); //Clear RTS pin
        }
        else{
            ioctl(fd,TIOCMBIS,&RTS_flag);   //Set RTS pin
        }

    /*
      [♦] INPUT MODES (c_iflag) [♦]
        Clearing all of the following bits disables any special handling of the bytes as they are received by the serial port, before they are passed to the application.
        We just want the raw data thanks!
    */
        //PortSettings.c_iflag |= ( IGNPAR | IGNBRK );         // input mode flags
        PortSettings.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

    /*
      [♦] OUTPUT MODES (c_oflag) [♦]
        The c_oflag member of the termios struct contains low-level settings for output processing.
        When configuring a serial port, we want to disable any special handling of output chars/bytes.
    */

        PortSettings.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
        PortSettings.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/l


    /*
      [♦] VMIN - VTIME (c_cc) [♦]
        When VMIN is 0, VTIME specifies a time-out from the start of the read() call.
        When VMIN is > 0, VTIME specifies the time-out from the start of the first received character.
        Let’s explore the different combinations:
        VMIN = 0, VTIME = 0: No blocking, return immediately with what is available
        VMIN > 0, VTIME = 0: This will make read() always wait for bytes (exactly how many is determined by VMIN), so read() could block indefinitely.
        VMIN = 0, VTIME > 0: This is a blocking read of any number of chars with a maximum timeout (given by VTIME). read() will block until either any amount of data is available, or the timeout occurs. This happens to be my favourite mode (and the one I use the most).
        VMIN > 0, VTIME > 0: Block until either VMIN characters have been received, or VTIME after first character has elapsed. Note that the timeout for VTIME does not begin until the first character is received.
        VMIN and VTIME are both defined as the type cc_t, which I have always seen be an alias for unsigned char (1 byte). This puts an upper limit on the number of VMIN characters to be 255 and the maximum timeout of 25.5 seconds (255 deciseconds).
    */

        PortSettings.c_cc[VTIME]=0;                          // Timer unused
        PortSettings.c_cc[VMIN]=0;                           // At least on character before satisfy reading

   // Save tty settings, also checking for error
    if (tcsetattr(fd, TCSANOW, &PortSettings) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));       
        return 1;
    }
    
    return 1;

然后在一条线上写着:

代码语言:javascript
复制
    while (1)
    {
        memset (ReadBuffer, 0, sizeof(ReadBuffer));
        bytes_read = read(in_fd, &ReadBuffer, sizeof(ReadBuffer));
        if (bytes_read > 0){
            memcpy (LocalBuffer+BytesCounter, ReadBuffer, bytes_read);
            BytesCounter = BytesCounter + bytes_read;
            SystemTick = GetSystem_getTick(); // Adapt your function here
        }
        if (BytesCounter == 0){
            break;
        }
        if (bytes_read == 0 && BytesCounter > 0){
            ElapsedTime = GetSystem_getTick() - SystemTick; // Adapt your function here
            if ((int)ElapsedTime > in_BytesDelay){
                break;
            }
        }
    }

    // Copy in out Buffer
    if (BytesCounter > LINUX_SERIAL_BUFFER){
        return -1;
    }
    else{
        memcpy ((unsigned char*)out_buffer, LocalBuffer, BytesCounter);
    }

您可以根据需要修改代码。

如果您想更好地测试它,我使用一个名为SerialTool www.serialtool.com的软件,它在Windows和MacOS上工作得很好。它允许您设置每个字节之间的读取超时(查看软件中某个位置的配置),并且不会丢失任何字节。至少您可以比较Windows和Linux中的两个版本。我在Ubuntu上使用它,它比FTDI USB转换器上的Windows运行得更快。看看它!

希望这对你的任务有所帮助。

我希望这能帮上忙

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74495069

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档