转载请标明是引用于 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;}