繁体   English   中英

具有最小延迟的串行通信

[英]Serial communication with minimal delay

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

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

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

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

客户端设备使用 ReadFile 从主机设备读取数据。 客户端读取完长度已知的完整数据包后,使用 WriteFile 将相应的数据包回复给主机设备。 读和写总是顺序的,没有并发。

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

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

  1. 使用 FILE_FLAG_NO_BUFFERING 标志调用 CreateFile? 我不确定这个标志在这种情况下是否相关。
  2. 在每个 WriteFile 之后调用 FlushFileBuffers? 或任何可以通知/中断串口立即传输数据的动作?
  3. 为处理串行通信的线程和进程设置更高的优先级
  4. 为模拟设备(及其驱动程序)设置延迟计时器或传输大小。 但是物理串口呢?
  5. Windows 上有什么类似的东西,比如 Linux 下的 setserial/low_latency?
  6. 禁用先进先出?

提前致谢!

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

经过多年的努力,在这一天,我终于能够使用 Microsoft 的 CDC 类 USB UART 驱动程序(USBSER.SYS,现在内置在 Windows 10 中使其真正可用)使我的串行通信终端变得足够快。

显然,前面提到的一组值是一个特殊的值,它设置了最小的超时和最小的延迟(至少对于 Microsoft 驱动程序,或者在我看来是这样)并且如果接收中没有新字符,也会导致 ReadFile 立即返回缓冲。

这是我的代码(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;
}

这是它工作的 USB 跟踪。 请注意 OUT 事务(输出字节),然后是 IN 事务(输入字节),然后是更多 OUT 事务(输出字节),所有这些都在 3 毫秒内:

具有最少超时的 USB 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++;
    }

大型传输在代码中的其他地方处理。

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

这是我找到相关信息的地方: http : //www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf

我遇到过类似的串口问题。 就我而言,我解决了降低串行端口延迟的问题。 您可以使用控制面板更改每个端口的延迟(默认设置为 16 毫秒)。 您可以在此处找到该方法: http : //www.chipkin.com/reducing-latency-on-com-ports/

祝你好运!!!

暂无
暂无

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

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