国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
WinCE虛擬串口驅(qū)動(dòng)(一)

  //========================================================================
  //TITLE:
  //    WinCE虛擬串口驅(qū)動(dòng)(一)
  //AUTHOR:
  //    norains
  //DATE:
  //    Saturday 28-March-2009
  //Environment:
  //    WINDOWS CE 5.0
  //========================================================================
  
  用過(guò)串口進(jìn)行開(kāi)發(fā)的朋友應(yīng)該都知道,串口驅(qū)動(dòng)是一個(gè)典型的獨(dú)占設(shè)備。簡(jiǎn)單點(diǎn)來(lái)說(shuō),就是在成功地調(diào)用CreateFile打開(kāi)串口之后,沒(méi)有通過(guò)CloseHandle進(jìn)行關(guān)閉,是無(wú)論如何都不能再次調(diào)用CreateFile來(lái)再次打開(kāi)相同的串口。
  
  有的朋友可能會(huì)覺(jué)得莫名奇妙,為什么微軟要在這上面做限制呢?但其實(shí)從另一個(gè)角度來(lái)講,微軟這么做是非常有道理的。以接收數(shù)據(jù)為例子,在驅(qū)動(dòng)里面會(huì)有一定的緩存,用來(lái)保留一定量的數(shù)據(jù)。當(dāng)通過(guò)ReadFile來(lái)獲取數(shù)據(jù)時(shí),驅(qū)動(dòng)就會(huì)將緩存給清空,然后再繼續(xù)接收數(shù)據(jù)。如果串口不是獨(dú)占設(shè)備,可以多次打開(kāi),那么在讀取數(shù)據(jù)上面就會(huì)有問(wèn)題:應(yīng)該什么時(shí)候才清空緩存?比方說(shuō),其中一個(gè)線程通過(guò)ReadFile來(lái)獲得了數(shù)據(jù),那么驅(qū)動(dòng)應(yīng)不應(yīng)該將緩沖清空?如果清空,那另一個(gè)線程也想獲得同樣的數(shù)據(jù)進(jìn)行分析,那就會(huì)產(chǎn)生數(shù)據(jù)丟失;如果不清空,萬(wàn)一之前已經(jīng)通過(guò)ReadFile獲取數(shù)據(jù)的線程再次進(jìn)行讀取,那么它將會(huì)得到同樣重復(fù)的數(shù)據(jù)。如果想要在這多個(gè)進(jìn)程中維持?jǐn)?shù)據(jù)的同步,肯定要額外增加相應(yīng)的標(biāo)識(shí),但這樣就會(huì)加大了驅(qū)動(dòng)的復(fù)雜度,并且也無(wú)法和別的驅(qū)動(dòng)保持一致。因此,微軟對(duì)串口實(shí)行獨(dú)占設(shè)備的策略,是非常正確的。
  
  但,正確并不代表放之四海而皆準(zhǔn),在某些特殊的情況下,我們還是需要非獨(dú)占性質(zhì)的串口。簡(jiǎn)單地舉個(gè)例子,在手持PND GPS設(shè)備中,導(dǎo)航軟件肯定是必須要能通過(guò)串口進(jìn)行數(shù)據(jù)獲取來(lái)定位;可另一方面,我的另一個(gè)應(yīng)用程序又想獲得GPS數(shù)據(jù)進(jìn)行系統(tǒng)時(shí)間的校準(zhǔn)。在這情形之下,我們就必須使用一個(gè)非獨(dú)占性質(zhì)的串口設(shè)備。
  
  為了簡(jiǎn)化設(shè)計(jì),該串口設(shè)備的驅(qū)動(dòng)我們約定如下:
  
  1.同一時(shí)間只能有一個(gè)進(jìn)程對(duì)外輸出數(shù)據(jù),其余進(jìn)程只能在該進(jìn)程輸出完畢之后才能進(jìn)行。
  
  2.程序不應(yīng)該主動(dòng)調(diào)用ReadFile來(lái)輪詢獲取數(shù)據(jù)。而是通過(guò)WaitCommEvent進(jìn)行檢測(cè),當(dāng)返回的狀態(tài)中具備EV_RXCHAR時(shí)才調(diào)用ReadFile。并且該調(diào)用必須在一定的時(shí)間間隔之內(nèi),而且為了不丟失數(shù)據(jù),緩沖大小一定要等于或大于READ_BUFFER_LENGTH。
  
  之所以有如上約束,完全是出于設(shè)計(jì)簡(jiǎn)便考慮。
  
  
  非獨(dú)占式串口驅(qū)動(dòng)主要是處理數(shù)據(jù)的分發(fā),可以和具體的硬件分開(kāi),換句話說(shuō),該驅(qū)動(dòng)是基于原有的串口驅(qū)動(dòng)之上,實(shí)際上并“沒(méi)有”該設(shè)備,因此我們將該非獨(dú)占式串口稱(chēng)之為“虛擬串口驅(qū)動(dòng)”。這樣設(shè)計(jì)的優(yōu)勢(shì)很明顯,可以不用理會(huì)具體的硬件規(guī)格,只要采用的是WinCE系統(tǒng),并且原來(lái)已經(jīng)具備了完善的串口驅(qū)動(dòng),那么該虛擬串口驅(qū)動(dòng)就能工作正常。
  
  
  接下來(lái)我們來(lái)看看該虛擬串口的具體實(shí)現(xiàn)。
  
  麻雀雖小,五官俱全,雖然說(shuō)該驅(qū)動(dòng)是“虛擬”的,但畢竟還是“驅(qū)動(dòng)”,該有的部分我們還是要具備的。
  
  驅(qū)動(dòng)的前綴為VSP,取自于Virtual Serial Port之意。
  
  該驅(qū)動(dòng)必須實(shí)現(xiàn)如下函數(shù):

  1. VSP_Close  
  2. VSP_Deinit  
  3. VSP_Init  
  4. VSP_IOControl  
  5. VSP_Open  
  6. VSP_PowerDown  
  7. VSP_PowerUp  
  8. VSP_Read  
  9. VSP_Seek  
  10. VSP_Write         

 

  因?yàn)榇隍?qū)動(dòng)是流設(shè)備,又和具體的電源管理五官,故VSP_Seek,VSP_PowerDown,VSP_PowerUp這些函數(shù)可以不用處理,直接返回即可。
  
  
  現(xiàn)在來(lái)看一下VSP_Open函數(shù)。
  
  VSP_Open函數(shù)我們大致需要如下流程處理事情:
  
  1.判斷當(dāng)前的是否已經(jīng)打開(kāi)串口,如果已經(jīng)打開(kāi),直接跳到4.
  
  2.獲取需要打開(kāi)的串口序號(hào),并打開(kāi)該串口。如果打開(kāi)失敗,直接跳到5.
  
  3.打開(kāi)數(shù)據(jù)監(jiān)視進(jìn)程(注:該部分在數(shù)據(jù)讀取部分進(jìn)行分析)。
  
  4.標(biāo)識(shí)記數(shù)(即g_uiOpenCount)增加1。
  
  5.函數(shù)返回
  
  
  流程1:
  
  全局變量g_uiOpenCount用來(lái)保存打開(kāi)的記數(shù),所以只要判斷該數(shù)值是否為0即可確定是否應(yīng)該打開(kāi)串口: 

  1. if(g_uiOpenCount != 0)  
  2. {         
  3. goto SET_SUCCEED_FLAG;  
  4. }  


  流程2:
  
  為了讓程序更具備靈活性,所打開(kāi)的串口序號(hào)我們不直接在驅(qū)動(dòng)中設(shè)定,而是通過(guò)讀取注冊(cè)表的數(shù)值獲得:

  1. if(reg.Open(REG_ROOT_KEY,REG_DEVICE_SUB_KEY) == FALSE)  
  2. {  
  3.     RETAILMSG(TRUE,(TEXT("[VSP]:Failed to open the registry/r/n")));  
  4.     goto LEAVE_CRITICAL_SECTION;  
  5. }  
  6.           
  7. //Get the MAP_PORT name   
  8. reg.GetValueSZ(REG_MAP_PORT_NAME,&vtBuf[0],vtBuf.size());  

 


  接下來(lái)便是打開(kāi)具體的串口:

  1. g_hCom = CreateFile(&vtBuf[0],GENERIC_READ | GENERIC_WRITE ,0,NULL,OPEN_EXISTING,0,NULL);  
  2. if(g_hCom == INVALID_HANDLE_VALUE )  
  3. {  
  4.     RETAILMSG(TRUE,(TEXT("[VSP]Failed to map to %s/r/n"),&vtBuf[0]));  
  5.     goto LEAVE_CRITICAL_SECTION;  
  6. }  
  7. else  
  8. {  
  9.     RETAILMSG(TRUE,(TEXT("[VSP]Succeed to map to %s/r/n"),&vtBuf[0]));  
  10. }     


  流程3:
  
  創(chuàng)建進(jìn)程來(lái)監(jiān)視數(shù)據(jù):

  1. InterlockedExchange(reinterpret_cast<LONG *>(&g_bExitMonitorProc),FALSE);  
  2. CloseHandle(CreateThread(NULL,NULL,MonitorCommEventProc,NULL,NULL,NULL));  


  流程4:
  
  成功打開(kāi)記數(shù)

  1. SET_SUCCEED_FLAG:     
  2.     g_uiOpenCount ++;  
  3.     bResult = TRUE;  

  

 

 

  流程5:
  
  函數(shù)返回:

  1. LEAVE_CRITICAL_SECTION:       
  2.     LeaveCriticalSection(&g_csOpen);      
  3.     return bResult;   




  和VSP_Open密切對(duì)應(yīng)的是VSP_Close,該函數(shù)流程基本和VSP_Open相反處理:
  
  1.打開(kāi)記數(shù)(g_uiOpenCount)減1。如果g_uiOpenCount為不為0,跳轉(zhuǎn)3。
  
  2.退出監(jiān)視數(shù)據(jù)進(jìn)程,并且關(guān)閉打開(kāi)的串口。
  
  3.函數(shù)返回。
  
  
  流程1和流程2處理如下:
  1. g_uiOpenCount --;     
  2. if(g_uiOpenCount == 0)  
  3. {         
  4.     //Notify the monitor thread to exit.      
  5.     InterlockedExchange(reinterpret_cast<LONG *>(&g_bExitMonitorProc),TRUE);  
  6.     DWORD dwMask = 0;  
  7.     GetCommMask(g_hCom,&dwMask);  
  8.     SetCommMask(g_hCom,dwMask);       
  9.               
  10.     while(InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),TRUE) == TRUE)  
  11.     {  
  12.         Sleep(20);  
  13.     }  
  14.     InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),FALSE);  
  15.               
  16.     CloseHandle(g_hCom);  
  17.     g_hCom = NULL;  
  18. }  


