出处:http://blog.csdn.net/firehood_/article/details/6195558
现在的车载和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大侠无私的奉献。
出处:http://blog.csdn.net/lovelynn/article/details/4466215
我们有一个GPS模块连接在COM3上,现在有两个应用程序都需要读取COM3的内容,然而WinCE的串口为独占式的串口,因此我们需要一个驱动程序,将COM3虚拟成COM4和COM5来供应用程序使用。下面我来介绍一下驱动程序的设计。
HANDLE hRes = RegisterDevice (L"COM", VirComNO, L"GPSCOM.dll", (DWORD) VirComNO);
COM_Init(
ULONG Identifier
)
{
PHW_INDEP_INFO pSerialHead = NULL;
// Allocate our control structure.
pSerialHead = (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));
pSerialHead->pAccessOwner = NULL;
//add com Identifier
if(5==Identifier)
{
RETAILMSG(DEBUG_COM2,(L" PLATFORM fwq COM_init5 /r/n"));
pSerialHead->COMNUM = 5;
g_pCircleBuffer5 =CP_CreateCircleBuffer(8192);
}
//如果我们创建的是COM4这个设备,那么把COM4的相关信息记录在pSerialHead中。
if(4==Identifier)
{
RETAILMSG(DEBUG_COM2,(L" PLATFORM fwq COM_init 4 /r/n"));
pSerialHead->COMNUM = 4;
// init circlebuffer for com4
g_pCircleBuffer4 =CP_CreateCircleBuffer(8192);
}
......
}
COM_Open(
HANDLE pHead, // @parm Handle returned by COM_Init.
DWORD AccessCode, // @parm access code.
DWORD ShareMode // @parm share mode - Not used in this driver.
)
{
RETAILMSG(DEBUG_COM2,(L" PLATFORM fwq COM_Open /r/n"));
PHW_INDEP_INFO pSerialHead = (PHW_INDEP_INFO)pHead;
PHW_OPEN_INFO pOpenHead = NULL;
// 为pOpenHead分配空间,这个空间内用来存放,打开设备的一些信息,比如运行时的状态等都可以存储在此空间内。
pOpenHead = (PHW_OPEN_INFO)LocalAlloc(LPTR, sizeof(HW_OPEN_INFO));
RETAILMSG(DEBUG_COM,(L"PLATFORM **()()()** pOpenHead=%d ",pOpenHead));
if ( !pOpenHead ) {
DEBUGMSG( DEBUG_COM,
(TEXT(" PLATFORMError allocating memory for pOpenHead, COM_Open failed/n/r")));
return(NULL);
}
// Init the structure
pOpenHead->pSerialHead = pSerialHead;
......
//InitializeCriticalSection(&(pOpenHead->CommEvents.EventCS));
EnterCriticalSection(&g_csOpen);
if(g_uiOpenCount != 0)
{
goto SET_SUCCEED_FLAG;
}
BOOL res=FALSE;
res = g_SerialPort.Open(3,4800);
if(res == FALSE )
{
RETAILMSG(DEBUG_COM,(TEXT("Failed to map 3/r/n")));
goto CleanUp;
}
else
{
RETAILMSG(DEBUG_COM,(TEXT("Succeed to map to 3/r/n")));
}
g_hReadEvent4 = CreateEvent(NULL,FALSE,FALSE,L"WaitCommGPS4");
SET_SUCCEED_FLAG:
if(pSerialHead->COMNUM == 4)
{
RETAILMSG(DEBUG_COM3,(L" PLATFORM open com4 /r/n"));
g_ComOpenFlag4 =1;
pSerialHead->COMOpenFlag =1;
}
if(pSerialHead->COMNUM == 5)
{
RETAILMSG(DEBUG_COM3,(L" PLATFORM open com5 /r/n"));
g_ComOpenFlag5 =1;
pSerialHead->COMOpenFlag =1;
}
// 记录串口3被打开的次数
g_uiOpenCount ++;
LeaveCriticalSection(&g_csOpen);
/////////////////////////////////////////////////////////////////////////////
return(pOpenHead);
CleanUp:
LeaveCriticalSection(&g_csOpen);
RETAILMSG(DEBUG_COM,(L" PLATFORM readqueue faild and exit COM_Open/r/n"));
return(NULL);
}
COM_IOControl(PHW_OPEN_INFO pOpenHead,
DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut){
......
PHW_INDEP_INFO pSerialHead= pOpenHead->pSerialHead;
switch ( dwCode ) {
case IOCTL_SERIAL_WAIT_ON_MASK :
// 应用程序调用WaitCommEvent()函数会进入这个分支
if(4 == pSerialHead->COMNUM)
{
WaitCommEvent4(pOpenHead, (DWORD *)pBufOut, NULL);
}
if(5 == pSerialHead->COMNUM)
{
WaitCommEvent5(pOpenHead, (DWORD *)pBufOut, NULL);
}
*pdwActualOut = sizeof(DWORD);
break;
default :
RETAILMSG (DEBUG_COM, (TEXT(" PLATFORM Invalid ioctl %d/r/n"), dwCode));
break;
}
......
return TRUE;
}
{
RETAILMSG(DEBUG_CODE,(L"ThreadReadCOM/r/n"));
DWORD dwCommModemStatus = 0;
BOOL bRet = FALSE;
int pdwBytesRead =0;
BYTE pBuffer [1024];
_try
{
while(INVALID_HANDLE_VALUE != g_hComFile)
{
//RETAILMSG(DEBUG_CODE,(L"COM1 Wait Comm Event/r/n"));
SetCommMask(g_hComFile,EV_RXCHAR);
WaitCommEvent (g_hComFile,&dwCommModemStatus,0);
//RETAILMSG(DEBUG_CODE,(L"第%d次收到数据:/r/n",count));
bRet = ReadFile(g_hComFile,pBuffer,128,(LPDWORD)&pdwBytesRead,0);
if(1 == g_ComOpenFlag4)
{
SetEvent(g_hReadEvent4);
g_pCircleBuffer4->Write(g_pCircleBuffer4,pBuffer,pdwBytesRead);
}
if(1 == g_ComOpenFlag5)
{
SetEvent(g_hReadEvent5);
g_pCircleBuffer5->Write(g_pCircleBuffer5,pBuffer,pdwBytesRead);
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
RETAILMSG(DEBUG_CODE,(L"Exception! call Serial Thread/r/n"));
}
RETAILMSG(DEBUG_CODE,(L"readcom Thread exit/r/n"));
return 1;
}
COM_Read(
HANDLE pHead, //@parm [IN] HANDLE returned by COM_Open
PUCHAR pTargetBuffer, //@parm [IN,OUT] Pointer to valid memory.
ULONG BufferLength //@parm [IN] Size in bytes of pTargetBuffer.
)
{
PHW_OPEN_INFO pOpenHead = (PHW_OPEN_INFO)pHead;
PHW_INDEP_INFO pSerialHead= pOpenHead->pSerialHead;
ULONG BytesRead = 0;
......
if(4 ==pSerialHead->COMNUM)
{
g_pCircleBuffer4->Read(g_pCircleBuffer4,pTargetBuffer,BufferLength,(unsigned int*)&BytesRead);
return BytesRead;
}
if(5 ==pSerialHead->COMNUM)
{
g_pCircleBuffer5->Read(g_pCircleBuffer5,pTargetBuffer,BufferLength,(unsigned int*)&BytesRead);
return BytesRead;
}
return -1;
}