《vc串口编程.pdf》由会员分享,可在线阅读,更多相关《vc串口编程.pdf(18页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、3.多线程串口类使用多线程串口通信更方便的途径是编写一个多线程的串口类,例如Remon Spekreijse编写了一个CSerialPort串口类。仔细分析这个类的源代码,将十分有助于我们对先前所学多线程及同步知识的理解。3.1 类的定义#ifndef _SERIALPORT_H_#define _SERIALPORT_H_#define WM_COMM_BREAK_DETECTED WM_USER+1/A break was detected on input.#define WM_COMM_CTS_DETECTED WM_USER+2/The CTS(clear-to-send)signa
2、l changed state.#define WM_COMM_DSR_DETECTED WM_USER+3/The DSR(data-set-ready)signal changed state.#define WM_COMM_ERR_DETECTED WM_USER+4/A line-status error occurred.Line-status errors are CE_FRAME,CE_OVERRUN,and CE_RXPARITY.#define WM_COMM_RING_DETECTED WM_USER+5/A ring indicator was detected.#def
3、ine WM_COMM_RLSD_DETECTED WM_USER+6/The RLSD(receive-line-signal-detect)signal changed state.#define WM_COMM_RXCHAR WM_USER+7/A character was received and placed in the input buffer.#define WM_COMM_RXFLAG_DETECTED WM_USER+8/The event character was received and placed in the input buffer.#define WM_C
4、OMM_TXEMPTY_DETECTED WM_USER+9/The last character in the output buffer was sent.class CSerialPort public:/contruction and destruction CSerialPort();virtual CSerialPort();/port initialisation BOOL InitPort(CWnd*pPortOwner,UINT portnr=1,UINT baud=19200,char parity=N,UINT databits=8,UINT stopsbits=1,DW
5、ORD dwCommEvents=EV_RXCHAR|EV_CTS,UINT nBufferSize=512);/start/stop comm watching BOOL StartMonitoring();BOOL RestartMonitoring();BOOL StopMonitoring();DWORD GetWriteBufferSize();DWORD GetCommEvents();DCB GetDCB();void WriteToPort(char*string);protected:/protected memberfunctions void ProcessErrorMe
6、ssage(char*ErrorText);static UINT CommThread(LPVOID pParam);static void ReceiveChar(CSerialPort*port,COMSTAT comstat);static void WriteChar(CSerialPort*port);/thread CWinThread*m_Thread;/synchronisation objects CRITICAL_SECTION m_csCommunicationSync;BOOL m_bThreadAlive;/handles HANDLE m_hShutdownEve
7、nt;HANDLE m_hComm;HANDLE m_hWriteEvent;/Event array./One element is used for each event.There are two event handles for each port./A Write event and a receive character event which is located in the overlapped structure(m_ov.hEvent)./There is a general shutdown when the port is closed.HANDLE m_hEven
8、tArray3;/structures OVERLAPPED m_ov;COMMTIMEOUTS m_CommTimeouts;DCB m_dcb;/owner window CWnd*m_pOwner;/misc UINT m_nPortNr;char*m_szWriteBuffer;DWORD m_dwCommEvents;DWORD m_nWriteBufferSize;#endif _SERIALPORT_H_ 3.2 类的实现3.2.1 构造函数与析构函数进行相关变量的赋初值及内存恢复:CSerialPort:CSerialPort()m_hComm=NULL;/initialize
9、 overlapped structure members to zero m_ov.Offset=0;m_ov.OffsetHigh=0;/create events m_ov.hEvent=NULL;m_hWriteEvent=NULL;m_hShutdownEvent=NULL;m_szWriteBuffer=NULL;m_bThreadAlive=FALSE;/Delete dynamic memory/CSerialPort:CSerialPort()do SetEvent(m_hShutdownEvent);while(m_bThreadAlive);TRACE(Thread en
10、dedn);delete m_szWriteBuffer;3.2.2 核心函数:初始化串口在初始化串口函数中,将打开串口,设置相关参数,并创建串口相关的用户控制事件,初始化临界区(Critical Section),以成队的 EnterCriticalSection()、LeaveCriticalSection()函数进行资源的排它性访问:BOOL CSerialPort:InitPort(CWnd*pPortOwner,/the owner(CWnd)of the port(receives message)UINT portnr,/portnumber(1.4)UINT baud,/bau
11、drate char parity,/parity UINT databits,/databits UINT stopbits,/stopbits DWORD dwCommEvents,/EV_RXCHAR,EV_CTS etc UINT writebuffersize)/size to the writebuffer assert(portnr 0&portnr m_bThreadAlive=TRUE;/Misc.variables DWORD BytesTransfered=0;DWORD Event=0;DWORD CommEvent=0;DWORD dwError=0;COMSTAT
12、comstat;BOOL bResult=TRUE;/Clear comm buffers at startup if(port-m_hComm)/check if the port is opened PurgeComm(port-m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);/begin forever loop.This loop will run as long as the thread is alive.for(;)/Make a call to WaitCommEvent().This call
13、will return immediatly/because our port was created as an async port(FILE_FLAG_OVERLAPPED/and an m_OverlappedStructerlapped structure specified).This call will cause the/m_OverlappedStructerlapped element m_OverlappedStruct.hEvent,which is part of the m_hEventArray to/be placed in a non-signeled sta
14、te if there are no bytes available to be read,/or to a signeled state if there are bytes available.If this event handle/is set to the non-signeled state,it will be set to signeled when a/character arrives at the port./we do this for each port!bResult=WaitCommEvent(port-m_hComm,&Event,&port-m_ov);if(
15、!bResult)/If WaitCommEvent()returns FALSE,process the last error to determin/the reason.switch(dwError=GetLastError()case ERROR_IO_PENDING:/This is a normal return value if there are no bytes/to read at the port./Do nothing and continue break;case 87:/Under Windows NT,this value is returned for some
16、 reason./I have not investigated why,but it is also a valid reply/Also do nothing and continue.break;default:/All other error codes indicate a serious error has/occured.Process this error.port-ProcessErrorMessage(WaitCommEvent();break;else /If WaitCommEvent()returns TRUE,check to be sure there are/a
17、ctually bytes in the buffer to read./If you are reading more than one byte at a time from the buffer/(which this program does not do)you will have the situation occur/where the first byte to arrive will cause the WaitForMultipleObjects()/function to stop waiting.The WaitForMultipleObjects()function/
18、resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state/as it returns./If in the time between the reset of this event and the call to/ReadFile()more bytes arrive,the m_OverlappedStruct.hEvent handle will be set again/to the signeled state.When the call to ReadFile()occurs,it
19、will/read all of the bytes from the buffer,and the program will/loop back around to WaitCommEvent()./At this point you will be in the situation where m_OverlappedStruct.hEvent is set,/but there are no bytes available to read.If you proceed and call/ReadFile(),it will return immediatly due to the asy
20、nc port setup,but/GetOverlappedResults()will not return until the next character arrives./It is not desirable for the GetOverlappedResults()function to be in/this state.The thread shutdown event(event 0)and the WriteFile()/event(Event2)will not work if the thread is blocked by GetOverlappedResults()
21、./The solution to this is to check the buffer with a call to ClearCommError()./This call will reset the event handle,and if there are no bytes to read/we can loop back through WaitCommEvent()again,then proceed./If there are really bytes to read,do nothing and proceed.bResult=ClearCommError(port-m_hC
22、omm,&dwError,&comstat);if(comstat.cbInQue=0)continue;/end if bResult/Main wait function.This function will normally block the thread/until one of nine events occur that require action.Event=WaitForMultipleObjects(3,port-m_hEventArray,FALSE,INFINITE);switch(Event)case 0:/Shutdown event.This is event
23、zero so it will be/the higest priority and be serviced first.port-m_bThreadAlive=FALSE;/Kill this thread.break is not needed,but makes me feel better.AfxEndThread(100);break;case 1:/read event GetCommMask(port-m_hComm,&CommEvent);if(CommEvent&EV_CTS):SendMessage(port-m_pOwner-m_hWnd,WM_COMM_CTS_DETE
24、CTED,(WPARAM)0,(LPARAM)port-m_nPortNr);if(CommEvent&EV_RXFLAG):SendMessage(port-m_pOwner-m_hWnd,WM_COMM_RXFLAG_DETECTED,(WPARAM)0,(LPARAM)port-m_nPortNr);if(CommEvent&EV_BREAK):SendMessage(port-m_pOwner-m_hWnd,WM_COMM_BREAK_DETECTED,(WPARAM)0,(LPARAM)port-m_nPortNr);if(CommEvent&EV_ERR):SendMessage(
25、port-m_pOwner-m_hWnd,WM_COMM_ERR_DETECTED,(WPARAM)0,(LPARAM)port-m_nPortNr);if(CommEvent&EV_RING):SendMessage(port-m_pOwner-m_hWnd,WM_COMM_RING_DETECTED,(WPARAM)0,(LPARAM)port-m_nPortNr);if(CommEvent&EV_RXCHAR)/Receive character event from port.ReceiveChar(port,comstat);break;case 2:/write event /Wr
26、ite character event from port WriteChar(port);break;/end switch /close forever loop return 0;下列三个函数用于对串口线程进行启动、挂起和恢复:/start comm watching/BOOL CSerialPort:StartMonitoring()if(!(m_Thread=AfxBeginThread(CommThread,this)return FALSE;TRACE(Thread startedn);return TRUE;/Restart the comm thread/BOOL CSeri
27、alPort:RestartMonitoring()TRACE(Thread resumedn);m_Thread-ResumeThread();return TRUE;/Suspend the comm thread/BOOL CSerialPort:StopMonitoring()TRACE(Thread suspendedn);m_Thread-SuspendThread();return TRUE;3.3.4 读写串口下面一组函数是用户对串口进行读写操作的接口:/Write a character./void CSerialPort:WriteChar(CSerialPort*port
28、)BOOL bWrite=TRUE;BOOL bResult=TRUE;DWORD BytesSent=0;ResetEvent(port-m_hWriteEvent);/Gain ownership of the critical section EnterCriticalSection(&port-m_csCommunicationSync);if(bWrite)/Initailize variables port-m_ov.Offset=0;port-m_ov.OffsetHigh=0;/Clear buffer PurgeComm(port-m_hComm,PURGE_RXCLEAR|
29、PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);bResult=WriteFile(port-m_hComm,/Handle to COMM Port port-m_szWriteBuffer,/Pointer to message buffer in calling finction strlen(char*)port-m_szWriteBuffer),/Length of message to send&BytesSent,/Where to store the number of bytes sent&port-m_ov);/Overlapped s
30、tructure/deal with any error codes if(!bResult)DWORD dwError=GetLastError();switch(dwError)case ERROR_IO_PENDING:/continue to GetOverlappedResults()BytesSent=0;bWrite=FALSE;break;default:/all other error codes port-ProcessErrorMessage(WriteFile();else LeaveCriticalSection(&port-m_csCommunicationSync
31、);/end if(bWrite)if(!bWrite)bWrite=TRUE;bResult=GetOverlappedResult(port-m_hComm,/Handle to COMM port&port-m_ov,/Overlapped structure&BytesSent,/Stores number of bytes sent TRUE);/Wait flag LeaveCriticalSection(&port-m_csCommunicationSync);/deal with the error code if(!bResult)port-ProcessErrorMessa
32、ge(GetOverlappedResults()in WriteFile();/end if(!bWrite)/Verify that the data size send equals what we tried to send if(BytesSent!=strlen(char*)port-m_szWriteBuffer)TRACE(WARNING:WriteFile()error.Bytes Sent:%d;Message Length:%dn,BytesSent,strlen(char*)port-m_szWriteBuffer);/Character received.Inform
33、 the owner/void CSerialPort:ReceiveChar(CSerialPort*port,COMSTAT comstat)BOOL bRead=TRUE;BOOL bResult=TRUE;DWORD dwError=0;DWORD BytesRead=0;unsigned char RXBuff;for(;)/Gain ownership of the comm port critical section./This process guarantees no other part of this program/is using the port object.En
34、terCriticalSection(&port-m_csCommunicationSync);/ClearCommError()will update the COMSTAT structure and/clear any other errors.bResult=ClearCommError(port-m_hComm,&dwError,&comstat);LeaveCriticalSection(&port-m_csCommunicationSync);/start forever loop.I use this type of loop because I/do not know at
35、runtime how many loops this will have to/run.My solution is to start a forever loop and to/break out of it when I have processed all of the/data available.Be careful with this approach and/be sure your loop will exit./My reasons for this are not as clear in this sample/as it is in my production code
36、,but I have found this/solutiion to be the most efficient way to do this.if(comstat.cbInQue=0)/break out when all bytes have been read break;EnterCriticalSection(&port-m_csCommunicationSync);if(bRead)bResult=ReadFile(port-m_hComm,/Handle to COMM port&RXBuff,/RX Buffer Pointer 1,/Read one byte&BytesR
37、ead,/Stores number of bytes read&port-m_ov);/pointer to the m_ov structure/deal with the error code if(!bResult)switch(dwError=GetLastError()case ERROR_IO_PENDING:/asynchronous i/o is still in progress/Proceed on to GetOverlappedResults();bRead=FALSE;break;default:/Another error has occured.Process
38、this error.port-ProcessErrorMessage(ReadFile();break;else /ReadFile()returned complete.It is not necessary to call GetOverlappedResults()bRead=TRUE;/close if(bRead)if(!bRead)bRead=TRUE;bResult=GetOverlappedResult(port-m_hComm,/Handle to COMM port&port-m_ov,/Overlapped structure&BytesRead,/Stores num
39、ber of bytes read TRUE);/Wait flag/deal with the error code if(!bResult)port-ProcessErrorMessage(GetOverlappedResults()in ReadFile();/close if(!bRead)LeaveCriticalSection(&port-m_csCommunicationSync);/notify parent that a byte was received:SendMessage(port-m_pOwner)-m_hWnd,WM_COMM_RXCHAR,(WPARAM)RXB
40、uff,(LPARAM)port-m_nPortNr);/end forever loop /Write a string to the port/void CSerialPort:WriteToPort(char*string)assert(m_hComm!=0);memset(m_szWriteBuffer,0,sizeof(m_szWriteBuffer);strcpy(m_szWriteBuffer,string);/set event for write SetEvent(m_hWriteEvent);/Return the output buffer size/DWORD CSer
41、ialPort:GetWriteBufferSize()return m_nWriteBufferSize;3.3.5 控制接口应用程序员使用下列一组public 函数可以获取串口的DCB 及串口上发生的事件:/Return the device control block/DCB CSerialPort:GetDCB()return m_dcb;/Return the communication event masks/DWORD CSerialPort:GetCommEvents()return m_dwCommEvents;3.3.6 错误处理/If there is a error,g
42、ive the right message/void CSerialPort:ProcessErrorMessage(char*ErrorText)char*Temp=new char200;LPVOID lpMsgBuf;FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),/Default language(LPTSTR)&lpMsgBuf,0,NULL);sprintf(Temp
43、,WARNING:%s Failed with the following error:n%snPort:%dn,(char*)ErrorText,lpMsgBuf,m_nPortNr);MessageBox(NULL,Temp,Application Error,MB_ICONSTOP);LocalFree(lpMsgBuf);delete Temp;仔细分析 Remon Spekreijse的 CSerialPort类对我们理解多线程及其同步机制是大有益处的,从http:/ C+/Turbo C串口通信编程实践一书的作者龚建伟也编写了一个使用CSerialPort类的例子,可以从http:
44、/ 主监控线程这种方式指的是程序中使用一个主线程监控某特定端口,一旦在这个端口上发生连接请求,则主监控线程动态使用CreateThread派生出新的子线程处理该请求。主线程在派生子线程后不再对子线程加以控制和调度,而由子线程独自和客户方发生连接并处理异常。使用这种方法的优点是:(1)可以较快地实现原型设计,尤其在用户数目较少、连接保持时间较长时有表现较好;(2)主线程不与子线程发生通信,在一定程度上减少了系统资源的消耗。其缺点是:(1)生成和终止子线程的开销比较大;(2)对远端用户的控制较弱。这种多线程方式总的特点是动态生成,静态调度。4.2 线程池这种方式指的是主线程在初始化时静态地生成一定
45、数量的悬挂子线程,放置于线程池中。随后,主线程将对这些悬挂子线程进行动态调度。一旦客户发出连接请求,主线程将从线程池中查找一个悬挂的子线程:(1)如果找到,主线程将该连接分配给这个被发现的子线程。子线程从主线程处接管该连接,并与用户通信。当连接结束时,该子线程将自动悬挂,并进人线程池等待再次被调度;(2)如果当前已没有可用的子线程,主线程将通告发起连接的客户。使用这种方法进行设计的优点是:(1)主线程可以更好地对派生的子线程进行控制和调度;(2)对远程用户的监控和管理能力较强。虽然主线程对子线程的调度要消耗一定的资源,但是与主监控线程方式中派生和终止线程所要耗费的资源相比,要少很多。因此,使用该种方法设计和实现的系统在客户端连接和终止变更频繁时有上佳表现。这种多线程方式总的特点是静态生成,动态调度。