关于Windows:以最小的延迟进行串行通信

Serial communication with minimal delay

我有一台通过串行通信(即物理或仿真串行端口的RS-232 / RS-422)与外部设备连接的计算机。它们之间通过频繁的数据交换(30Hz)进行通信,但只有很小的数据包(每个数据包少于16个字节)。

通信的最关键要求是低延迟或发送和接收之间的延迟。

数据交换模式类似于握手。一个主机设备启动通信,并继续在客户端设备上发送通知。客户端设备需要尽快回复来自主机设备的每个通知(这正是需要实现低延迟的地方)。通知和回复的数据包定义明确;即数据长度是已知的。
并且基本上不允许数据丢失。

我使用以下常见的Win API函数以同步方式进行I / O读/写:
CreateFile,ReadFile,WriteFile

客户端设备使用ReadFile从主机设备读取数据。客户端读取完长度已知的完整数据包后,便使用WriteFile用相应的数据包答复主机设备。读取和写入始终是连续的,没有并发性。

通讯速度不够快。即数据发送和接收之间的时间间隔太长。我想这可能是串行端口缓冲或中断的问题。

在这里,我总结了一些可以改善延迟的可能措施。
请给我一些建议和更正:)

  • 调用带有FILE_FLAG_NO_BUFFERING标志的CreateFile?我不确定此标志在此情况下是否相关。
  • 在每个WriteFile之后调用FlushFileBuffers?或可以通知/中断串行端口立即传输数据的任何操作?
  • 为处理串行通信的线程和进程设置更高的优先级
  • 为仿真设备(及其驱动程序)设置等待时间计时器或传输大小。但是物理串口呢?
  • Windows上是否有任何等效的东西,例如Linux下的setserial / low_latency?
  • 禁用FIFO?
  • 提前致谢!


    我通过将通讯超时设置为{MAXDWORD,0,0,0,0}解决了这个问题。

    经过多年的努力,直到今天,我终于能够使用Microsoft的CDC级USB UART驱动程序(USBSER.SYS,它现在已在Windows 10中内置,因此可以实际使用)使串行通讯终端足够快。

    显然,上述值的集合是一个特殊值,它设置了最小的超时和最小的延迟(至少使用Microsoft驱动程序,或者对我来说还是如此),并且如果接收中没有新字符,也会导致ReadFile立即返回缓冲。

    这是我的代码(Visual C ++ 2008,项目字符集从" Unicode"更改为" Not set",以避免端口名的LPCWSTR类型转换问题)打开端口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    static HANDLE port=0;
    static COMMTIMEOUTS originalTimeouts;

    static bool OpenComPort(char* p,int targetSpeed) { // e.g. OpenComPort ("COM7",115200);
        char portname[16];
        sprintf(portname,"\\\\\\\\.\\\\%s",p);
        port=CreateFile(portname,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
        if(!port) {
            printf("COM port is not valid: %s\
    ",portname);
            return false;
        }
        if(!GetCommTimeouts(port,&originalTimeouts)) {
            printf("Cannot get comm timeouts\
    ");
            return false;
        }
        COMMTIMEOUTS newTimeouts={MAXDWORD,0,0,0,0};
        SetCommTimeouts(port,&newTimeouts);
        if(!ComSetParams(port,targetSpeed)) {
            SetCommTimeouts(port,&originalTimeouts);
            CloseHandle(port);
            printf("Failed to set COM parameters\
    ");
            return false;
        }
        printf("Successfully set COM parameters\
    ");
        return true;
    }

    static bool ComSetParams(HANDLE port,int baud) {
        DCB dcb;
        memset(&dcb,0,sizeof(dcb));
        dcb.DCBlength=sizeof(dcb);
        dcb.BaudRate=baud;
        dcb.fBinary=1;
        dcb.Parity=NOPARITY;
        dcb.StopBits=ONESTOPBIT;
        dcb.ByteSize=8;
        return SetCommState(port,&dcb)!=0;
    }

    这是USB工作的痕迹。请注意OUT事务(输出字节),然后是IN事务(输入字节),然后是3毫秒内的更多OUT事务(输出字节):

    USB UART packet trace with minimal timeouts

    最后,由于您正在阅读本文,因此您可能会对我通过UART发送和接收字符的函数感兴趣:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        unsigned char outbuf[16384];
        unsigned char inbuf[16384];
        unsigned char *inLast = inbuf;
        unsigned char *inP = inbuf;
        unsigned long bytesWritten;
        unsigned long bytesReceived;

        // Read character from UART and while doing that, send keypresses to UART.
        unsigned char vgetc() {
            while (inP >= inLast) { //My input buffer is empty, try to read from UART
                while (_kbhit()) { //If keyboard input available, send it to UART
                    outbuf[0] = _getch(); //Get keyboard character
                    WriteFile(port,outbuf,1,&bytesWritten,NULL); //send keychar to UART
                }
                ReadFile(port,inbuf,1024,&bytesReceived,NULL);
                inP = inbuf;
                inLast = &inbuf[bytesReceived];
            }
            return *inP++;
        }

    大额转账在代码中的其他地方处理。

    最后一点,显然,这是自1998年放弃DOS以来我设法编写的第一个快速UART代码。O,当您玩得开心时,时光飞逝。

    我在这里找到了相关信息:http://www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf


    我在串行端口上遇到了类似的问题。
    就我而言,我解决了减少串行端口延迟的问题。
    您可以使用控制面板更改每个端口的延迟(默认情况下设置为16ms)。
    您可以在这里找到方法:
    http://www.chipkin.com/reducing-latency-on-com-ports/

    祝好运!!!