⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 comm.cpp

📁 串口调试助手的源代码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
// From Win32 SDK's TTY Sample & TAPICOMM Sample
// Modify by JHCC, 1996-1997

#include "stdafx.h"
#include "comm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_SERIAL(CCommInfo, CObject, 0)

// constant definitions
const int	cRXQueue = 4096;
const int	cTXQueue = 4096;

CString	CCommInfo::m_strCommNameFmt = _T("COM%d");

CCommInfo::CCommInfo()
{
	m_hCommFile = 0;

	m_bRTS = FALSE;
}

CCommInfo::~CCommInfo()
{
	if (m_bConnected)
		CloseConnection();
}

// for the Comm Device already opened, like TAPI
BOOL	CCommInfo::Create(CWnd*  pNotifyWnd, DWORD  dwCommBaseCommandID,
	BYTE*  pReadBuf, int  nReadBufLen, HANDLE  hCommFile)
{
	// Are we already doing comm?
	if (m_hCommFile != NULL)
	{
		TRACE0("Already have a comm file open\n");
		return  FALSE;
	}
	m_bConnected = FALSE;

	m_pNotifyWnd = pNotifyWnd;
	m_dwCommBaseCommandID = dwCommBaseCommandID;
	m_pReadBuf = pReadBuf;
	m_nReadBufLen = nReadBufLen;

	// Is this a valid comm handle?
	if (hCommFile && ::GetFileType(hCommFile) != FILE_TYPE_CHAR)
	{
		TRACE0("File handle is not a comm handle.\n");
		return  FALSE;
	}
	m_hCommFile = hCommFile;
	return  TRUE;
}
	
// for manual opened Comm Device
BOOL	CCommInfo::Create(CWnd*  pNotifyWnd, DWORD  dwCommBaseCommandID,
	BYTE*  pReadBuf, int  nReadBufLen, int  nPort,
	DWORD  dwBaudRate, BYTE  byteFlowCtrl, BYTE  byteDataBits,
	BYTE  byteStopBits, BYTE  byteParity, BOOL  bRTS)
{
	// Are we already doing comm?
	if (m_hCommFile != NULL)
	{
		TRACE0("Already have a comm file open\n");
		return  FALSE;
	}
	m_bConnected = FALSE;

	// special for connection with DIANTAI ? 
	m_bRTS = bRTS;

	m_pNotifyWnd = pNotifyWnd;
	m_dwCommBaseCommandID = dwCommBaseCommandID;
	m_pReadBuf = pReadBuf;
	m_nReadBufLen = nReadBufLen;

	m_nPort = nPort;
	m_dwBaudRate = dwBaudRate;
	m_byteFlowCtrl = byteFlowCtrl;

	// for most comm application
	m_byteDataBits = byteDataBits;
	m_byteParity = byteParity;
	m_byteStopBits = byteStopBits;
	return  TRUE;
}

// Stop and end all communication threads.
// Tries to gracefully signal all communication threads to
// close, but terminates them if it has to.
BOOL	CCommInfo::CloseConnection(void)
{
	// No need to continue if we're not communicating.
	if (m_hCommFile == 0)
	{
		TRACE0("The Comm File is not opened !\n"); 
		return  TRUE;
	}
	TRACE0("Stopping the Comm\n");

	// set connected flag to FALSE
	m_bConnected = FALSE;	// halt the thread

	// disable event notification and wait for thread to halt
	::SetCommMask(m_hCommFile, 0);

	// Close the threads.
	CloseReadThread();
	CloseWriteThread();

	// drop DTR
	::EscapeCommFunction(m_hCommFile, CLRDTR);	// ???

	// Close the threads.
//	CloseReadThread();
//	CloseWriteThread();

	// Not needed anymore.
	::CloseHandle(m_hCloseEvent);
	::CloseHandle(m_hPostEvent);

	// Now close the comm port handle.
	if (m_hCommFile)
	{
		::CloseHandle(m_hCommFile);
		m_hCommFile = 0;
	}
	return  TRUE;
}

