当前位置: 代码迷 >> WinCE >> winCE中兑现虚拟串口的方法
  详细解决方案

winCE中兑现虚拟串口的方法

热度:73   发布时间:2016-04-28 12:55:36.0
winCE中实现虚拟串口的方法

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家拍砖!

 

环境:wince6.0+ARM+Freescell

 

一、目的

设计一个读GPS串口数据的驱动,并注册为COM口。

二、实现过程

1、COM_Init

注册表中读出GPS COM口号及是否Debug,创建共享数据区,创建读取数据的线程,打开GPS真实的COM口;

开一线程MonitorCommEventProc读取GPS口数据,并用DecodeGpsBuf解析读取到的数据。

这里注意在WinCE驱动中的XXX_Init中的入口参数DWORD dwContext通过

LPCTSTR regKeyPath = (LPCTSTR)dwContext;HKEY hKey = OpenDeviceKey(regKeyPath);可以转化为其所在驱动的.reg注册的HKEY。

 

DWORD COM_Init(DWORD dwContext)   {   	DWORD dwPare,dwReturn;	LPCTSTR regKeyPath = (LPCTSTR)dwContext;	HKEY hKey = OpenDeviceKey(regKeyPath);	DWORD size = 4;	DWORD index;	MyPDebugStringW(L"Enter Com_Init");	if ( hKey == NULL )	{		MyPDebugStringW(L"Fun:%s; hKey == NULL", __FUNCTIONW__);		//return NULL;	}	if (ERROR_SUCCESS != RegQueryValueEx(hKey,_T("GpsCom"), 0, NULL, (LPBYTE)&g_GsmPort, &size))	{	//	RETAILMSG(TRUE,(TEXT("We get original com index :%d ......\r\n"),g_GsmPort));	}	if (ERROR_SUCCESS != RegQueryValueEx(hKey,_T("Dbg"), 0, NULL, (LPBYTE)&g_bDbg, &size))	{	//	RETAILMSG(TRUE,(TEXT("We get original com index :%d ......\r\n"),g_GsmPort));	}#if 0	if (ERROR_SUCCESS != RegQueryValueEx(hKey,_T("Index"), 0, NULL, (LPBYTE)&index, &size))	{		RegCloseKey(hKey);		return NULL;	}	GetFromVirtalRegistry((BYTE *)&g_GsmPort,&size, NULL, HKEY_LOCAL_MACHINE, _T("virtalSerial"), _T("Index"));#endif	RegCloseKey(hKey);	MyPDebugStringW(L"The apply port is com%d", g_GsmPort);	InitializeCriticalSection(&g_csOpen);   	InitializeCriticalSection(&g_csRead);   	InitializeCriticalSection(&g_csWrite);   		g_dwWM_PEKSUNNY  = RegisterWindowMessage(L"WM_PEKSUNNY");	if(!g_hEventComm)		g_hEventComm = CreateEvent(NULL,TRUE,FALSE,NULL);   #if 0	//给GSM模块上电	dwPare=(0<<16)|(21<<8)|(1);	KernelIoControl(IOCTL_HAL_SET_GPIO_HIGH_LOW,&dwPare,sizeof(DWORD),NULL,0,&dwReturn);	dwPare=(0<<16)|(26<<8)|(1);	KernelIoControl(IOCTL_HAL_SET_GPIO_HIGH_LOW,&dwPare,sizeof(DWORD),NULL,0,&dwReturn);#endif	g_pGpsDataMem = (PGPSDATA)ReadShareArea();	g_pGpsDataMem->iSatNum = 67;	return OpenTransmitThr();  }   
BOOL OpenTransmitThr(){	TCHAR szPort[12];	swprintf(szPort, L"COM%u:", g_GsmPort);	g_hCom = CreateFile(szPort, GENERIC_READ | GENERIC_WRITE , 0, NULL, OPEN_EXISTING, 0, NULL);	if(g_hCom == INVALID_HANDLE_VALUE )   	{   		MyPDebugStringW(L"Fun:%s; CreateFile %s failed", __FUNCTIONW__, szPort);  		return FALSE;   	}   	else  	{   		MyPDebugStringW(L"Fun:%s; CreateFile %s success", __FUNCTIONW__, szPort);   	} #if 1	if(!InitComDcb(g_hCom,CBR_9600))	{		MyPDebugStringW(L"Fun:%s; Line:%d; InitComDcb failed", __FUNCTIONW__, __LINE__);	}		//goto SET_SUCCEED_FLAG;	if(!InitComTimeOuts(g_hCom))	{		MyPDebugStringW(L"Fun:%s; Line:%d; InitComTimeOuts failed", __FUNCTIONW__, __LINE__);	}		//goto SET_SUCCEED_FLAG;	SetupComm(g_hCom, 1024,1024);	PurgeComm(g_hCom, PURGE_TXCLEAR|PURGE_RXCLEAR|PURGE_RXABORT|PURGE_TXABORT);	//设置端口上指定信号的状态	// SETDTR: 发送DTR (data terminal ready)信号	// SETRTS: 发送RTS (request to send)信号	EscapeCommFunction(g_hCom,SETDTR);	EscapeCommFunction(g_hCom,SETRTS);#endif	InterlockedExchange((LPLONG)(&g_bExitMonitorProc),FALSE);   	g_hReadThread =CreateThread(NULL,NULL,MonitorCommEventProc,NULL,NULL,NULL); 	return TRUE;}



 

DWORD MonitorCommEventProc(LPVOID pParam)   {   	TCHAR wcDebug[1024] = {0};	MyPDebugStringW(L"Enter MonitorCommEventProc");	SetCommMask(g_hCom, EV_RXCHAR);	while(!g_bExitMonitorProc)   	{    		DWORD   dwErrorFlags;		COMSTAT ComStat;		DWORD dwLength;		DWORD dwEvtMask = 0;   		DWORD dwRead = 0;		BOOL bWaitRes = WaitCommEvent(g_hCom, &dwEvtMask, NULL); 		//		RETAILMSG(TRUE,(TEXT("VSP comm event come %d ,last set is %d\r\n"),dwEvtMask,g_dwWaitMask));		//MyPDebugStringW(L"Gps com has event come up");		if(dwEvtMask==0)		{			//wait error			Sleep(30);			if(g_dwWaitMask)			{				continue; //Last set is not null,give up,because exit read thread need null event			}		}		if(bWaitRes == FALSE)   		{   			continue;   		}     		if(dwEvtMask & EV_RXCHAR)   		{   			Sleep(20);			EnterCriticalSection(&g_csRead);    			ClearCommError(g_hCom,&dwErrorFlags,&ComStat);			dwLength=ComStat.cbInQue;			if(/*dwLength>0*/1)			{				ZeroMemory(g_vtBufRead, sizeof(g_vtBufRead));				ReadFile(g_hCom, &g_vtBufRead, sizeof(g_vtBufRead), &dwRead, NULL);			 	mbstowcs(wcDebug,  (char*)g_vtBufRead, strlen((char*)g_vtBufRead));			//	OutputErrorInfo(L"Read data:%s\r\n", wcDebug);			//	DecodeGpsNema((char*)g_vtBufRead);				DecodeGpsBuf((char*)g_vtBufRead);				g_dwLenReadBuf=dwRead;				if(g_pChanle1)				{					g_pChanle1->iIndex = 0;					g_pChanle1->iTolLen = dwRead;					memcpy(g_pChanle1->cReadBuf,g_vtBufRead,dwRead);				}				if(g_pChanle2)				{					g_pChanle2->iIndex = 0;					g_pChanle2->iTolLen = dwRead;					memcpy(g_pChanle2->cReadBuf,g_vtBufRead,dwRead);				}				if(g_pChanle3)				{					g_pChanle3->iIndex = 0;					g_pChanle3->iTolLen = dwRead;					memcpy(g_pChanle3->cReadBuf,g_vtBufRead,dwRead);				}			}			else			{				RETAILMSG(g_bDbg,(L"[VSP]:ClearCommError(%d): dwLength is 0,continue ......\r\n",dwErrorFlags));				//	LeaveCriticalSection(&g_csRead);				//	continue;			}			LeaveCriticalSection(&g_csRead);   		}   #if 0		//if(dwEvtMask == EV_RXCHAR && ((g_dwWaitMask & EV_RXCHAR) == 0 || dwRead == 0))   		if(dwEvtMask == EV_RXCHAR && ((g_dwWaitMask & EV_RXCHAR) == 0 ))   		{   			//The return event mask is only EV_RXCHAR and there is not EV_RXCHAR in the wait mask.   			RETAILMSG(g_bDbg,(L"Skip send event C......\r\n"));			continue;   		}   #endif		InterlockedExchange((LPLONG)(&g_dwEvtMask),dwEvtMask);   		PulseEvent(g_hEventComm);		//	RETAILMSG(TRUE,(TEXT("PulseEvent %d\r\n"),g_dwEvtMask));		//Sleep for other thread to respond to the event   		//	Sleep(100);   		Sleep(20); 		DEBUGMSG(g_bDbg,(TEXT("[VSP]:PulseEvent! The event-mask is 0x%x\r\n"),dwEvtMask));    	}   	MyPDebugStringW(L"Fun:%s; over MonitorCommEventProc", __FUNCTIONW__);	return 0;   }   
2、COM_Open

为了让上层的应用程序能实现三次打开此设备而初始化三块COM_DEV内存。

typedef struct{	int iIndex;	int iTolLen;	BYTE cReadBuf[READ_BUFFER_LENGTH];}COM_DEV,*PCOM_DEV;


 

DWORD COM_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)   {   	PCOM_DEV pComDev = NULL;	MyPDebugStringW(L"Enter %s", __FUNCTIONW__);	EnterCriticalSection(&g_csOpen);   	if(!g_pChanle1)	{		g_pChanle1 = new COM_DEV;		g_pChanle1->iTolLen = g_pChanle1->iIndex = 0;		pComDev = g_pChanle1;	}	else if(!g_pChanle2)	{		g_pChanle2 = new COM_DEV;		g_pChanle2->iTolLen=g_pChanle2->iIndex =0;		pComDev = g_pChanle2;	}	else if(!g_pChanle3)	{		g_pChanle3 = new COM_DEV;		g_pChanle3->iTolLen=g_pChanle3->iIndex =0;		pComDev = g_pChanle3;	}	else	{		return 0;	}#if 1	//The variable   	if(g_uiOpenCount >= MAX_OPEN_CNT)		return 0;	g_uiOpenCount ++;  	pComDev->iIndex = 0;	//	dwResult = dwData;   LEAVE_CRITICAL_SECTION:    #endif	LeaveCriticalSection(&g_csOpen); 	return (DWORD)pComDev;   }   
3、COM_Deinit

主要是关闭读线程,反初始化资源;

关闭线程的方法有几种:

(1)是直接CloseHandle真实的COM口,然后在线程中会WaitCommEvent失败,然后退出,这这种方法太间接;

(2)设备全局变量,采用原子操作函数来变化它的标识。

A、定义变量:BOOL g_bExitMonitorProc = FALSE;   ;

B、在线程中:

DWORD MonitorCommEventProc(LPVOID pParam)   {    TCHAR wcDebug[1024] = {0}; MyPDebugStringW(L"Enter MonitorCommEventProc"); SetCommMask(g_hCom, EV_RXCHAR); while(!g_bExitMonitorProc)    {  

C、在创建线程前使它为FLASE:

InterlockedExchange((LPLONG)(&g_bExitMonitorProc),FALSE);   

D、在要关闭线程时:

BOOL CloseTransmitThr(){	DWORD dwMask = 0; 	//Notify the monitor thread to exit.    	InterlockedExchange((LPLONG)(&g_bExitMonitorProc),TRUE);   

4、COM_IOControl

为了使应用程序能像使用真实COM口一样的使用虚拟COM口,我们需要给出COM_IOControl,并去case需要的IOCODE,

(1)IOCTL_SERIAL_SET_TIMEOUTS
应用程序设备虚拟COM口的此接口,我们就给真实的COM口通过系统API SetCommTimeouts来设置:

SetCommTimeouts(g_hCom,(LPCOMMTIMEOUTS)(pBufIn));   

(2)IOCTL_SERIAL_GET_TIMEOUTS

类似于(1)

(3)IOCTL_SERIAL_SET_DCB

类似于(1)

(4)IOCTL_SERIAL_GET_DCB

类似于(1)

(5)IOCTL_SERIAL_WAIT_ON_MASK

(5、1)因为我们的读线程中有调用了WaitCommEvent,所以这里我们得做好同步,同步的方法是:

A、初始化事件

g_hEventComm = CreateEvent(NULL,TRUE,FALSE,NULL);   

B、在读取完数据后让事件激活

PulseEvent(g_hEventComm);

C、在case IOCTL_SEARIAL_WAIT_ON_MASK时去等待

case IOCTL_SERIAL_WAIT_ON_MASK:   		{      			MyPDebugStringW(L"receive IOCTL_SERIAL_WAIT_ON_MASK");			if(dwBufOutSize < sizeof(DWORD) ||  WaitForSingleObject(g_hEventComm,INFINITE) == WAIT_TIMEOUT)   			{   

(5、2)把读线程WaitCommEvent返回的结果返回给应用程序

A、

BOOL bWaitRes = WaitCommEvent(g_hCom, &dwEvtMask, NULL); 

B、

InterlockedExchange((LPLONG)(&g_dwEvtMask),dwEvtMask);   


C、在case IOCTL_SEARIAL_WAIT_ON_MASK等待成功时

InterlockedExchange((LPLONG)(pBufOut),g_dwEvtMask);   

 

(6)IOCTL_SERIAL_SET_WAIT_MASK

g_dwWaitMask = *(DWORD*)(pBufIn);   			RETAILMSG(g_bDbg,(TEXT("VSP : Set wait mask : %d ......\r\n"),g_dwWaitMask));			return SetCommMask(g_hCom,g_dwWaitMask | EV_RXCHAR); //The driver need the EV_RXCHAR notify event.   

也是调用了系统的SetCommMask,不过对入口设置的标志或上了EV_RXHCAR,因为我们的驱动需要EX_RXCHAR 通知事件。

 

(7)IOCTL_SERIAL_GET_WAIT_MASK

(8)IOCTL_SERIAL_GET_COMMSTATUS

(9)

 

 

三、实现机制要点

(1)采用文件映射实现共享数据。

(1、1)共享的数据为GPS数据,在COM_Init中去创建文件映射:

	g_pGpsDataMem = (PGPSDATA)ReadShareArea();	g_pGpsDataMem->iSatNum = 67;


映射文件的大小由SHARE_MEM_SIZE指定,为256。

PVOID ReadShareArea()   {   	PBYTE pShareMem=NULL;	HANDLE hMySharedFile=		CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,SHARE_MEM_SIZE,SHARE_MEM_NAME);	if(hMySharedFile!=NULL)		pShareMem =(PBYTE)MapViewOfFile(hMySharedFile,FILE_MAP_ALL_ACCESS,0,0,0);	CloseHandle(hMySharedFile);	return pShareMem;  }


(1、2)在COM _Open中为应用程序分析三个内存用于存储在读线程中得到的数据。

其中存储线程中得到的数据为:

DecodeGpsBuf((char*)g_vtBufRead);				g_dwLenReadBuf=dwRead;				if(g_pChanle1)				{					g_pChanle1->iIndex = 0;					g_pChanle1->iTolLen = dwRead;					memcpy(g_pChanle1->cReadBuf,g_vtBufRead,dwRead);				}				if(g_pChanle2)				{					g_pChanle2->iIndex = 0;					g_pChanle2->iTolLen = dwRead;					memcpy(g_pChanle2->cReadBuf,g_vtBufRead,dwRead);				}				if(g_pChanle3)				{					g_pChanle3->iIndex = 0;					g_pChanle3->iTolLen = dwRead;					memcpy(g_pChanle3->cReadBuf,g_vtBufRead,dwRead);				}


(1、3)在导出给应用程序的接口ReadGpsFromCom中把映射的数据拷到形参中

BOOL ReadGpsFromCom(PVOID pReadBuf){	MyPDebugStringW(L"Enter %s", __FUNCTIONW__);	PGPSDATA pGpsDataMem= (PGPSDATA)ReadShareArea();;	memcpy(pReadBuf,pGpsDataMem,sizeof(GPSDATA));	if(pGpsDataMem)		UnmapViewOfFile(pGpsDataMem);	return TRUE;}


 

 

  相关解决方案