您的位置:首页 > 其它

A Simple RS-232 Serial Port Communication Program

2009-10-15 01:38 435 查看
Interaction between target device and PC through RS-232 serial port is proved to be very useful in test process automation. Therefore, the effectiveness and accuracy of the communication along with the design of the protocol is crucial to the success of the testing system. Although a lot of issues regarding relevant technical details which should surely be carefully dealt with may come up during the development of serial port communication program, there is no doubt that one should focus more on things like the deployment of the communcation and structure of the diagram rather than the nuts and bolts of the infrastructure such as the APIs. And only by prudently devising the scheme can one largely avoid those puzzles arises in the form of API inefficiencies, and even bugs.

Below is part of the code I wrote for the particular purpose of a project I'm working on. It now supports a demo program offering simple but promising testing interaction.

== ETermTransceiver.h ==

The Transceiver module deals with routine work of serial port communication and leaves the message parsing to the protocol module (derivate of ETermProtocol, which is not covered here)

#pragma once

#include <windows.h>
#include <queue>

#include "ETermProtocol.h"
#include "ETermException.h"

class ETermTransceiver : public ETermMessagePusher
{
public:
enum
{
k_Success = 0,
k_GenError = -1,
k_TimeOut = -3,
};

enum Event
{
k_Event_NoProtocol = -1,
k_Event_ConsultingError = -2,
k_Event_WaitCommEventError = -3,
k_Event_GetOverlappedResultError = -4,
k_Event_Message = -5,
};

typedef void (*EventHandler)(Event e, unsigned long data);

public:
ETermTransceiver(ETermProtocol *protocol);
ETermTransceiver(ETermProtocol *protocol, EventHandler eh);
~ETermTransceiver(void);

void SetProtocol(ETermProtocol *protocol);
void SetEventHandler(EventHandler eh);

void Init(int portindex = 1);
void Deinit();

/* EV_DSR, EV_CTS, EV_RXCHAR, EV_TXEMPTY, EV_RXFLAG */
BOOL GetCommState(DCB &dcb);
BOOL SetCommState(DCB &dcb);

BOOL GetCommMask(DWORD &mask);
BOOL SetCommMask(DWORD mask);

BOOL ConsultProtocol();

void Listen();
void Close();

DWORD SendString(char *str);
DWORD SendData(unsigned char *buf, int size);

int PopMessage(ETermMessage *&msg, DWORD miliseconds = INFINITE);
int ClearMessages(DWORD miliseconds = INFINITE);
bool HasMessage();

protected:
/* from ETermMessagePusher */
virtual void PushMessage(ETermMessage *msg);

private:
static DWORD __stdcall PortListen(LPVOID pPar);
void ProcessMessage(unsigned char *buf, int len);

private:
bool	m_bRunning;
bool	m_bInited;

HANDLE	m_hPort;
HANDLE	m_hCommEvent;

HANDLE	m_hMsgIn;
HANDLE	m_mtxMsgIn;
std::queue<ETermMessage *>	m_qMsgIn;

OVERLAPPED m_osReader;
OVERLAPPED m_osWriter;

HANDLE	m_hListener;

ETermProtocol	*m_Protocol;

EventHandler m_EventHandler;
};


== ETermTransceiver.cpp ==

#include "StdAfx.h"
#include "ETermTransceiver.h"

ETermTransceiver::ETermTransceiver(ETermProtocol *protocol)
{
m_bInited = false;
m_bRunning = false;

m_hPort = 0;
m_hCommEvent = 0;

m_hMsgIn = 0;
m_mtxMsgIn = 0;

memset(&m_osReader, 0, sizeof(m_osReader));
memset(&m_osWriter, 0, sizeof(m_osWriter));

m_hListener = 0;

SetProtocol(protocol);
m_EventHandler = NULL;
}

ETermTransceiver::ETermTransceiver(ETermProtocol *protocol, EventHandler eh)
{
m_bInited = false;
m_bRunning = false;

m_hPort = 0;
m_hCommEvent = 0;

m_hMsgIn = 0;
m_mtxMsgIn = 0;

memset(&m_osReader, 0, sizeof(m_osReader));
memset(&m_osWriter, 0, sizeof(m_osWriter));

m_hListener = 0;

SetProtocol(protocol);
SetEventHandler(eh);
}

ETermTransceiver::~ETermTransceiver(void)
{
Deinit();
}

void ETermTransceiver::SetProtocol(ETermProtocol *protocol)
{
if (m_bRunning)
{
throw ETermException("Error.ETermTransceiver.SetProtocol_OnRunning/n");
}
m_Protocol = protocol;
m_Protocol->AttachMessagePusher(this);
}

void ETermTransceiver::SetEventHandler(EventHandler eh)
{
if (m_bRunning)
{
throw ETermException("Error.ETermTransceiver.SetEventHandler_OnRunning/n");
}
m_EventHandler = eh;
}

