MFC 串口通讯程序

用惯了C#、VB、Java之类东西制作桌面程序,虽然以前自学过VC,却用的极少,基本不知道VC是怎么设计程序的。近期一个项目需要制作一个串口转TCP/IP的功能,决定使用MFC。本文的串口操作类参考的网上源码,特在此感谢同行的付出。

一、建立项目

在VS2012里,新建项目,选择其他语言、Visual C++、MFC,

1.名称填入SerialTest,确定。

应用程序类型选择 :基于对话框

高级功能全不选。

2、设置Unicode

在项目上右键选择属性-配置属性-常规-项目默认值

字符集改为未设置。

二、绘制界面:

主要放置端口combobox、打开/关闭串口按钮,其它还有一些设置项,这里不一一列举。

三、编写程序

从网上查询得知,一般串口通讯有两种方法,一种基于Active控件,一种基于Win32API。本次测试使用API。

1。遍历串口:

void CSerialTestDlg::FindAllCom()
{
	HKEY hKey;  
	int   i=0;
	TCHAR   portName[256],commName[256];  
	DWORD   dwLong,dwSize;

	int rtn = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Hardware\\DeviceMap\\SerialComm",NULL, KEY_READ, &hKey);//打开串口注册表
	if( rtn == ERROR_SUCCESS)     
	{
		while(TRUE)   
		{
			dwLong = dwSize=sizeof(portName);   
			memset(portName, 0, sizeof(portName));
			memset(commName, 0, sizeof(commName));

			rtn = RegEnumValue( hKey, i, portName, &dwLong,NULL, NULL, (PUCHAR)commName, &dwSize );

			if( rtn == ERROR_NO_MORE_ITEMS )   //   枚举串口   
				break;

			m_combo_com_list.AddString(commName);
			i++;   
		}   
		if(m_combo_com_list.GetCount()==0){
			//没有找到串口
		}
		RegCloseKey(hKey);   
	}  
}
 
 

2.打开/关闭串口

	//关闭串口
	virtual void Close()
	{
		if(IsOpen()){
				PurgeComm(_hCommHandle,PURGE_TXABORT | PURGE_TXCLEAR);
				EndThread();
				::CloseHandle(_hCommHandle);
				_hCommHandle = INVALID_HANDLE_VALUE;
		}
	}
       //打开串口
	bool Open(DWORD dwPort,DWORD dwBaudRate)
	{
		if(dwPort<1 || dwPort >1024) return false;
		BindCommPort(dwPort);
		if(!OpenCommPort()) return false;
		if(!SetupPort()) return false;
		return SetState(dwBaudRate);
	}

3.参数设置

//设置串口参数 DCB
	bool SetState(DCB *pdcb = NULL)
	{
		return IsOpen() ? ::SetCommState(_hCommHandle, pdcb == NULL ? &_DCB:pdcb) == TRUE: false;
	}
//设置串口参数:波特率,停止位,等 支持设置字符串 "9600, 8, n, 1"
	bool SetState(char *szSetStr)
	{
		if (IsOpen())
		{
			if (::GetCommState(_hCommHandle, &_DCB) != TRUE)
			return false;
			if (::BuildCommDCB(szSetStr, &_DCB) != TRUE)
			return false;
			return ::SetCommState(_hCommHandle, &_DCB) == TRUE;
		}
		return false;
	}
	//设置串口参数:波特率,停止位,等
	bool SetState(DWORD dwBaudRate, DWORD dwByteSize = 8, DWORD dwParity =NOPARITY, DWORD dwStopBits = ONESTOPBIT)
	{
		if (IsOpen())
		{
			if (::GetCommState(_hCommHandle, &_DCB) != TRUE)
			return false;
			_DCB.BaudRate = dwBaudRate;
			_DCB.ByteSize = (unsigned char)dwByteSize;
			_DCB.Parity = (unsigned char)dwParity;
			_DCB.StopBits = (unsigned char)dwStopBits;
			return ::SetCommState(_hCommHandle, &_DCB) == TRUE;
		}
		return false;
	}

4.绑定端口

	//绑定端口
	void BindCommPort(DWORD dwPort){
		assert(dwPort >=1 && dwPort <=1024);
		char p[5];
		_dwPort=dwPort;
		strcpy_s(_szCommStr,"\\\\.\\COM");
		_ltoa_s(_dwPort,p,10);  //转字符串
		strcat_s(_szCommStr,p); //组合\\.\COM1 类似字符串
	}


5.读写

	//读取串口 dwBufferLength个字符到 Buffer 返回实际读到的字符数 可读任意数据
	DWORD Read(LPVOID Buffer, DWORD dwBufferLength, DWORD dwWaitTime = 10)
	{
		if (!IsOpen()) return 0;
		COMSTAT Stat;
		DWORD dwError;
		if (::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0)
		{
			::PurgeComm(_hCommHandle,PURGE_RXABORT | PURGE_RXCLEAR);
			return 0;
		}
		if (!Stat.cbInQue) return 0;   // 缓冲区无数据
		unsigned long uReadLength = 0;
		dwBufferLength = dwBufferLength > Stat.cbInQue ? Stat.cbInQue :dwBufferLength;
		if (!::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength,&_ReadOverlapped))
		{
			if (::GetLastError() == ERROR_IO_PENDING)
			{
				WaitForSingleObject(_ReadOverlapped.hEvent, dwWaitTime);
			// 结束异步I/O
				if (!::GetOverlappedResult(_hCommHandle, &_ReadOverlapped,&uReadLength, false))
				{
					if (::GetLastError() != ERROR_IO_INCOMPLETE) uReadLength = 0;
				}
			}
			else	uReadLength = 0;
		}
			return uReadLength;
	}
//读取串口 dwBufferLength - 1 个字符到 szBuffer 返回ANSI C 模式字符串指针 适合一般字符通讯
	char *ReadString(char *szBuffer, DWORD dwBufferLength, DWORD dwWaitTime =20)
	{
		unsigned long uReadLength = Read(szBuffer, dwBufferLength - 1,dwWaitTime);
		szBuffer[uReadLength] = '\0';
		return szBuffer;
	}
//写串口 可写任意数据 "abcd" or "\x0\x1\x2"
	DWORD Write(LPVOID Buffer, DWORD dwBufferLength)
	{
		if (!IsOpen())
		return 0;
		DWORD dwError;
		if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0)
		::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR);
		unsigned long uWriteLength = 0;
		if (!::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength,&_WriteOverlapped))
		if (::GetLastError() != ERROR_IO_PENDING)
		uWriteLength = 0;
		return uWriteLength;
	}
//写串口 写ANSI C 模式字符串指针 
	DWORD Write(const char *szBuffer)
	{
		assert(szBuffer);
		return Write((void*)szBuffer, strlen(szBuffer));
	}
//读串口 同步应用
	DWORD ReadSync(LPVOID Buffer, DWORD dwBufferLength)
	{
		if (!IsOpen())
		return 0;
		DWORD dwError;
		if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0)
		{
		::PurgeComm(_hCommHandle,PURGE_RXABORT | PURGE_RXCLEAR);
		return 0;
		}
		DWORD uReadLength = 0;
		::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, NULL);
		return uReadLength;
	}
//写串口 同步应用
	DWORD WriteSync(LPVOID Buffer, DWORD dwBufferLength)
	{
		if (!IsOpen())
		return 0;
		DWORD dwError;
		if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0)
		::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR);
		unsigned long uWriteLength = 0;
		::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, NULL);
		return uWriteLength;
	}
//写串口 szBuffer 可以输出格式字符串 包含缓冲区长度
	DWORD Write(char *szBuffer, DWORD dwBufferLength, char *szFormat, ...)
	{
		if (!IsOpen())
		return 0;
		va_list va;
		va_start(va, szFormat);
		//_vsnprintf(szBuffer, dwBufferLength, szFormat, va);
		_vsnprintf_s(szBuffer,dwBufferLength,_TRUNCATE,szFormat,va);
		va_end(va);
		return Write(szBuffer);
	}

//写串口 szBuffer 可以输出格式字符串 不检查缓冲区长度 小心溢出
	DWORD Write(char *szBuffer, char *szFormat, ...)
	{
		if (!IsOpen()) 	return 0;
		va_list va;
		va_start(va, szFormat);
		//vsprintf(szBuffer, szFormat, va);
		int  len = _vscprintf( szFormat, szBuffer ) // _vscprintf doesn't count
                               + 1; // terminating '\0'
		
		//char * buffer =(char*) malloc( len * sizeof(char) );
		vsprintf_s(szBuffer,len, szFormat,va);
		va_end(va);
		return Write(szBuffer);
	}

四、DCB结构

typedef struct _DCB

{ DWORD DCBlength;

DWORD BaudRate; //波特率
DWORD fBinary :1; 
DWORD fParity :1; //是否奇偶校验
DWORD fOutxCtsFlow :1; // CTS output flow control 指定CTS是否用于检测发送控制。当为TRUE时CTS为OFF,发送将被挂起。(发送清除)
DWORD fOutxDsrFlow :1; // DSR output flow control 指定DSR是否用于检测发送控制。(数据装备好) 当为TRUE是DSR为OFF,发送将被挂起。
DWORD fDtrControl :2; // DTR flow control type 
//DTR_CONTROL_DISABLE值将DTR置为OFF, 

//DTR_CONTROL_ENABLE值将DTR置为ON, 

//DTR_CONTROL_HANDSHAKE 允许DTR"握手",
DWORD fDsrSensitivity :1; //若为TRUE,通讯驱动程序对DSR信号状态敏感。驱动程序将忽略任何接收的字节数,除非DSR调制解调器的输入线为高。
DWORD fTXContinueOnXoff :1; //为TRUE,输入缓冲区内字节已经满XoffLim及驱动程序已经发送XoffChar停止接收字节时,仍然继续发送。为FALSE,输入缓冲区内XonLim是空的,及驱动程序已经发送XonChar字符恢复接收的字节传输后,才会继续接收。
DWORD fOutX :1; //发送方的行为定义,为TRUE时,接收到XoffChar之后便停止发送,接收到XonChar之后将重新开始发送;
DWORD fInX :1;  //接收方的行为定义,为TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去;接收缓冲区空的Buffer达到XonLim之后,XonChar发送出去。
DWORD fErrorChar :1; 
DWORD fNull :1; 
DWORD fRtsControl :2; // RTS Control Flow

//RTS_CONTROL_DISABLE时,RTS置为OFF
//RTS_CONTROL_ENABLE时, RTS置为ON
//RTS_CONTROL_HANDSHAKE时,
//当接收缓冲区小于半满时RTS为ON
//当接收缓冲区超过四分之三满时RTS为OFF
//RTS_CONTROL_TOGGLE时,
//当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF
DWORD fAbortOnError :1; // abort reads/writes on error,为TRUE时,有错误发生时中止读和写操作
DWORD fDummy2 :17; 
WORD wReserved; 
WORD XonLim; //指定在XON字符发送之前接收缓冲区中空缓冲区可允许的最小字节数
WORD XoffLim; //指定在XOFF字符发送这前接收缓冲区中数据缓冲可允许的最小字节数
BYTE ByteSize; 
BYTE Parity; //奇偶校验方式
BYTE StopBits; //停止位
char XonChar;  //请求发送方继续发送时的字符 0x11

char XoffChar; //请求发送方停止发送时的字符 0x13
char ErrorChar; 

char EofChar; 
char EvtChar; 
WORD wReserved1;

} DCB, *LPDCB;



你可能感兴趣的:(mfc,VC,串口,通讯,DCB)