解决Win32下USB转串口一次只读一字节问题

最近项目遇到一个问题:从串口读数据时,如果使用 USB-Serial Adapter,那么每次读取只能读到1字节,循环读取效率特别低下。而如果是接 RS232 则不会产生这个问题。

推测这个问题产生的原因,是Win32下不同驱动程序产生 COMM 事件的时机不同。RS232是在受信缓冲区中产生一定量的数据之后,才会令API产生 EV_RXCHAR,而USB转串口的驱动,一收到数据就直接发生 EV_RXCHAR 了。

基于这个推论,再找解决办法。只要“等”一小会儿就可以了。那么等多久合适呢?
实验发现,受信缓冲区中数据的大小,其实存在ClearCommError()函数的输出参数的 COMSTAT 结构体中。
COMSTAT 的文档中说得很明确:

cbInQue

The number of bytes received by the serial provider but not yet read by a ReadFile operation.

这就好办了,只要等到这个值不发生变化,那就是收完了。
下面是代码示例,只是示意,不要照抄,编不过的。其精华就在于while。

bool MassRead(HANDLE hFile, UINT8* pBuff, UINT* pLen) {
    UINT8 tmpBuf[1024] = {0};
    DWORD dwRead(0);
    DOWRD dwIn(0);
    DWORD Err(0);
    COMSTAT cs;
    
    ClearCommError(h_drv, &Err, &cs);
    while(dwIn < cs.cbInQue &#038;&#038; cs.cbInQue < 500)//一般串口的受信buff大小是512,避免缓冲区溢出。
    {
        dwIn = cs.cbInQue;
        ::Sleep(10);
        ClearCommError(h_drv, &#038;Err, &#038;cs);
    }
    
    bool fResult = ReadFile (hFile, tmpBuf, sizeof(tmpBuf), &#038;dwRead, 0);
    if (fResult&#038;&#038;dwRead) {
        memcpy_s(pBuff, dwRead, tmpBuf, dwRead);
        *pLen = dwRead;
    }
    return fResult;
}

DWORD dwCommModemStatus(0);
SetCommMask (h_drv, EV_RXCHAR | EV_ERR);
WaitCommEvent (h_drv, &#038;dwCommModemStatus, 0);

if (dwCommModemStatus&#038;EV_RXCHAR) 
    MassRead (h_drv, read_buf, sizeof(read_buf), &#038;len);

已有4条评论

  1. 我怎么感觉你之前写过类似的.
    话说这个又该怎么理解?是目前的USB-RS232通用驱动有问题?

    1. 不能算驱动有问题。驱动可以有不同的理解。写代码的时候把现象统一了就行。
      我们这也是在连USB这头的时候收长数据出现了超时才发现的。

    2. 之前写过一个基板上的USB切断未识别的问题,那个驱动完全是自己写的。
      这个封了好几层了,是WindowAPI的调用。

      1. 跟 USB-RS232 这玩意打交道的经历我大概能写出相当长一篇文章出来。有一段时间完全想自己买一根拿来用,但是真的当我可以买的时候,使用场景已经完全不存在了。

        1. 调基板的时候使用串口的场景还挺多的。PC跟PC间确实很少用了。

          1. 最初摸RS232是2003年前后吧,当时的文曲星只能靠串口传数据。
            第二次摸是2011年,在电信,接交换机要用串口。
            后来是到北京,网康的设备装系统需要用串口。
            再后来遇到的几个公司就都很傻逼,买的硬件虽然都带串口,但是员工装系统都要抱着显示器才能装,有时还要拆机箱插显卡。多数员工还不会在命令行下配IP,所以还要装图形界面。
            等到我真正回归到正常公司时,IPMI 已经面世了,甭说RS232,连USB口都可以不用就能搞定一切。

  2. 学习了,感谢

你好,新朋友。留言前请先填写昵称邮箱