void ETermTransceiver::Init(int portindex/*=1*/)
{
char portname[8];
if (m_bInited)
{
Deinit();
}
if (portindex > 64)
{
throw ETermException("Error.ETermTransceiver.InvalidPortIndex");
}

#if _MSC_VER >= 1400
sprintf_s(portname, "COM%d", portindex);
#else
sprintf(portname, "COM%d", portindex);
#endif

/* open port */
m_hPort = CreateFileA(portname, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

if ((signed int)m_hPort <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.OpenPort");
}

/* set parameter */
DCB dcb;
if (!::GetCommState(m_hPort, &dcb))
{
Deinit();
throw ETermException("Error.ETermTransceiver.GetCommState");
}

dcb.ByteSize = 8;
dcb.fRtsControl = 0;
dcb.BaudRate = CBR_115200;

if (!::SetCommState(m_hPort, &dcb))
{
Deinit();
throw ETermException("Error.ETermTransceiver.SetCommState");
}

if (!::SetCommMask(m_hPort, EV_RXCHAR))
{
Deinit();
throw ETermException("Error.ETermTransceiver.SetCommMask");
}

/* init overlapped */
memset(&m_osReader, 0, sizeof(m_osReader));
m_osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if ((signed int)m_osReader.hEvent <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.CreateEvent_Reader");
}

memset(&m_osWriter, 0, sizeof(m_osWriter));
m_osWriter.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if ((signed int)m_osWriter.hEvent <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.CreateEvent_Writer");
}

m_bRunning = false;
m_hCommEvent = 0;

m_mtxMsgIn = CreateMutex(NULL, FALSE, NULL);
if ((signed int)m_mtxMsgIn <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.CreateMutex_MsgIn");
}

m_hMsgIn = CreateEvent(NULL, FALSE, FALSE, NULL);
if ((signed int)m_hMsgIn <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.CreateEvent_MsgIn");
}

m_bInited = true;
}

void ETermTransceiver::Deinit()
{
Close();

if ((signed int)m_hMsgIn > 0)
{
CloseHandle(m_hMsgIn);
m_hMsgIn = 0;
}

if ((signed int)m_mtxMsgIn > 0)
{
CloseHandle(m_mtxMsgIn);
m_mtxMsgIn = 0;
}

if ((signed int)m_osReader.hEvent > 0)
{
CloseHandle(m_osReader.hEvent);
m_osReader.hEvent = 0;
}

if ((signed int)m_osWriter.hEvent > 0)
{
CloseHandle(m_osWriter.hEvent);
m_osWriter.hEvent = 0;
}

if ((signed int)m_hPort > 0)
{
CloseHandle(m_hPort);
m_hPort = 0;
}

while (!m_qMsgIn.empty())
{
ETermMessage *msg = m_qMsgIn.front();
if (msg)
{
delete msg;
}
m_qMsgIn.pop();
}

if (m_Protocol)
{
m_Protocol->Reset();
}

m_bInited = false;
}

BOOL ETermTransceiver::GetCommState(DCB &dcb)
{
return ::GetCommState(m_hPort, &dcb);
}

BOOL ETermTransceiver::SetCommState(DCB &dcb)
{
return ::SetCommState(m_hPort, &dcb);
}

BOOL ETermTransceiver::GetCommMask(DWORD &mask)
{
return ::GetCommMask(m_hPort, &mask);
}

BOOL ETermTransceiver::SetCommMask(DWORD mask)
{
return ::SetCommMask(m_hPort, mask);
}

BOOL ETermTransceiver::ConsultProtocol()
{
if (!m_Protocol)
{
return FALSE;
}

DCB dcb;
DWORD mask;
if (!GetCommState(dcb))
{
return FALSE;
}

if (m_Protocol->ModifyCommState(dcb))
{
if (!SetCommState(dcb))
{
return FALSE;
}
}

if (!GetCommMask(mask))
{
return FALSE;
}
if (m_Protocol->ModifyCommMask(mask))
{
if (!SetCommMask(mask))
{
return FALSE;
}
}

return TRUE;
}

void ETermTransceiver::Listen()
{
DWORD idThreadListen;
m_hListener = CreateThread(NULL, 0, PortListen, this, 0, &idThreadListen);
if ((signed int)m_hListener <= 0)
{
Deinit();
throw ETermException("Error.ETermTransceiver.CreateThread_Listener");
}
}

void ETermTransceiver::Close()
{
m_bRunning = false;

if ((signed int)m_hCommEvent > 0)
{
SetEvent(m_hCommEvent);
m_hCommEvent = 0;
}

if ((signed int)m_hListener > 0)
{
if (WaitForSingleObject(m_hListener, 5000) != 0)
{
TerminateThread(m_hListener, 0);
}
CloseHandle(m_hListener);
m_hListener = 0;
}
}

DWORD ETermTransceiver::SendString(char *str)
{
SendData((unsigned char*)str, strlen(str) + 1);

return 0;
}

DWORD ETermTransceiver::SendData(unsigned char *buf, int size)
{
DWORD nWritten;
WriteFile(m_hPort, buf, size, &nWritten, &m_osWriter);

return 0;
}

int ETermTransceiver::ClearMessages(DWORD miliseconds /* = INFINITE */)
{
DWORD dwRet = WaitForSingleObject(m_mtxMsgIn, miliseconds);
if (dwRet == WAIT_TIMEOUT)
{
return k_TimeOut;
}
else if (dwRet != 0)
{
return k_GenError;
}
while (!m_qMsgIn.empty())
{
ETermMessage *msg = m_qMsgIn.front();
if (msg)
{
delete msg;
}
m_qMsgIn.pop();
}
ReleaseMutex(m_mtxMsgIn);
return 0;
}

int ETermTransceiver::PopMessage(ETermMessage *&msg, DWORD miliseconds /*=INFINITE*/)
{
if (miliseconds >= 0xf0000000) miliseconds = INFINITE;

bool empty = true;
DWORD timeout = miliseconds;

DWORD t1 = ::timeGetTime();
DWORD t;
DWORD res = 0;

while (1)
{
if (miliseconds != INFINITE)
{
t = ::timeGetTime();
DWORD dt = t - t1;
timeout = miliseconds > dt? miliseconds - dt : 0;
}
res = WaitForSingleObject(m_mtxMsgIn, timeout);
if (res == WAIT_TIMEOUT)
{
return k_TimeOut;
}
else if (res != WAIT_OBJECT_0)
{
return k_GenError;
}

empty = m_qMsgIn.empty();

ReleaseMutex(m_mtxMsgIn);

if (!empty)
break;

if (miliseconds != INFINITE)
{
t = ::timeGetTime();
DWORD dt = t - t1;
timeout = miliseconds > dt? miliseconds - dt : 0;
}
res = WaitForSingleObject(m_hMsgIn, timeout);
if (res == WAIT_TIMEOUT)
{
return k_TimeOut;
}
else if (res != WAIT_OBJECT_0)
{
return k_GenError;
}
}

if (miliseconds != INFINITE)
{
t = ::timeGetTime();
DWORD dt = t - t1;
timeout = miliseconds > dt? miliseconds - dt : 0;
}
res = WaitForSingleObject(m_mtxMsgIn, timeout);
if (res == WAIT_TIMEOUT)
{
return k_TimeOut;
}
else if (res != WAIT_OBJECT_0)
{
return k_GenError;
}

msg = m_qMsgIn.front();

m_qMsgIn.pop();

ReleaseMutex(m_mtxMsgIn);

return k_Success;
}

bool ETermTransceiver::HasMessage()
{
return !m_qMsgIn.empty();
}

void ETermTransceiver::PushMessage(ETermMessage *msg)
{
WaitForSingleObject(m_mtxMsgIn, INFINITE);

m_qMsgIn.push(msg);
SetEvent(m_hMsgIn);

ReleaseMutex(m_mtxMsgIn);

if (m_EventHandler)
{
m_EventHandler(k_Event_Message, (unsigned long)msg);
}
}

DWORD __stdcall ETermTransceiver::PortListen(LPVOID pPar)
{
ETermTransceiver* thisobj = (ETermTransceiver*)pPar;

if (!thisobj->m_Protocol)
{
return -1; // No protocol
}

OVERLAPPED os;
memset(&os, 0, sizeof(os));
os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
thisobj->m_hCommEvent = os.hEvent;

DWORD nRead;
DWORD dwEvtMask;
DWORD dwErrFlags;
COMSTAT comstat;

BOOL bRet;

bool bWaitOnRead = false;
DWORD dwRet = 0;

thisobj->m_Protocol->Reset();
if (!thisobj->ConsultProtocol())
{
if (thisobj->m_EventHandler)
thisobj->m_EventHandler(k_Event_ConsultingError, 0);
dwRet = k_Event_ConsultingError;
goto bail;
}

thisobj->m_bRunning = true;
while (thisobj->m_bRunning)
{
ClearCommError(thisobj->m_hPort, &dwErrFlags, &comstat);

if (comstat.cbInQue)
{	// '/r' arrives
// read message

int buflen = comstat.cbInQue;
unsigned char *buf = new unsigned char[buflen];

ReadFile(thisobj->m_hPort, buf, buflen, &nRead, &os);

thisobj->m_Protocol->ProcessMessage(buf, comstat.cbInQue);

delete[] buf;

continue;
}

bRet = WaitCommEvent(thisobj->m_hPort, &dwEvtMask, &os);

if (!bRet)
{	// overlapped
dwRet = GetLastError();
if (dwRet == ERROR_IO_PENDING)
{
bRet = GetOverlappedResult(thisobj->m_hPort, &os, &nRead, TRUE);
if (!bRet)
{
if (thisobj->m_EventHandler)
thisobj->m_EventHandler(k_Event_GetOverlappedResultError, 0);
dwRet = k_Event_GetOverlappedResultError;
goto bail;
}
}
else
{
if (thisobj->m_EventHandler)
thisobj->m_EventHandler(k_Event_GetOverlappedResultError, dwRet);
dwRet = k_Event_GetOverlappedResultError;
goto bail;
}
}
}

bail:
thisobj->m_Protocol->Reset();
CloseHandle(os.hEvent);

return dwRet;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: