如何在VC下检测当前存在的串口及串口热拔插

当我们在用VS进行串口编程时,在打开串口前,经常想知道当前PC上存在多少个串口,哪些串口可用?哪些串口已经打开了,最好是在一个Combo Box中列表系统当前所有可用的串口以供选择,然而如何获取系统当前可用的串口有哪些呢?

 

这里介绍的方法也是最简单也是最笨的一种方法,即一个一个去试,试完了就知道了。至于还有没有其它更好的方法,暂不在本文所讨论的范围之内。

一个在对话框的OnInitDialog函数内做如下操作:

1 初始化时检测串口

BOOL Ctbox_debug_viewDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	AddCom();//向组合框中添加串口设备	
	m_CombolPort.SetCurSel(0);
         //...
}

 

AddCom函数如下定义:

void Ctbox_debug_viewDlg::AddCom(void)
{
	EnumerateSerialPorts(ports,portse,portsu);
	unsigned short uicounter;
	unsigned short uisetcom;
	CString str;

	//获取可用串口个数
	uicounter = portse.GetSize(); 
	//如果个数大于0
	if(uicounter > 0)
	{
		//初始化串口列表框
		for(int i=0; i<uicounter; i++)
		{
			uisetcom = portse.ElementAt(i);
			str.Format(_T("COM%d "),uisetcom);
			m_CombolPort.AddString(str);
		}
	}
}


EnumerateSerialPorts函数如下:

void Ctbox_debug_viewDlg::EnumerateSerialPorts(CUIntArray& ports, CUIntArray& portse, CUIntArray& portsu)
{
	//清除串口数组内容
	ports.RemoveAll();
	portse.RemoveAll();
	portsu.RemoveAll();
	//因为至多有255个串口,所以依次检查各串口是否存在
	//如果能打开某一串口,或打开串口不成功,但返回的是 ERROR_ACCESS_DENIED错误信息,
	//都认为串口存在,只不过后者表明串口已经被占用
	//否则串口不存在
	for (int i=1; i<256; i++)
	{
		//Form the Raw device name
		CString sPort;
		sPort.Format(_T("\\\\.\\COM%d"), i);

		//Try to open the port
		BOOL bSuccess = FALSE;
		HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
		if (hPort == INVALID_HANDLE_VALUE)
		{
			DWORD dwError = GetLastError();

            if (dwError == ERROR_ACCESS_DENIED)
			{
				bSuccess = TRUE;
				portsu.Add(i);       //已占用的串口
			}
		}
		else
		{
			//The port was opened successfully
			bSuccess = TRUE;
			portse.Add(i);      可用的串口
			//Don't forget to close the port, since we are going to do nothing with it anyway
			CloseHandle(hPort);
		}

		//Add the port number to the array which will be returned
		if (bSuccess)
			ports.Add(i);   //所有存在的串口
	 }
}


如上操作即可将系统当前可用的所有串口添加到CombolPort所对应的Combol Box中了。

 

2 打开串口

代码如下:

void Ctbox_debug_viewDlg::OnBnClickedBtOpen()
{
	// TODO: Add your control notification handler code here

	if(m_mscomm.get_PortOpen())
	{
		m_mscomm.put_PortOpen(FALSE);
		GetDlgItem(IDC_BT_OPEN)->SetWindowText(_T("打开"));
		ShowInfo(_T("关闭串口成功!"));
		m_OpenStatus =false;
		return;
	}
	UpdateData(TRUE);
	m_OpenStatus =true;
	//当前端口号
	int curPort =portse.ElementAt(m_CombolPort.GetCurSel());
	m_mscomm.put_CommPort(curPort);//端口号
	m_mscomm.put_InBufferSize(1024);//接收缓冲区 
	m_mscomm.put_OutBufferSize(1024);//发送缓冲区 

	m_mscomm.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取   
	m_mscomm.put_InputMode(1);//以二进制方式读写数据   
	m_mscomm.put_RThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnComm事件   
	 
	 

	//波特率
	DWORD baudrate;
	switch(m_CombolBaudrate.GetCurSel())
	{
	case 0:
		baudrate =115200;
		break;
	case 1:
		baudrate =9600;
		break;
	default:
		ASSERT(FALSE);
		break;
	}
	CString setting;
	setting.Format(_T("%d,n,8,1"),baudrate);
	m_mscomm.put_Settings(setting/*_T("115200,n,8,1")*/);//波特率,无校验,8个数据位,1个停止1位
	 
	m_mscomm.put_PortOpen(TRUE);//打开串口  
	GetDlgItem(IDC_BT_OPEN)->SetWindowText(_T("关闭"));
	ShowInfo(_T("打开串口成功!"));
 
}


3 串口热拔插时检测串口

这里主要利用Cwnd的ON_WM_DEVICECHANGE消息来处理。

ON_WM_DEVICECHANGE消息在VS2010中好像只能通过手动来添加,反正我没找到可以通过对话的形式来添加的,后续有知道的兄台麻烦告知我一下。

因此,首先得手动添加ON_WM_DEVICECHANGE消息:

第一步:在消息映射BEGIN_MESSAGE_MAP(Ctbox_debug_viewDlg, CDialogEx)中添加:

ON_WM_DEVICECHANGE()


第二步:在头文件类的定义中添加函数声明:

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);


第三步:实现函数

BOOL Ctbox_debug_viewDlg::OnDeviceChange(UINT nEventType,DWORD dwData)
{
        //DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*) dwData;
	switch (nEventType)
	{
	case DBT_DEVICEREMOVECOMPLETE://移除设备
	case DBT_DEVICEARRIVAL://添加设备
		RefreshCom();//刷新组合框的内容
		break;

	default:
		break;
	}

	return TRUE;

}


RefreshCom函数如下:

void Ctbox_debug_viewDlg::RefreshCom(void)
{
	int count =m_CombolPort.GetCount();

	for(int i=0;i<count;i++)
	{
		m_CombolPort.DeleteString(count -1 -i);
	}
	AddCom();
	m_CombolPort.SetCurSel(0);
}

第四步:在.cpp文件中包含头文件

#include <Dbt.h>


这是因为DEV_BROADCAST_DEVICEINTERFACE,DBT_DEVICEREMOVECOMPLETE,DBT_DEVICEARRIVAL这几个东东在头文件Dbt.h中定义的,这样才能编译通过。

 


而AddCom函数已经在之前讲到过,回过头去看看。

这里的思想是通过检测到设备的插入和拔出时刷新组合框的内容,是通过重新测试串口的方式来实现的。

另:需要注意的是,当插入和拔出设备时,系统会检测到两次"设备改变事件",因此会两次调用OnDeviceChange函数,第一次调用时传入的函数nEventType的值固定为0x07,第二次调用时nEventType才是上述代码switch中所定义的事件,这与网上所说的需要注册是相悖的,不知道为什么网上有些文章会说一定需要注册什么的才能检测到设备改变的事件,反正我这里不需要,我用的是VS2010.

 

完!

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
您好!对于在VC下实现稳定的串口通讯并且在线程中进行串口读取的代码,您可以参考以下示例: 首先,您需要包含以下头文件: ```cpp #include <Windows.h> #include <stdio.h> ``` 接下来,定义一个线程函数来读取串口数据: ```cpp DWORD WINAPI SerialReadThread(LPVOID lpParam) { HANDLE hSerial = (HANDLE)lpParam; DWORD bytesRead; char buffer[1024]; while (1) { // 读取串口数据 if (!ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) { // 处理读取错误 // ... } // 处理接收到的数据 // ... } return 0; } ``` 然后,在主函数中进行串口的初始化和线程的创建: ```cpp int main() { HANDLE hSerial; DCB dcbSerialParams = { 0 }; COMMTIMEOUTS timeouts = { 0 }; DWORD threadId; // 打开串口 hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { // 处理串口打开错误 // ... return 1; } // 配置串口参数 dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { // 处理获取串口参数错误 // ... CloseHandle(hSerial); return 1; } dcbSerialParams.BaudRate = CBR_9600; // 设置波特率为9600 dcbSerialParams.ByteSize = 8; // 设置数据位为8 dcbSerialParams.StopBits = ONESTOPBIT;// 设置停止位为1 dcbSerialParams.Parity = NOPARITY; // 设置校验位为无校验 if (!SetCommState(hSerial, &dcbSerialParams)) { // 处理设置串口参数错误 // ... CloseHandle(hSerial); return 1; } // 配置串口读取超时时间 timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutConstant = 0; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; if (!SetCommTimeouts(hSerial, &timeouts)) { // 处理设置超时时间错误 // ... CloseHandle(hSerial); return 1; } // 创建串口读取线程 HANDLE hThread = CreateThread(NULL, 0, SerialReadThread, hSerial, 0, &threadId); if (hThread == NULL) { // 处理线程创建错误 // ... CloseHandle(hSerial); return 1; } // 等待线程结束 WaitForSingleObject(hThread, INFINITE); // 关闭串口和线程句柄 CloseHandle(hSerial); CloseHandle(hThread); return 0; } ``` 以上代码是一个简单的示例,实现了在VC下稳定的串口通讯并在线程中读取串口数据。您可以根据自己的需求进行修改和扩展。希望能对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值