串行通信,延迟最小

时间:2013-04-01 21:32:10

标签: windows winapi serial-port hardware low-latency

我有一台通过串行通讯与外部设备连接的计算机(即物理或仿真串口的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. 禁用FIFO?
  7. 提前感谢!

2 个答案:

答案 0 :(得分:1)

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

祝你好运!!!

答案 1 :(得分:1)

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

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

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

这是我的代码(Visual C ++ 2008,项目字符集从“ Unicode”更改为“ Not set”,以避免端口名的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事务(输入字节),然后是3毫秒内所有OUT事务(输出字节):

USB UART packet trace with minimal timeouts

最后,由于您正在阅读本文,因此您可能会对我的函数通过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代码。O,当您玩得开心的时候,时间不会流逝。

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