我們必須確保VSP_Open和VSP_Close中的某一個(gè)必須要全部處理完才能再次調(diào)用,否則在處理過(guò)程中如果又再次調(diào)用本函數(shù)或相對(duì)應(yīng)的加載或卸載函數(shù),那么一定會(huì)引發(fā)我們不可預(yù)料的情況,所以我們?cè)谶@兩個(gè)函數(shù)中增加了關(guān)鍵段,以維持處理上的同步: 
  1. EnterCriticalSection(&g_csOpen);  
  2. ...  
  3. LeaveCriticalSection(&g_csOpen);  



  其余的接口,算起來(lái)最簡(jiǎn)單的是VSP_Write,只要確定同一時(shí)間只能有唯一的一個(gè)進(jìn)程進(jìn)行輸出即可:
  1. EnterCriticalSection(&g_csWrite);  
  2. DWORD dwWrite = 0;  
  3. WriteFile(g_hCom,pBuffer,dwNumBytes,&dwWrite,NULL);  
  4. LeaveCriticalSection(&g_csWrite);  

  

    在完成VSP_Read之前,我們先來(lái)看另外一個(gè)函數(shù):WaitCommEvent。這是串口驅(qū)動(dòng)特有的,目的是有某些時(shí)間發(fā)生時(shí),能夠第一時(shí)間激活線程。該函數(shù)和驅(qū)動(dòng)的MMD層有關(guān),是MDD層的應(yīng)用程序級(jí)別接口。具體串口的PDD層,WaitCommEvent函數(shù)體內(nèi)也僅僅是調(diào)用了COM_IOControl接口,然后傳入IOCTL_SERIAL_WAIT_ON_MASK控制碼而已。也就是說(shuō),調(diào)用WaitCommEvent的代碼,就相當(dāng)于如此調(diào)用COM_IOControl:

  1. DeviceIoControl(hCom,  
  2.                                 IOCTL_SERIAL_WAIT_ON_MASK,  
  3.                                     NULL,  
  4.                                     0,  
  5.                                     pOutBuf,  
  6.                                     dwOutBufLen,  
  7.                                     &dwReturn,  
  8.                                     NULL);  



  換句話說(shuō),如果想讓虛擬串口驅(qū)動(dòng)支持WaitCommEvent函數(shù),我們只需要在VSP_IOControl處理IOCTL_SERIAL_WAIT_ON_MASK控制碼即可:

  1. BOOL VSP_IOControl(  
  2.    DWORD dwHandle,  
  3.    DWORD dwIoControlCode,  
  4.    PBYTE pBufIn,  
  5.    DWORD dwBufInSize,  
  6.    PBYTE pBufOut,  
  7.    DWORD dwBufOutSize,  
  8.    PDWORD pBytesReturned  
  9.    )  
  10. {  
  11.     ...  
  12.       
  13.     switch(dwIoControlCode)   
  14.     {  
  15.         ...  
  16.                   
  17.         case IOCTL_SERIAL_WAIT_ON_MASK:  
  18.                       
  19.             ...                   
  20.             break;  
  21.               
  22.         ...  
  23.     }  
  24. }  
  25.           



  推而廣之,像SetCommState,SetCommTimeouts等串口特有的函數(shù),都僅僅只是對(duì)COM_IOControl函數(shù)進(jìn)行的一層封裝而已。
  
  我們?cè)倩氐絎aitCommEvent函數(shù)??赡苡械呐笥阎苯诱J(rèn)為,我們只要在IOCTL_SERIAL_WAIT_ON_MASK段直接簡(jiǎn)單調(diào)用原有的WaitCommEvent即可:

  1. switch(dwIoControlCode)   
  2. {  
  3.     ...  
  4.               
  5.     case IOCTL_SERIAL_WAIT_ON_MASK:  
  6.     {                 
  7.         //直接調(diào)用原生的WaitCommEvent,但實(shí)際是錯(cuò)誤的  
  8.         if(dwBufOutSize < sizeof(DWORD) || WaitCommEvent(g_hCom,reinterpret_cast<DWORD *>(pBufOut),NULL) == FALSE)  
  9.         {  
  10.             *pBytesReturned = 0;              
  11.             return FALSE;  
  12.         }  
  13.         else  
  14.         {  
  15.             *pBytesReturned = sizeof(DWORD);  
  16.             return TRUE;  
  17.         }                     
  18.     }  
  19.                   
  20.     ...  
  21. }  


 

 但實(shí)際上這樣是不行的。查看文檔關(guān)于WaitCommEvent函數(shù)的描述,注意事項(xiàng)中有這么一條:Only one WaitCommEvent can be used for each open COM port handle. This means that if you have three threads in your application and each thread needs to wait on a specific comm event, each thread needs to open the COM port and then use the assigned port handle for their respective WaitCommEvent calls.

  
  也就是說(shuō),WaitCommEvent只能被一個(gè)線程調(diào)用。如果多線程都同時(shí)調(diào)用該函數(shù),會(huì)發(fā)生什么情況呢?經(jīng)過(guò)實(shí)際測(cè)試,如果多線程都調(diào)用相同的WaitCommEvent,那么在某個(gè)線程調(diào)用WaitCommEvent時(shí),之前已經(jīng)有其余的線程通過(guò)調(diào)用該函數(shù)進(jìn)行等待狀態(tài)的話,那等待的線程立馬會(huì)喚醒。簡(jiǎn)單點(diǎn)來(lái)說(shuō),就是同一時(shí)間只能有唯一的一個(gè)線程通過(guò)WaitCommEvent函數(shù)進(jìn)入等待狀態(tài)。所以,對(duì)于IOCTL_SERIAL_WAIT_ON_MASK控制碼,我們不能簡(jiǎn)單地調(diào)用WaitCommEvent函數(shù)。
  
  在這里我們采用這么一種設(shè)計(jì),對(duì)于IOCTL_SERIAL_WAIT_ON_MASK的處理,我們是通過(guò)調(diào)用WaitForSingleObject進(jìn)行線程等待。而虛擬串口驅(qū)動(dòng),會(huì)額外開(kāi)放一個(gè)線程,該線程主要是通過(guò)調(diào)用WaitCommEvent來(lái)獲取原生串口的狀態(tài),當(dāng)狀態(tài)有通知時(shí),再發(fā)送event給等待的線程。因此,對(duì)于IOCTL_SERIAL_WAIT_ON_MASK控制碼的處理可以所作如下:

  1. switch(dwIoControlCode)   
  2. {  
  3.     ...  
  4.                   
  5.     case IOCTL_SERIAL_WAIT_ON_MASK:  
  6.     {                 
  7.         if(dwBufOutSize < sizeof(DWORD) ||   WaitForSingleObject(g_hEventComm,INFINITE) == WAIT_TIMEOUT)  
  8.                 {  
  9.                     *pBytesReturned = 0;              
  10.                     return FALSE;  
  11.                 }  
  12.                 else  
  13.                 {  
  14.                     InterlockedExchange(reinterpret_cast<LONG *>(pBufOut),g_dwEvtMask);  
  15.                     *pBytesReturned = sizeof(DWORD);                          
  16.                     return TRUE;  
  17.                 }                     
  18.             }  
  19.                   
  20.             ...  
  21.         }  



  驅(qū)動(dòng)額外的等待線程所做如是:
  1. DWORD MonitorCommEventProc(LPVOID pParam)  
  2. {             
  3.     ...  
  4.               
  5.     while(TRUE)  
  6.     {     
  7.         DWORD dwEvtMask = 0;  
  8.         BOOL bWaitRes = WaitCommEvent(g_hCom,&dwEvtMask,NULL);                
  9.                   
  10.         if(g_bExitMonitorProc != FALSE)  
  11.         {  
  12.             break;  
  13.         }                     
  14.                   
  15.         if(bWaitRes == FALSE)  
  16.         {  
  17.             continue;  
  18.         }         
  19.                   
  20.         ...  
  21.               
  22.         InterlockedExchange(reinterpret_cast<LONG *>(&g_dwEvtMask),dwEvtMask);  
  23.         PulseEvent(g_hEventComm);         
  24.                   
  25.         ...  
  26.                   
  27.     }  
  28.               
  29.     ...  
  30.               
  31.     return 0;  
  32. }  


  現(xiàn)在是到考慮ReadFile實(shí)現(xiàn)的時(shí)候了。我們需要考慮到,不同進(jìn)程,在同時(shí)讀取數(shù)據(jù)時(shí),應(yīng)該能獲得相同的數(shù)據(jù)。但對(duì)于原生的串口驅(qū)動(dòng),如果再次調(diào)用ReadFile,所獲得的數(shù)據(jù)絕對(duì)是不會(huì)和之前的一樣,否則就亂套了。于是,和IOCTL_SERIAL_WAIT_ON_MASK一樣,我們這么也不能粗暴簡(jiǎn)單地調(diào)用原生的ReadFile完事。
  
  我們轉(zhuǎn)換個(gè)思維,對(duì)于“不同進(jìn)程,在同時(shí)讀取數(shù)據(jù)時(shí),應(yīng)該能獲得相同的數(shù)據(jù)”,我們應(yīng)該是這么理解:“不同進(jìn)程,相當(dāng)短的間隔內(nèi)讀取數(shù)據(jù),應(yīng)該能獲得相同的數(shù)據(jù)”。如果要做到這點(diǎn),我們只需要設(shè)置一個(gè)讀取緩存,當(dāng)上級(jí)程序想要獲取數(shù)據(jù)時(shí),我們只需要簡(jiǎn)單地將數(shù)據(jù)返回即可。那么接下來(lái)最關(guān)鍵的是,我們應(yīng)該什么時(shí)候讀取數(shù)據(jù)?什么時(shí)候該刷新緩存呢?
  
  分開(kāi)來(lái)說(shuō),最簡(jiǎn)單的方式,就是在監(jiān)視進(jìn)程MonitorCommEventProc中讀取數(shù)據(jù)并刷新緩存。因?yàn)樵摼€程會(huì)調(diào)用WaitCommEvent函數(shù)進(jìn)行等待,它能夠充分知道什么時(shí)候有數(shù)據(jù)進(jìn)來(lái)。只要有數(shù)據(jù)進(jìn)來(lái),我們就進(jìn)行讀取。如果之前的緩存已經(jīng)被讀取過(guò),我們就清空緩存,存入新的數(shù)據(jù);否則就在舊緩存之后添加我們新的數(shù)據(jù)。故此,完善的MonitorCommEventProc實(shí)現(xiàn)就應(yīng)該如此:

  1. DWORD MonitorCommEventProc(LPVOID pParam)  
  2. {  
  3.     InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),TRUE);  
  4.       
  5.     RETAILMSG(TRUE,(TEXT("[VSP]:MonitorCommEventProc Running!/r/n")));  
  6.       
  7.     std::vector<BYTE> vtBufRead(g_vtBufRead.size(),0);          
  8.     while(TRUE)  
  9.     {     
  10.         DWORD dwEvtMask = 0;  
  11.         BOOL bWaitRes = WaitCommEvent(g_hCom,&dwEvtMask,NULL);                
  12.           
  13.         if(g_bExitMonitorProc != FALSE)  
  14.         {  
  15.             break;  
  16.         }                     
  17.           
  18.         if(bWaitRes == FALSE)  
  19.         {  
  20.             continue;  
  21.         }         
  22.           
  23.         DWORD dwRead = 0;             
  24.         if(dwEvtMask & EV_RXCHAR)  
  25.         {  
  26.             EnterCriticalSection(&g_csRead);                      
  27.               
  28.             ReadFile(g_hCom,&g_vtBufRead[0],vtBufRead.size(),&dwRead,NULL);       
  29.             if(dwRead == vtBufRead.size() || g_bReaded != FALSE)  
  30.             {  
  31.                 g_dwLenReadBuf = dwRead;  
  32.                 g_vtBufRead.swap(vtBufRead);  
  33.             }  
  34.             else if(dwRead != 0)  
  35.             {  
  36.                 if(g_dwLenReadBuf + dwRead <= g_vtBufRead.size())  
  37.                 {  
  38.                     g_dwLenReadBuf += dwRead;  
  39.                     g_vtBufRead.insert(g_vtBufRead.end(),vtBufRead.begin(),vtBufRead.begin() + dwRead);  
  40.                 }  
  41.                 else  
  42.                 {  
  43.                     DWORD dwCover = g_dwLenReadBuf + dwRead - g_vtBufRead.size();  
  44.                     std::copy(g_vtBufRead.begin() + dwCover,g_vtBufRead.begin() + g_dwLenReadBuf,g_vtBufRead.begin());  
  45.                     std::copy(vtBufRead.begin(),vtBufRead.begin() + dwRead,g_vtBufRead.begin() + (g_dwLenReadBuf - dwCover));  
  46.                     g_dwLenReadBuf = g_vtBufRead.size();  
  47.                 }  
  48.             }  
  49.               
  50.             g_bReaded = FALSE;  
  51.               
  52.             DEBUGMSG(TRUE,(TEXT("[VSP]:Read data : %d/r/n"),dwRead));     
  53.           
  54.             LeaveCriticalSection(&g_csRead);  
  55.         }  
  56.       
  57.         if(dwEvtMask == EV_RXCHAR && ((g_dwWaitMask & EV_RXCHAR) == 0 || dwRead == 0))  
  58.         {  
  59.             //The return event mask is only EV_RXCHAR and there is not EV_RXCHAR in the wait mask.  
  60.             continue;  
  61.         }  
  62.       
  63.         InterlockedExchange(reinterpret_cast<LONG *>(&g_dwEvtMask),dwEvtMask);  
  64.         PulseEvent(g_hEventComm);         
  65.           
  66.         //Sleep for other thread to respond to the event  
  67.         Sleep(100);  
  68.           
  69.         DEBUGMSG(TRUE,(TEXT("[VSP]:PulseEvent! The event-mask is 0x%x/r/n"),dwEvtMask));      
  70.           
  71.     }  
  72.       
  73.     RETAILMSG(TRUE,(TEXT("[VSP]:Exit the MonitorCommEventProc/r/n")));    
  74.     InterlockedExchange(reinterpret_cast<LONG *>(&g_bMonitorProcRunning),FALSE);  
  75.       
  76.     return 0;  
  77. }  


  正因?yàn)樽x取是如此實(shí)現(xiàn),所以我們才有文章開(kāi)頭的第二點(diǎn)約定:
  
  程序不應(yīng)該主動(dòng)調(diào)用ReadFile來(lái)輪詢獲取數(shù)據(jù)。而是通過(guò)WaitCommEvent進(jìn)行檢測(cè),當(dāng)返回的狀態(tài)中具備EV_RXCHAR時(shí)才調(diào)用ReadFile(如果一直采用ReadFile來(lái)輪詢接收數(shù)據(jù),很可能會(huì)讀取重復(fù)的數(shù)據(jù))。并且該調(diào)用必須在一定的時(shí)間間隔之內(nèi)(如果間隔太久,很可能因?yàn)榫彺嬉呀?jīng)刷新,數(shù)據(jù)丟失),而且為了不丟失數(shù)據(jù),緩沖大小一定要等于或大于READ_BUFFER_LENGTH(因?yàn)橹灰x取一次數(shù)據(jù),讀取的標(biāo)識(shí)就會(huì)被設(shè)置,當(dāng)有新數(shù)據(jù)到達(dá)時(shí),會(huì)刷新緩存,導(dǎo)致數(shù)據(jù)丟失)。
  
  這也同時(shí)解釋了MonitorCommEventProc進(jìn)程為何在PulseEvent之后會(huì)調(diào)用Sleep函數(shù)進(jìn)行短暫的休眠,其作用主要是讓驅(qū)動(dòng)的讀取進(jìn)程歇歇,好讓上級(jí)等待進(jìn)程能在等待事件返回時(shí)有足夠的時(shí)間來(lái)讀取獲得的數(shù)據(jù)。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
實(shí)戰(zhàn)串行通訊
串口通信常用API WaitCommEvent 等
串口編程入門(mén)
win32串口編程
串口通訊方法(WINAPI實(shí)現(xiàn))
VC知識(shí)庫(kù)文章 - Win32串口編程
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服