`
itfirefly
  • 浏览: 4752 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

C++ 串口读写

 
阅读更多
TSerialPort.h 

#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-sennd) sig 
nal changed state. 
#define WM_COMM_DSR_DETECTED WM_USER+3 // The DSR (data-set-reaady) si 
gnal changed state. 
#define WM_COMM_ERR_DETECTED WM_USER+4 // A line-status error ooccurre 
d. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 

#define WM_COMM_RING_DETECTED WM_USER+5 // A ring indicator was etec ted. 
#define WM_COMM_RLSD_DETECTED WM_USER+6 // The RLSD (receive-lin 
-sig 
nal-detect) signal changed state. 
#define WM_COMM_RXCHAR WM_USER+7 // A character w 
s received and pl 
aced in the input buffer. 
#define WM_COMM_RXFLAG_DETECTED WM_USER+8 // The event character w 
s 
received and placed in the input buffer. 
#define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in 
th 
e output buffer was sent. 

class TSerialPort 
{ 
public: 
// contruction and destruction 
TSerialPort(); 
virtual ~TSerialPort(); 

// port initialisation 
BOOL InitPort(TForm* pPortOwner, UINT portnr = 1, UINT baud = 
19200, 
char parity = 'N', UINT databits = 8, UINT stopsbits = 1, DWORD dwCom 
mEvents = 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 ProcessErrorMessage(char* ErrorText); 
static DWORD _stdcall CommThread(LPVOID pParam); 
static void ReceiveChar(TSerialPort* port, COMSTAT comstat); 
static void WriteChar(TSerialPort* port); 

// thread 
HANDLE m_HThread; 

// synchronisation objects 
CRITICAL_SECTION m_csCommunicationSync; 
BOOL m_bThreadAlive; 

// handles 
HANDLE m_hShutdownEvent; 
HANDLE m_hComm; 
HANDLE m_hWriteEvent; 

// Event array. 
// One element is used for each event. There are two event handles fo 
r each port. 
// A Write event and a receive character event which is located in th 
e overlapped structure (m_ov.hEvent). 
// There is a general shutdown when the port is closed. 
HANDLE m_hEventArray[3]; 

// structures 
OVERLAPPED m_ov; 
COMMTIMEOUTS m_CommTimeouts; 
DCB m_dcb; 

// owner window 
TForm* m_pOwner; 

// misc 
UINT m_nPortNr; 
char* m_szWriteBuffer; 
DWORD m_dwCommEvents; 
DWORD m_nWriteBufferSize; 
}; 

#endif __SERIALPORT_H__ 


TSerialPort.cpp 

#include 
#pragma hdrstop 
#include "SerialPort.h" 
#include 
#include 
#pragma package(smart_init) 
// 
// Constructor 
// 
TSerialPort::TSerialPort() 
{ 
m_hComm = NULL; 

// initialize 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 
// 
TSerialPort::~TSerialPort() 
{ 
do 
{ 
SetEvent(m_hShutdownEvent); 
} while (m_bThreadAlive); 

delete [] m_szWriteBuffer; 
} 

// 
// Initialize the port. This can be port 1 to 4. 
// 
BOOL TSerialPort::InitPort(TForm* pPortOwner, // the owner (CWnd) of t 
he port (receives message) 
UINT portnr, 
/ portnumber (1..4) 
UINT baud, 
/ baudrate 
char parity, 
/ parity 
UINT databits, 
/ databits 
UINT stopbits, 
/ stopbits 
DWORD dwCommEvents, // EV_RX 
HAR, EV_CTS etc 
UINT writebuffersize) 
/ size to the writebuffer 
{ 
assert(portnr > 0 && portnr < 5); 
assert(pPortOwner != NULL); 

// if the thread is alive: Kill 
if (m_bThreadAlive) 
{ 
do 
{ 
SetEvent(m_hShutdownEvent); 
} while (m_bThreadAlive); 
} 

// create events 
if (m_ov.hEvent != NULL) 
ResetEvent(m_ov.hEvent); 
m_ov.hEvent = CreateEvent(NULL, true, false, NULL); 

if (m_hWriteEvent != NULL) 
ResetEvent(m_hWriteEvent); 
m_hWriteEvent = CreateEvent(NULL, true, false, NULL); 

if (m_hShutdownEvent != NULL) 
ResetEvent(m_hShutdownEvent); 
m_hShutdownEvent = CreateEvent(NULL, true, false, NULL); 

// initialize the event objects 
m_hEventArray[0] = m_hShutdownEvent; // highest priority 
m_hEventArray[1] = m_ov.hEvent; 
m_hEventArray[2] = m_hWriteEvent; 

// initialize critical section 
InitializeCriticalSection(&m_csCommunicationSync); 

// set buffersize for writing and save the owner 
m_pOwner = pPortOwner; 

if (m_szWriteBuffer != NULL) 
delete [] m_szWriteBuffer; 
m_szWriteBuffer = new char[writebuffersize]; 

m_nPortNr = portnr; 

m_nWriteBufferSize = writebuffersize; 
m_dwCommEvents = dwCommEvents; 

BOOL bResult = false; 
char *szPort = new char[50]; 
char *szBaud = new char[50]; 

// now it critical! 
EnterCriticalSection(&m_csCommunicationSync); 

// if the port is already opened: close it 
if (m_hComm != NULL) 
{ 
CloseHandle(m_hComm); 
m_hComm = NULL; 
} 

// prepare port strings 
sprintf(szPort, "COM%d", portnr); 
sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, da 
tabits, stopbits); 

// get a handle to the port 
m_hComm = CreateFile(szPort, 
/ communication port string (COMX) 

GENERIC_READ | GENERIC_WRITE, 
/ read/write types 
0, 
/ comm devices must be opened with exclusive acce 
ss 
ss 
NULL, 
/ no security attributes 
OPEN_EXISTING, 
/ comm devices must use OPEN_EXISTING 
FILE_FLAG_OVERLAPPED, 
/ Async I/O 
0); 
/ template must be 0 for comm devices 

if (m_hComm == INVALID_HANDLE_VALUE) 
{ 
// port not found 
delete [] szPort; 
delete [] szBaud; 

return false; 
} 

// set the timeout values 
m_CommTimeouts.ReadIntervalTimeout = 1000; 
m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000; 
m_CommTimeouts.ReadTotalTimeoutConstant = 1000; 
m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000; 
m_CommTimeouts.WriteTotalTimeoutConstant = 1000; 

// configure 
if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) 
{ 
if (SetCommMask(m_hComm, dwCommEvents)) 
{ 
if (GetCommState(m_hComm, &m_dcb)) 
{ 
m_dcb.fRtsControl = RTS_CONTROL_ENABLE; 
/ set RTS bit high! 
if (BuildCommDCB(szBaud, &m_dcb)) 
{ 
if (SetCommState(m_hComm, &m_dcb)) 
; // normal operation... continu 

else 
ProcessErrorMessage("SetCommStat 
()"); 
} 
else 
ProcessErrorMessage("BuildCommDCB()"); 
} 
else 
ProcessErrorMessage("GetCommState()"); 
} 
else 
ProcessErrorMessage("SetCommMask()"); 
} 
else 
ProcessErrorMessage("SetCommTimeouts()"); 

delete [] szPort; 
delete [] szBaud; 

// flush the port 
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PU 
RGE_TXABORT); 

// release critical section 
LeaveCriticalSection(&m_csCommunicationSync); 

return true; 
} 

// 
// The CommThread Function. 
// 
DWORD _stdcall TSerialPort::CommThread(LPVOID pParam) 
{ 
// Cast the void pointer passed to the thread back to 
// a pointer of TSerialPort class 
TSerialPort *port = (TSerialPort*)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_R 
ABO 
RT | PURGE_TXABORT); 

// begin forever loop. This loop will run as long as the thread is a 
live. 
for (;;) 
{ 

// Make a call to WaitCommEvent(). This call will return immedi 
tly 

// because our port was created as an async port (FILE_FLAG_OVER 
APP 
ED 
// and an m_OverlappedStructerlapped structure specified). This 
cal 
l will cause the 
// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, 
whi 
ch is part of the m_hEventArray to 
// be placed in a non-signeled state if there are no bytes avail 
ble 
to be read, 
// or to a signeled state if there are bytes available. If this 
eve 
nt handle 
// is set to the non-signeled state, it will be set to signeled 
hen 
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 er 
or to dete 
rmin 
rmin 
// the reason.. 
switch (dwError = GetLastError()) 
{ 
case ERROR_IO_PENDING: 
{ 
// This is a normal return value if ther 
are no bytes 
// to read at the port. 
// Do nothing and continue 
break; 
} 
case 87: 
{ 
// Under Windows NT, this value is retur 
ed for some reason. 
// I have not investigated why, but it i 
also a valid reply 
// Also do nothing and continue. 
break; 
} 
default: 
{ 
// All other error codes indicate a seri 
us error has 
// occured. Process this error. 
port->ProcessErrorMessage("WaitCommEvent 
)"); 
break; 
} 
} 
} 
else 
{ 
// If WaitCommEvent() returns TRUE, check to be sure the 
e are 
// actually bytes in the buffer to read. 
// 
// If you are reading more than one byte at a time from 
he buffer 

// (which this program does not do) you will have the si 
uation occ 
ur 
// where the first byte to arrive will cause the WaitFor 
ultipleObj 
ects() 
// function to stop waiting. The WaitForMultipleObjects 
) function 

// resets the event handle in m_OverlappedStruct.hEvent 
o the non- 
signelead state 
// as it returns. 
// 
// If in the time between the reset of this event and th 
call to 
// ReadFile() more bytes arrive, the m_OverlappedStruct. 
Event hand 
le will be set again 
// to the signeled state. When the call to ReadFile() oc 
urs, it wi 
ll 
// read all of the bytes from the buffer, and the progra 
will 
// loop back around to WaitCommEvent(). 
// 
// At this point you will be in the situation where m_Ov 
rlappedStr 
uct.hEvent is set, 
// but there are no bytes available to read. If you pro 
eed and ca 
ll 
// ReadFile(), it will return immediatly due to the asyn 
port setu 
p, but 
// GetOverlappedResults() will not return until the next 
character 
arrives. 
// 
// It is not desirable for the GetOverlappedResults() fu 
ction to b 
e in 
// this state. The thread shutdown event (event 0) and 
he WriteFi 
le() 
// event (Event2) will not work if the thread is blocked 
by GetOver 
lappedResults(). 
// 
// The solution to this is to check the buffer with a ca 
l to Clear 
CommError(). 
// This call will reset the event handle, and if there a 
e no bytes 
to read 
// we can loop back through WaitCommEvent() again, then 
roceed. 
// If there are really bytes to read, do nothing and pro 
eed. 

bResult = ClearCommError(port->m_hComm, &dwError, &comst 
t); 

if (comstat.cbInQue == 0) 
continue; 
} // end if bResult 

// Main wait function. This function will normally block the th 
ead 


// until one of nine events occur that require action. 
Event = WaitForMultipleObjects(3, port->m_hEventArray, false, IN 
INI 
TE); 

switch (Event) 
{ 
case 0: 
{ 
// Shutdown event. This is event zero so it wil 
be 
// the higest priority and be serviced first. 

port->m_bThreadAlive = false; 

// Kill this thread. break is not needed, but m 
kes me feel bette 
r. 
ExitThread(100); 
break; 
} 
case 1: // read event 
{ 
GetCommMask(port->m_hComm, &CommEvent); 
if (CommEvent & EV_CTS) 
::SendMessage(port->m_pOwner->Handle, WM 
COMM_CTS_DETECTED, (WPAR 
AM) 0, (LPARAM) port->m_nPortNr); 
if (CommEvent & EV_RXFLAG) 
::SendMessage(port->m_pOwner->Handle, WM 
COMM_RXFLAG_DETECTED, (W 
PARAM) 0, (LPARAM) port->m_nPortNr); 
if (CommEvent & EV_BREAK) 
::SendMessage(port->m_pOwner->Handle, WM 
COMM_BREAK_DETECTED, (WP 
ARAM) 0, (LPARAM) port->m_nPortNr); 
if (CommEvent & EV_ERR) 
::SendMessage(port->m_pOwner->Handle, WM 
COMM_ERR_DETECTED, (WPAR 
AM) 0, (LPARAM) port->m_nPortNr); 
if (CommEvent & EV_RING) 
::SendMessage(port->m_pOwner->Handle, WM 
COMM_RING_DETECTED, (WPA 
RAM) 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 TSerialPort::StartMonitoring() 
{ 
DWORD lpThreadId; 
m_HThread =CreateThread(NULL, 
0, 
CommThread, 
this, 
0, 
&lpThreadId); 
if(m_HThread==NULL) 
{ 
ProcessErrorMessage("Create Thread Error"); 
return false; 
} 
return true; 
} 

// 
// Restart the comm thread 
// 
// 
BOOL TSerialPort::RestartMonitoring() 
{ 
ResumeThread(m_HThread); 
return true; 
} 

// 
// Suspend the comm thread 
// 
BOOL TSerialPort::StopMonitoring() 
{ 
SuspendThread(m_HThread); 
return true; 
} 

// 
// If there is a error, give the right message 
// 
void TSerialPort::ProcessErrorMessage(char* ErrorText) 
{ 
char *Temp = new char[200]; 


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, "WARNING: %s Failed with the following error: \n%s\nPo 
rt: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr); 
Application->MessageBox(Temp, "Application Error", MB_ICONSTOP); 

LocalFree(lpMsgBuf); 
delete[] Temp; 
} 

// 
// Write a character. 
// 
void TSerialPort::WriteChar(TSerialPort* 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_R 
ABO 
ABO 
RT | PURGE_TXABORT); 

bResult = WriteFile(port->m_hComm, 
/ Handle to COMM Port 
port->m_szWriteBuffer, / 
/ Pointer to message buffer in call 
ing finction 
strlen((char*)port->m_sz 
riteBuffer), // Length of message to s 
end 
&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 GetOverlappedResu 
ts() 
BytesSent = 0; 
bWrite = false; 
break; 
} 
default: 
{ 
// all other error codes 
port->ProcessErrorMessage("Write 
ile()"); 
} 
} 
} 
else 
{ 
LeaveCriticalSection(&port->m_csCommunicationSync); 
} 
} // end if(bWrite) 

if (!bWrite) 
{ 
bWrite = true; 

bResult = GetOverlappedResult(port->m_hComm, // Handle to COM 
port 

&port- 
m_ov, // Overlapped structure 
&Bytes 
ent, // Stores number of bytes sent 
true); 
// Wait flag 

LeaveCriticalSection(&port->m_csCommunicationSync); 

// deal with the error code 
if (!bResult) 
{ 
port->ProcessErrorMessage("GetOverlappedResults() in Wri 
eFile()"); 

} 
} // end if (!bWrite) 

// Verify that the data size send equals what we tried to send 
if (BytesSent != strlen((char*)port->m_szWriteBuffer)) 
{ 
printf("WARNING: WriteFile() error.. Bytes Sent: %d; Message Len 
th: 
%d\n", BytesSent, strlen((char*)port->m_szWriteBuffer)); 
} 
} 

// 
// Character received. Inform the owner 
// 
void TSerialPort::ReceiveChar(TSerialPort* 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, // Handl 
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 s 
ill in progress 
// Proceed on to GetOver 
appedResults(); 
bRead = false; 
break; 
} 
default: 
{ 
// Another error has occ 
red. Process this error. 
port->ProcessErrorMessag 
("ReadFile()"); 
break; 
} 
} 
} 
else 
{ 
// ReadFile() returned complete. It is not neces 
ary to call GetOv 
erlappedResults() 
bRead = true; 
} 
} // close if (bRead) 

if (!bRead) 
{ 
bRead = true; 
bResult = GetOverlappedResult(port->m_hComm, // Handl 
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)->Handle, WM_COMM_RXCHAR, (WPARAM) 
RXB 
uff, (LPARAM) port->m_nPortNr); 
} // end forever loop 

} 


// 
// Write a string to the port 
// 
void TSerialPort::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 device control block 
// 
DCB TSerialPort::GetDCB() 
{ 
return m_dcb; 
} 


// 
// Return the communication event masks 
// 
DWORD TSerialPort::GetCommEvents() 
{ 
return m_dwCommEvents; 
} 

// 
// Return the output buffer size 
// 
DWORD TSerialPort::GetWriteBufferSize() 
{ 
return m_nWriteBufferSize; 
} 

//----------------------------------------------------------------

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics