【MFC串口】MFC实现自动查找串口号

前言

         为了实现串口号的自动连接,本人自写了一个串口通信程序,希望对在写串口的您有所帮助。

一、查找函数 CreateFile。

        在串口通信中一般设备都是手动输入串口号实现串口通信的,为了能够实现串口号的自动输入,这里采用CreateFile函数来实现        

m_hCom = CreateFile(comStr, GENERIC_READ |
                GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
                FILE_FLAG_OVERLAPPED, NULL);  

        这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件,如果该设备不存在或者被占用,则会返回一个错误,然后对比 INVALID_HANDLE_VALUE ,据此可以判断可使用性。详细参见MSDN中的介绍。

m_hCom = CreateFile(comStr, GENERIC_READ |
			GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
			FILE_FLAG_OVERLAPPED, NULL);  // 这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件,如果该设备不存在或者被占用,则会返回一个错误
//下面的 INVALID_HANDLE_VALUE ,据此可以判断可使用性。
if (m_hCom != INVALID_HANDLE_VALUE) // 如果没有该设备,或者被其他应用程序在用
{
	return FALSE;
}
else
{
	SetCommMask(m_hCom, 0);// 关闭文件句柄,后面我们采用控件,不用API
	CloseHandle(m_hCom);
}

            最后,把检测到的数据记录下来就可以实现串口号的查找了。

二、   串口号的范围:

        在Visual Studio的Microsoft Communications Control工具中,使用WIN32 API 打开当COM号大于10的时候,会出现打开错误或失败,一般解决的办法是人工修改USB 串口的com的名称让它的com号是单位数(如: COM1~COM9),但这样较麻烦。而且在设备接入大量串口设备时,没办法解决此问题。
        产生这种奇怪现象的原因是:微软预定义的标准设备中含有“COM1”-“COM9”。所以,“COM1”-“COM9”作为文件名传递给函数时操作系统会自动地将之解析为相应的设备。但对于COM10及以上的串口,“COM10”之类的文件名系统只视之为一般意义上的文件,而非串行设备。

        为了增加对COM10及以上串行端口的支持,微软规定,如果要访问这样的设备,应使用这样的文件名(以COM10为例):\\.\COM10。所以,串口赋值代码如下:        

​if (ComPortNum < 10)
 {
    comStr.Format("COM%d", ComPortNum );
 }
 else
 {
    comStr.Format("\\\\.\\COM%d", ComPortNum );//大于10就要改变输入方式,否则找不到
 }

        然而,原有的MSCOMM32.OCX控件只能输入串口号小于16的串口,当串口号大于16时就会报错。找到的可用的破解方法:
        1、在c:\windows\system32中找到MSCOMM32.OCX;
        2、备份之(为安全起见);
        3、使用HEXEDIT反汇编工具,打开它;
        4、找到 "66 3D 10 00 "(这是文件中唯一一处) 将其修改为 "66 3D FF 00";
        5、保存该文件即可实现支持256个串口。
以下为更改后的mscomm32.ocx资源链接,支持COM1~COM255:icon-default.png?t=N7T8https://download.csdn.net/download/wjy27/88333486

三、串口接收中断:

        在实际的串口项目中串口接收我们一般都是通过串口中断OnOncommMycom()函数来实现的:

ON_EVENT(CBCFZSYSMFCDlg, IDC_MYCOM, 1, CBCFZSYSMFCDlg :: OnOncommMycom ,  VTS_NONE);

        然而在OnOncommMycom()函数中,我们可能要运行一些程序,例如再利用串口发送一些回应数据等,这样就会使得串口中断在前一次中断没有执行完之前被再次触发,这样造成中断程序的崩溃,为了避免这个错误,我们在进入中断时,把put_RThreshold置0,串口中断设置为不触发。在中断执行完成后,再给put_RThreshold赋值。这样一个稳定的串口中断接收程序就写好了。        

void CBCFZSYSMFCDlg::OnOncommMycom()
{
   // TODO: 在此处添加消息处理程序代码
   m_mscomm.put_RThreshold(0);//不触发中断
   ... ...
   ... ...
   ... ...
   m_mscomm.put_RThreshold(1);
   UpdateData(FALSE);//更新编辑框的内容
}        

四、函数的实现:

        首先,要编写一个查找函数 iGetCom() 来实现存在可用串口号的查找:

/*查找串口号,FindComNum=0,从COM 1~16 搜索,非0时,判断其是否存在*/
BOOL CMcomDlg::iGetCom(int FindComNum)
{
	CString comStr;
	int iTmp;
	int jTmp;
	//m_com = 0;
	int kTmp = 0;
	HANDLE m_hCom;

	if (FindComNum == 0) // 如果串口存在,则执行相应的初始化(采用控件)
	{
		jTmp = 1;
		//iTmp = 16;
		comPortStr.Empty();//清除

		for (iTmp = 0; iTmp < 16; iTmp++)
			ComNum[iTmp] = 0;

		for (; jTmp <= iTmp; jTmp++)
		{
			if (jTmp < 10)
			{
				comStr.Format(_T("COM%d"), jTmp);
			}
			else
			{
				comStr.Format(_T("\\\\.\\COM%d"), jTmp);//大于10就要改变输入方式,否则找不到
			}
			m_hCom = CreateFile(comStr, GENERIC_READ |
				GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
				FILE_FLAG_OVERLAPPED, NULL);  // 这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件,如果该设备不存在或者被占用,则会返回一个错误,
												//即下面的 INVALID_HANDLE_VALUE ,据此可以判断可使用性。详细参见MSDN中的介绍。
			if (m_hCom != INVALID_HANDLE_VALUE) // 如果没有该设备,或者被其他应用程序在用    
			{
				/*查找可用串口*/
				comStr.Format(_T("COM%d"), jTmp);
				comPortStr += comStr;    //全局记录字符串,edit用于显示
				comPortStr += "  ";
				m_combobox1.AddString(comStr); //写入combobox
				ComNum[kTmp++] = jTmp;
				if (kTmp % 8 == 0)
					comPortStr += "\r\n    ";
				
			}

			SetCommMask(m_hCom, 0);// 关闭文件句柄,后面我们采用控件,不用API
			CloseHandle(m_hCom);
		}
		if (jTmp)
			return TRUE;
		else
			return FALSE;
	}
	else
	{
		if (FindComNum < 10)
		{
			comStr.Format(_T("COM%d"), FindComNum);
		}
		else
		{
			comStr.Format(_T("\\\\.\\COM%d"), FindComNum);//大于10就要改变输入方式,否则找不到
		}
		m_hCom = CreateFile(comStr, GENERIC_READ |
			GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
			FILE_FLAG_OVERLAPPED, NULL);  // 这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件,如果该设备不存在或者被占用,则会返回一个错误,
											//即下面的 INVALID_HANDLE_VALUE ,据此可以判断可使用性。详细参见MSDN中的介绍。
		if (m_hCom != INVALID_HANDLE_VALUE) // 如果没有该设备,或者被其他应用程序在用
		{
			return FALSE;
		}
		else
		{
			SetCommMask(m_hCom, 0);// 关闭文件句柄,后面我们采用控件,不用API
			CloseHandle(m_hCom);
		}
	}
	return TRUE;
}

        其次,在找到可用串口号后,我们必须逐个测试哪个串口是连接我们设备的,这里就要求上位机与串口通信设备间存在应答的程序,这样在开启串口后,我们就可以通过发送测试数据,来获取回应,如果回应的数据是对的则判断此串口为我们需要连接的设备

流程图如下:

 

 具体代码实现:

void CMcomDlg::OnBnClickedBtnSend()
{
	// TODO: 在此添加控件通知处理程序代码
	iGetCom(0);//查找串口
	iNum = 0;	
	SetTimer(1, 800, NULL);//开启定时器1	,定时逐个判断存在串口的可用性
}

void CMcomDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nIDEvent)//nIDEvent 为定时器事件ID,1,2,3  
	{
	case 1:
		if (ComNum[iNum])
		{
			SetComPort(ComNum[iNum]);
			iNum++;
			CString str;
			GetDlgItem(IDC_EDIT1)->GetWindowText(str);
			if (m_radioascii.GetCheck())
			{
				//SendMscommData(str);
				m_com.put_Output(COleVariant(str));//发送数据
			}
			else
			{
				m_com.put_Output(HexM2OleVariant(str));
			}
		}
		else
		{
			iNum = 0;
			KillTimer(1);//关闭定时器
			if (m_com.get_PortOpen()) //释放串口
			{
				m_com.put_PortOpen(FALSE);
			}
		}
		break;
	}
	CDialogEx::OnTimer(nIDEvent);
}
void CMcomDlg::SetComPort(int ComPort )
{
	if (m_com.get_PortOpen()) // m_ctrlMscom是控件的一个实例
	{
		m_com.put_PortOpen(FALSE);
	}

	CString cstr;
	CString ComSetStr;

	cstr.Format(_T("COM%d"), ComPort);
	m_combobox1.SelectString(0, cstr);
		
	m_baud.GetLBText(m_baud.GetCurSel(), cstr);
	ComSetStr += cstr + ",";
	m_crcbit.GetLBText(m_crcbit.GetCurSel(), cstr);
	ComSetStr += (CString)cstr.GetAt(0) + ",";
	m_databit.GetLBText(m_databit.GetCurSel(), cstr);
	ComSetStr += cstr + ",";
	m_stopbit.GetLBText(m_stopbit.GetCurSel(), cstr);
	ComSetStr += cstr;

	m_com.put_CommPort(ComPort);//写串口值
	m_com.put_InBufferSize(1024);		// 设置输入缓冲区的大小,Bytes
	m_com.put_OutBufferSize(1024);		// 设置输入缓冲区的大小,Bytes//

	m_com.put_Settings(ComSetStr);
	m_com.put_InputMode(1);
	m_com.put_RThreshold(1);//接受缓冲区又1个及1个以上的字符时,引发接受数据的OnComm事件
	m_com.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取
	//m_mscomm.get_Input();
	m_com.put_SThreshold(0);    //每发送一个字符时,不触发OnComm事件

	if (!m_com.get_PortOpen())
	{
		m_com.put_PortOpen(TRUE);

	}
}

        最后,串口的编写,代码并不复杂,但是要想它能很好的与串口设备稳定连接,就需要用耐心去调试,有时候一个很小的细节,就会让串口通信变得不稳定。

文中部分代码链接: https://download.csdn.net/download/wjy27/88317380

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
MFC(Microsoft Foundation Classes)是微软的一个C++类库,它提供了一系列用于开发Windows应用程序的类和函数。在MFC中进行串口通信可以通过使用Windows API函数来实现。以下是一个简单的示例,演示如何在MFC应用程序中进行串口通信的数据收发: 1. 首先,在你的MFC应用程序中包含头文件 "afxwin.h",以便使用MFC类和函数。 2. 在你的对话框类中添加一个按钮和一个编辑框控件,分别用于发送数据和显示接收到的数据。 3. 在对话框类的头文件中声明以下成员变量和函数: ```cpp CSerialPort m_serialPort; // 串口对象 CString m_receivedData; // 接收到的数据 void OnBnClickedSend(); // 发送按钮点击事件处理函数 afx_msg LRESULT OnSerialData(WPARAM wParam, LPARAM lParam); // 串口数据到达消息处理函数 ``` 4. 在对话框类的源文件中实现发送按钮点击事件处理函数和串口数据到达消息处理函数: ```cpp void CYourDialog::OnBnClickedSend() { CString sendData; GetDlgItemText(IDC_EDIT_SEND, sendData); // 获取发送数据 if (m_serialPort.IsOpen()) { m_serialPort.Write(sendData.GetBuffer(), sendData.GetLength()); // 发送数据 } } LRESULT CYourDialog::OnSerialData(WPARAM wParam, LPARAM lParam) { if (m_serialPort.IsOpen()) { char buffer[1024]; int bytesRead = m_serialPort.Read(buffer, sizeof(buffer)); // 读取数据 if (bytesRead > 0) { buffer[bytesRead] = '\0'; m_receivedData += CString(buffer); // 追加到接收数据字符串 SetDlgItemText(IDC_EDIT_RECEIVED, m_receivedData); // 显示接收数据 } } return 0; } ``` 5. 在对话框类的OnInitDialog()函数中初始化串口对象并打开串口: ```cpp BOOL CYourDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 初始化串口对象 m_serialPort.SetPort("COM1"); m_serialPort.SetBaudRate(9600); m_serialPort.SetParity(NOPARITY); m_serialPort.SetStopBits(ONESTOPBIT); m_serialPort.SetDataBits(8); // 打开串口 if (!m_serialPort.Open()) { AfxMessageBox("Failed to open serial port!"); } // 注册串口数据到达消息 m_serialPort.SetWindow(this); m_serialPort.SetCallbackMsg(WM_USER_SERIAL_DATA); return TRUE; } ``` 以上是一个简单的MFC串口通信实现示例,你可以根据自己的需求进行修改和扩展。注意,在使用串口通信时需要根据实际情况配置正确的串口参数,如端口、波特率、校验位等。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

༄༣ི为照࿂ྀ࿐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值