我有一台通过串行通信(即物理或仿真串行端口的RS-232/RS-422)与外部设备连接的计算机。它们之间通过频繁的数据交换(30Hz)进行通信,但只有很小的数据包(每个数据包少于16个字节)。
通信的最关键要求是低等待时间或发送和接收之间的延迟。
数据交换模式类似于握手。一个主机设备启动通信,并继续在客户端设备上发送通知。客户端设备需要尽可能快地答复来自主机设备的每个通知(这正是需要实现低延迟的地方)。通知和回复的数据包定义良好;即数据长度是已知的。
并且基本上不允许数据丢失。
我使用以下常见的Win API函数以同步方式进行I/O读/写:
CreateFile,ReadFile,WriteFile
客户端设备使用ReadFile从主机设备读取数据。客户端读取完已知长度的完整数据包后,便使用WriteFile用相应的数据包答复主机设备。读取和写入始终是连续的,没有并发性。
通讯速度不够快。即,数据发送和接收之间的时间间隔太长。我想这可能是串行端口缓冲或中断的问题。
在这里,我总结了一些可以改善延迟的可能措施。
请给我一些建议和更正:)
提前致谢!
最佳答案
在我的情况下,我通过将comm超时设置为{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事务(输出字节):
最后,由于您正在阅读本文,因此您可能会对我通过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
关于windows - 串行通讯,延迟最小,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15752272/