| |
深入浅出Win32多线程程序设计之综合实例 |
|
时间: 2005-12-23 来自:天极开发 |
 |
|
3.3.3核心函数:串口线程控制函数
串口线程处理函数是整个类中最核心的部分,它主要完成两类工作:
(1)利用WaitCommEvent函数对串口上发生的事件进行获取并根据事件的不同类型进行相应的处理;
(2)利用WaitForMultipleObjects函数对串口相关的用户控制事件进行等待并做相应处理。
UINT CSerialPort::CommThread(LPVOID pParam) { // Cast the void
pointer passed to the thread back to // a pointer of CSerialPort
class CSerialPort *port = (CSerialPort*)pParam;
// Set the
status variable in the dialog class to // TRUE to indicate the thread
is running. port->m_bThreadAlive = TRUE;
// Misc.
variables DWORD BytesTransfered = 0; DWORD Event = 0; DWORD
CommEvent = 0; DWORD dwError = 0; COMSTAT 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 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 state 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 (!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 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 //
actually 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 //
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
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 async
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(). // // 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_hComm, &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 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_DETECTED, (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(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 { // Write 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
started\n"); return TRUE; }
// // Restart the comm
thread // BOOL
CSerialPort::RestartMonitoring() { TRACE("Thread
resumed\n"); m_Thread->ResumeThread(); return
TRUE; }
// // Suspend the comm thread // BOOL
CSerialPort::StopMonitoring() { TRACE("Thread
suspended\n"); m_Thread->SuspendThread(); return
TRUE; } | 3.3.4读写串口
下面一组函数是用户对串口进行读写操作的接口:
// // Write a character. // void
CSerialPort::WriteChar(CSerialPort *port) { 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 | 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 structure
//
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); } }
// 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->ProcessErrorMessage("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: %d\n", BytesSent,
strlen((char*)port->m_szWriteBuffer)); } }
// //
Character received. Inform 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.
EnterCriticalSection(&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 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, 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 &BytesRead, // 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 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 number 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)RXBuff,(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
CSerialPort::GetWriteBufferSize() { return
m_nWriteBufferSize; } |
|
|
|
|
|
|
|
|