WINCE下虚拟串口驱动设计

    技术2026-08-09  5

           现在的车载和PND设备都有自动校正系统时间的功能,实现方法一般是通过GPS较时(当然对于有CMMB模块的设备也可以通过CMMB校时)。 但由于串口设备是一个独占设备,GPS串口不能同时被导航软件和校时程序使用。如果此时导航软件正在运行,GPS校时程序是无法访问GPS串口的。

           在这样的情况下,我们就需要创建一个非独占性质的串口设备,将一个物理串口虚拟成多个串口来使用。 虚拟串口驱动通过打开物理串口并监听物理串口事件,当物理串口有数据过来是将数据保存到缓存中供多个不同的虚拟串口使用。这样通过虚拟的方式实现了同一物理串口在多个程序之间的数据共享。

           基于上述思想,采用了如下设计。(这里设定驱动的前缀为COM,与物理串口驱动的前缀保持一致,这样应用层可以像访问物理串口驱动一样访问虚拟串口驱动。)

            1. 在COM_Open()函数中打开物理串口,并创建线程MonitorCommEventProc用于监听并处理物理串口数据。

            2. 定义两个Buffer,分别保存两个虚拟串口的数据信息,Buffer的设计采用循环队列的方式。

            3. 在监听线程MonitorCommEventProc中监听物理串口事件,当物理串口有数据过来时读取数据并将数据分别保存到两个Buffer里。

            4. 在COM_Read()函数中实现读取Buffer中的数据。

            5. 在COM_IOControl()函数中实现相应控制码接口。如IOCTL_SERIAL_SET_WAIT_MASK,IOCTL_SERIAL_WAIT_ON_MASK、IOCTL_SERIAL_GET_COMMSTATUS、IOCTL_SERIAL_PURGE等。

    MonitorCommEventProc监听线程参考代码:

    //we use this program to always read the physical serial data DWORD MonitorCommEventProc(LPVOID pParam) { InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),TRUE); unsigned char vtBufRead[READ_BUFFER_LENGTH]; while(TRUE) { if(g_bExitMonitorProc != FALSE) { RETAILMSG(TRUE,(TEXT("MonitorCommEventProc Stop!/r/n"))); //if com is closed,we will stop to read break; } DWORD dwEvtMask = 0; SetCommMask (g_hCom, g_dwWaitMask | EV_RXCHAR); // wait the physical serial event if(WaitCommEvent(g_hCom,&dwEvtMask,NULL)&&(dwEvtMask & EV_RXCHAR)) { SetCommMask (g_hCom, g_dwWaitMask | EV_RXCHAR); { COMSTAT cmState; DWORD dwReadErrors; ClearCommError(g_hCom,&dwReadErrors,&cmState); DWORD willReadLen = cmState.cbInQue ; if (willReadLen <= 0) continue; DWORD dwRead = 0; ReadFile(g_hCom,vtBufRead,willReadLen,&dwRead,NULL); EnterCriticalSection(&g_csRead1); EnterCriticalSection(&g_csRead2); if(g_bComState1==COM_STATUS_OPEN) { WriteBuffer(g_ucDateBuf1,READ_BUFFER_LENGTH,vtBufRead,dwRead, &g_dwDateBufHead1,&g_dwDateBufTail1); } if(g_bComState2==COM_STATUS_OPEN) { WriteBuffer(g_ucDateBuf2,READ_BUFFER_LENGTH,vtBufRead,dwRead, &g_dwDateBufHead2,&g_dwDateBufTail2); } LeaveCriticalSection(&g_csRead2); LeaveCriticalSection(&g_csRead1); InterlockedExchange(reinterpret_cast<LONG *>(&g_dwEvtMask),dwEvtMask); PulseEvent(g_hCanReadEvent); printf("PulseEvent g_hCanReadEvent.../n"); // sleep for other thread to respond to the event,very important.. Sleep(1); } } InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),FALSE); return 0; }

     

            COM_IOControl()函数多数控制码的实现可以直接调用物理串口DeviceIoControl()来处理,但有些控制码需要自己实现,比如清空缓存,不能直接调用DeviceIoControl()来清空物理串口的数据,而是应该清空虚拟串口Buffer中的数据。几个重要控制码实现的参考代码:

    BOOL COM_IOControl(HANDLE dwHandle, DWORD dwIoControlCode, PBYTE pBufIn, DWORD dwBufInSize, PBYTE pBufOut, DWORD dwBufOutSize, PDWORD pBytesReturned) { switch(dwIoControlCode) { case IOCTL_SERIAL_SET_WAIT_MASK: //printf("Serial Command: SERIAL_SET_WAIT_MASK/n"); if(g_uiOpenCount==1) { g_dwWaitMask = *reinterpret_cast<DWORD *>(pBufIn); return DeviceIoControl(g_hCom, IOCTL_SERIAL_SET_WAIT_MASK,pBufIn, dwBufInSize,pBufOut,dwBufOutSize,pBytesReturned,NULL); } else return TRUE; case IOCTL_SERIAL_WAIT_ON_MASK: { PVSP_INFO pOpenHead = (PVSP_INFO) dwHandle; // return immediately if the buffer has the available data if(*(pOpenHead->BufferHead)!=*(pOpenHead->BufferTail)) return TRUE; if(dwBufOutSize < sizeof(DWORD) || WaitForSingleObject(g_hCanReadEvent,INFINITE) == WAIT_TIMEOUT) { *pBytesReturned = 0; return FALSE; } else { InterlockedExchange(reinterpret_cast<LONG *>(pBufOut),g_dwEvtMask); *pBytesReturned = sizeof(DWORD); return TRUE; } } case IOCTL_SERIAL_PURGE: { //clean the virtual serial buffer PVSP_INFO pOpenHead = (PVSP_INFO) dwHandle; *(pOpenHead->BufferHead)=0; *(pOpenHead->BufferTail)=0; return TRUE; } ..... } return FALSE; }

     

    Buffer(循环队列)读写操作参考代码:

    void WriteBuffer(PUCHAR targetBuffer,DWORD LongOfTarget,PUCHAR sourceBuffer, DWORD NumInSource,DWORD *BufferHead,DWORD *BufferTail) { BOOL ChangeHead = FALSE; DWORD i=*BufferTail; DWORD j=0; if(NumInSource >= LongOfTarget) { memcpy(targetBuffer,sourceBuffer,LongOfTarget); *BufferHead=0; *BufferTail=LongOfTarget-1; return; } else { for(;j<NumInSource;j++) { targetBuffer[i++]=sourceBuffer[j]; if(i>=LongOfTarget) { i=0; } if(i==(*BufferHead)) { ChangeHead=TRUE; } } if(ChangeHead==FALSE) { *BufferTail=i; return; } else { *BufferTail=i; *BufferHead=i+1; return; } } } DWORD ReadBuffer(PUCHAR targetBuffer,PUCHAR sourceBuffer,DWORD SizeOfTargeBuf, DWORD LongOfSourceBuf,DWORD *BufferHead,DWORD *BufferTail) { DWORD i=0; DWORD j=*BufferHead; BOOL IsEmpty=FALSE; for(i=0;i<SizeOfTargeBuf;i++) { if(j==(*BufferTail)) { IsEmpty=TRUE; break; } targetBuffer[i]=sourceBuffer[j++]; if(j>=LongOfSourceBuf) { j=0; } } if(IsEmpty==FALSE) { *BufferHead=j; } else { (*BufferHead)=(*BufferTail)=0; } return i; } 

        参考资料:《WinCE虚拟串口驱动》http://blog.csdn.net/norains/archive/2009/03/28/4032257.aspx  作者:norains 感谢 norains大侠无私的奉献。

    最新回复(0)