// Win-32 Porting Issues:
// - OpenComm() is not supported under Win-32.
//   Use CreateFile() and setup for OVERLAPPED_IO.
// - Win-32 has specific communication timeout parameters.
// - Created the secondary thread for event notification.
BOOL	CCommInfo::OpenConnection(void)
{ian
	DCB	dcb;
	COMMTIMEOUTS	CommTimeOuts;
	if (m_hCommFile == 0)
	{
		CString	strPortName;
		strPortName.Format(m_strCommNameFmt, m_nPort);

		// open COMM device
		if ((m_hCommFile =
			::CreateFile(
				strPortName, GENERIC_READ | GENERIC_WRITE,
				0,	// exclusive access
				NULL,	// no security attrs
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,	// overlapped I/O
				NULL)) == INVALID_HANDLE_VALUE)
		{
			TRACE0("Open the Comm Port Eror !\n");
			return  FALSE;
		}

		CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
		CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
		CommTimeOuts.ReadTotalTimeoutConstant = 1000;
		CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
		CommTimeOuts.WriteTotalTimeoutConstant = 1000;

		// get any early notifications
		::SetCommMask(m_hCommFile, EV_RXCHAR);
		// setup device buffers
		::SetupComm(m_hCommFile, cRXQueue, cTXQueue);
		// purge any information in the buffer
		::PurgeComm(m_hCommFile, 
			PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

		dcb.DCBlength = sizeof(DCB);
		::GetCommState(m_hCommFile, &dcb);

		dcb.BaudRate = m_dwBaudRate;
		dcb.ByteSize = m_byteDataBits;
		dcb.Parity = m_byteParity;
		dcb.StopBits = m_byteStopBits;

		// setup hardware flow control
		// configuration of the hardware handshaking lines.
		BYTE	bSet = (BYTE)((m_byteFlowCtrl & PCF_DTRDSR) != 0);
			dcb.fOutxDsrFlow = bSet;
		if (bSet)
			dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
		else
			dcb.fDtrControl = DTR_CONTROL_ENABLE;

		bSet = (BYTE)((m_byteFlowCtrl & PCF_RTSCTS) != 0);
		dcb.fOutxCtsFlow = bSet;
		if (bSet)
			dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
		else
			dcb.fRtsControl = RTS_CONTROL_ENABLE;

		// setup software flow control
		bSet = (BYTE)((m_byteFlowCtrl & PCF_XONXOFF) != 0);
		dcb.fInX = dcb.fOutX = bSet;
		dcb.XonChar = ASCII_XON;
		dcb.XoffChar = ASCII_XOFF;
		dcb.XonLim = 100;
		dcb.XoffLim = 100;

		// other various settings
		dcb.fBinary = TRUE;
		dcb.fParity = TRUE;
	}
	else	// already opened !
	{
		// The CommTimeout numbers will very likely change if you are
		// coding to meet some kind of specification where
		// you need to reply within a certain amount of time after
		// recieving the last byte.  However,  If 1/4th of a second
		// goes by between recieving two characters, its a good 
		// indication that the transmitting end has finished, even
		// assuming a 1200 baud modem.
		GetCommTimeouts(m_hCommFile, &CommTimeOuts);
		CommTimeOuts.ReadIntervalTimeout = 250;
		CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
		CommTimeOuts.ReadTotalTimeoutConstant = 0;
		CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
		CommTimeOuts.WriteTotalTimeoutConstant = 0;

		// fAbortOnError is the only DCB dependancy in Tapi.
		// Can't guarentee that the SP will set this to what we expect.
		GetCommState(m_hCommFile, &dcb);
		dcb.fAbortOnError = FALSE;

		// Setting and querying the comm port configurations.
		{ 
			// Configure the comm settings.
			COMMPROP	commprop;
			DWORD	fdwEvtMask;

			// These are here just so you can set a breakpoint
			// and see what the comm settings are.  Most Comm settings
			// are already set through TAPI.
			GetCommProperties(m_hCommFile, &commprop);
			GetCommMask(m_hCommFile, &fdwEvtMask);
		}
	}
	if (!::SetCommState(m_hCommFile, &dcb))
		return  FALSE;
	// set up for overlapped I/O 
	::SetCommTimeouts(m_hCommFile, &CommTimeOuts);

	m_bConnected = TRUE;

	// Create the event that will signal the threads to close.
	m_hCloseEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	if (m_hCloseEvent == NULL)
	{
		TRACE0("Unable to CreateEvent m_hCloseEvent\n");
		m_hCommFile = NULL;
		return  FALSE;
	}

	m_hPostEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
	if (m_hPostEvent == NULL)
	{
		TRACE0("Unable to CreateEvent m_hPostEvent\n");
		m_hCommFile = NULL;
		return  FALSE;
	}

	// Create the Read thread.
	m_pReadThread = 
		::AfxBeginThread((AFX_THREADPROC)CCommInfo::ReadThreadProc,	// pfnThreadProc
			(LPVOID)this,	// pParam
			THREAD_PRIORITY_HIGHEST,	// nPriority
			(UINT)0,	// nStackSize
			(DWORD)CREATE_SUSPENDED,	// dwCreateFlags,
			(LPSECURITY_ATTRIBUTES)NULL	// lpSecurityAttrs
			);
	m_pReadThread->m_bAutoDelete = FALSE;
/*	m_hReadThread =
		::CreateThread(
			(LPSECURITY_ATTRIBUTES)NULL,
			0,
			(LPTHREAD_START_ROUTINE)CCommInfo::ReadThreadProc,
			(LPVOID)this,
			0,
			&m_dwReadThreadID);
	if (m_hReadThread == NULL)*/
	if (m_pReadThread == NULL)
	{
		TRACE0("Unable to create Read thread \n");

//		m_dwReadThreadID = 0;
		::CloseHandle(m_hCommFile);
		m_hCommFile = 0;
		return  FALSE;
	}

	// assert DTR
	::EscapeCommFunction(m_hCommFile, SETDTR);
    
	// Comm threads should to have a higher base priority than the UI thread.
	// If they don't, then any temporary priority boost the UI thread gains
	// could cause the COMM threads to loose data.
//	::SetThreadPriority(m_hReadThread, THREAD_PRIORITY_HIGHEST);
	m_pReadThread->ResumeThread();

	// Create the Write thread.
	m_pWriteThread = 
		::AfxBeginThread((AFX_THREADPROC)CCommInfo::WriteThreadProc,	// pfnThreadProc
			(LPVOID)this,	// pParam
			THREAD_PRIORITY_HIGHEST,	// nPriority
			(UINT)0,	// nStackSize
			(DWORD)CREATE_SUSPENDED,	// dwCreateFlags,
			(LPSECURITY_ATTRIBUTES)NULL	// lpSecurityAttrs
			);
	m_pWriteThread->m_bAutoDelete = FALSE;

/*
	m_hWriteThread = 
		::CreateThread(
			(LPSECURITY_ATTRIBUTES)NULL,
			0,
			(LPTHREAD_START_ROUTINE)CCommInfo::WriteThreadProc,
			(LPVOID)this,
			0,
			&m_dwWriteThreadID);
        
	if (m_hWriteThread == NULL)*/
	if (m_pWriteThread == NULL)
	{
		TRACE0("Unable to create Write thread \n");
		CloseReadThread();
//		m_dwWriteThreadID = 0;
		::CloseHandle(m_hCommFile);
		m_hCommFile = 0;
		return  FALSE;
	}
//	::SetThreadPriority(m_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);
	m_pWriteThread->ResumeThread();

	// special for connection with DIANTAI ? 
	if (m_bRTS)
	{
		::EscapeCommFunction(m_hCommFile, CLRRTS);
		::EscapeCommFunction(m_hCommFile, CLRDTR);
	}

	// Everything was created ok. Ready to go!
	return  TRUE;
}

// Send a String to the Write Thread to be written to the Comm.
// This is a wrapper function so that other modules don't care that
// Comm writing is done via PostMessage to a Write thread.  Note that
// using PostMessage speeds up response to the UI (very little delay to
// 'write' a string) and provides a natural buffer if the comm is slow
// (ie:  the messages just pile up in the message queue).
BOOL	CCommInfo::WriteCommBlock(LPCTSTR  lpByte, DWORD  dwBytesToWrite)
{
	if (m_pWriteThread)
	{
		if (m_pWriteThread->PostThreadMessage(/*m_dwWriteThreadID, */
					PWM_COMMWRITE,(WPARAM)dwBytesToWrite, (LPARAM)lpByte))
		{
			return  TRUE;
		}
		else
		{
			switch (::GetLastError())
			{
			case ERROR_INVALID_THREAD_ID:
				TRACE0("idThread is not a valid thread identifier, "
					"or if the thread specified by idThread does not have a message queue !\n");
				break;

			default:
				TRACE0("Failed to Post to Write thread.\n");
			}
		}
	}
	else
		TRACE0("Write thread not created\n");

	return  FALSE;
}

// Closes the Read thread by signaling the CloseEvent.
// Purges any outstanding reads on the comm port.
//
// Note that terminating a thread leaks memory (read the docs).
// Besides the normal leak incurred, there is an event object
// that doesn't get closed. This isn't worth worrying about 
// since it shouldn't happen anyway.
void	CCommInfo::CloseReadThread(void)
{
	// If it exists...
//	if (m_hReadThread)
	if (m_pReadThread)
	{
		TRACE0("Closing Read Thread\n");

		// Signal the event to close the worker threads.
		::SetEvent(m_hCloseEvent);

		// Purge all outstanding reads
		::PurgeComm(m_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);

		// Wait 10 seconds for it to exit.  Shouldn't happen.
//		if (::WaitForSingleObject(m_hReadThread, 10000) == WAIT_TIMEOUT)
		if (::WaitForSingleObject(m_pReadThread->m_hThread, 10000) == WAIT_TIMEOUT)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -