简体   繁体   English

具有最小延迟的串行通信

[英]Serial communication with minimal delay

I have a computer which is connected with external devices via serial communication (ie RS-232/RS-422 of physical or emulated serial ports).我有一台通过串行通信(即物理或仿真串行端口的 RS-232/RS-422)与外部设备连接的计算机。 They communicate with each other by frequent data exchange (30Hz) but with only small data packet (less than 16 bytes for each packet).它们通过频繁的数据交换(30Hz)相互通信,但只有小数据包(每个数据包小于 16 字节)。

The most critical requirement of the communication is low latency or delay between transmitting and receiving.通信最关键的要求是传输和接收之间的低延迟或延迟。

The data exchange pattern is handshake-like.数据交换模式类似于握手。 One host device initiates communication and keeps sending notification on a client device.一台主机设备发起通信并不断在客户端设备上发送通知。 A client device needs to reply every notification from the host device as quick as possible (this is exactly where the low latency needs to be achieved).客户端设备需要尽快回复来自主机设备的每个通知(这正是需要实现低延迟的地方)。 The data packets of notifications and replies are well defined;通知和回复的数据包定义明确; namely the data length is known.即数据长度是已知的。 And basically data loss is not allowed.并且基本上不允许数据丢失。

I have used following common Win API functions to do the I/O read/write in a synchronous manner: CreateFile, ReadFile, WriteFile我使用以下常见的 Win API 函数以同步方式进行 I/O 读/写:CreateFile、ReadFile、WriteFile

A client device uses ReadFile to read data from a host device.客户端设备使用 ReadFile 从主机设备读取数据。 Once the client reads the complete data packet whose length is known, it uses WriteFile to reply the host device with according data packet.客户端读取完长度已知的完整数据包后,使用 WriteFile 将相应的数据包回复给主机设备。 The reads and writes are always sequential without concurrency.读和写总是顺序的,没有并发。

Somehow the communication is not fast enough.不知何故,通信速度不够快。 Namely the time duration between data sending and receiving takes too long.即数据发送和接收之间的时间持续时间太长。 I guess that it could be a problem with serial port buffering or interrupts.我想这可能是串行端口缓冲或中断的问题。

Here I summarize some possible actions to improve the delay.这里我总结了一些可能的措施来改善延迟。 Please give me some suggestions and corrections :)请给我一些建议和更正:)

  1. call CreateFile with FILE_FLAG_NO_BUFFERING flag?使用 FILE_FLAG_NO_BUFFERING 标志调用 CreateFile? I am not sure if this flag is relevant in this context.我不确定这个标志在这种情况下是否相关。
  2. call FlushFileBuffers after each WriteFile?在每个 WriteFile 之后调用 FlushFileBuffers? or any action which can notify/interrupt serial port to immediately transmit data?或任何可以通知/中断串口立即传输数据的动作?
  3. set higher priority for thread and process which handling serial communication为处理串行通信的线程和进程设置更高的优先级
  4. set latency timer or transfer size for emulated devices (with their driver).为模拟设备(及其驱动程序)设置延迟计时器或传输大小。 But how about the physical serial port?但是物理串口呢?
  5. any equivalent stuff on Windows like setserial/low_latency under Linux? Windows 上有什么类似的东西,比如 Linux 下的 setserial/low_latency?
  6. disable FIFO?禁用先进先出?

thanks in advance!提前致谢!

I solved this in my case by setting the comm timeouts to {MAXDWORD,0,0,0,0} .我通过将通信超时设置为{MAXDWORD,0,0,0,0}解决了这个问题。

After years of struggling this, on this very day I finally was able to make my serial comms terminal thingy fast enough with Microsoft's CDC class USB UART driver (USBSER.SYS, which is now built in in Windows 10 making it actually usable).经过多年的努力,在这一天,我终于能够使用 Microsoft 的 CDC 类 USB UART 驱动程序(USBSER.SYS,现在内置在 Windows 10 中使其真正可用)使我的串行通信终端变得足够快。

Apparently the aforementioned set of values is a special value that sets minimal timeouts as well as minimal latency (at least with the Microsoft driver, or so it seems to me anyway) and also causes ReadFile to return immediately if no new characters are in the receive buffer.显然,前面提到的一组值是一个特殊的值,它设置了最小的超时和最小的延迟(至少对于 Microsoft 驱动程序,或者在我看来是这样)并且如果接收中没有新字符,也会导致 ReadFile 立即返回缓冲。

Here's my code (Visual C++ 2008, project character set changed from "Unicode" to "Not set" to avoid LPCWSTR type cast problem of portname) to open the port:这是我的代码(Visual C++ 2008,项目字符集从“Unicode”更改为“未设置”以避免端口名的 LPCWSTR 类型转换问题)打开端口:

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\n",portname);
        return false;
    }
    if(!GetCommTimeouts(port,&originalTimeouts)) {
        printf("Cannot get comm timeouts\n");
        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\n");
        return false;
    }
    printf("Successfully set COM parameters\n");
    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;
}

And here's a USB trace of it working.这是它工作的 USB 跟踪。 Please note the OUT transactions (output bytes) followed by IN transactions (input bytes) and then more OUT transactions (output bytes) all within 3 milliseconds:请注意 OUT 事务(输出字节),然后是 IN 事务(输入字节),然后是更多 OUT 事务(输出字节),所有这些都在 3 毫秒内:

具有最少超时的 USB UART 数据包跟踪

And finally, since if you are reading this, you might be interested to see my function that sends and receives characters over the UART:最后,如果您正在阅读本文,您可能有兴趣查看我通过 UART 发送和接收字符的函数:

    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++;
    }

Large transfers are handled elsewhere in code.大型传输在代码中的其他地方处理。

On a final note, apparently this is the first fast UART code I've managed to write since abandoning DOS in 1998. O, doest the time fly when thou art having fun.最后一点,显然这是自 1998 年放弃 DOS 以来我设法编写的第一个快速 UART 代码。哦,当你玩得开心时,时间会飞逝吗?

This is where I found the relevant information: http://www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf这是我找到相关信息的地方: http : //www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf

I have experienced similar problem with serial port.我遇到过类似的串口问题。 In my case I resolved the problem decreasing the latency of the serial port.就我而言,我解决了降低串行端口延迟的问题。 You can change the latency of every port (which by default is set to 16ms) using control panel.您可以使用控制面板更改每个端口的延迟(默认设置为 16 毫秒)。 You can find the method here: http://www.chipkin.com/reducing-latency-on-com-ports/您可以在此处找到该方法: http : //www.chipkin.com/reducing-latency-on-com-ports/

Good Luck!!!祝你好运!!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM