前言:

        为了方便串口的调试和串口设备的安装,这里编写了一款具备串口端口搜索、串口数据的收发、定时搜索串口和定时发送数据等功能的串口(调试)工具。

        工具下载:https://download.csdn.net/download/wjy27/88371929icon-default.png?t=N7T8https://download.csdn.net/download/wjy27/88371929

一、串口端口搜索:

MFC实现自动查找串口号_代码日日记的博客-CSDN博客在串口通信中一般设备都是手动输入串口号,为了能够实现串口号的自动输入,这里采用CreateFile函数来实现串口号的自动查找。https://blog.csdn.net/wjy27/article/details/132775812?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132775812%22%2C%22source%22%3A%22wjy27%22%7D        这里使用CreateFile函数创建系统设备文件,如果该串口设备不存在或者被占用,则会返回一个错误,最后与INVALID_HANDLE_VALUE比较,可以判断端口的可使用性。函数代码如下:

BOOL CMcomDlg::iGetCom()
{
	CString comStr;
	int iTmp;
	int jTmp;
	//m_com = 0;
	int kTmp = 0;
	HANDLE m_hCom;
	//m_combobox1.ResetContent();
	jTmp = 1;
	//iTmp = 16;

	for (iTmp = 0; iTmp < 256; iTmp++)
		ComNum[iTmp] = 0;
	m_combobox1.ResetContent();

	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);  
		if (m_hCom != INVALID_HANDLE_VALUE) // 如果没有该设备,或者被其他应用程序在用    
		{
			//查找可用串口
			comStr.Format(_T("COM%d"), jTmp);
			m_combobox1.AddString(comStr); //写入combobox										   
			ComNum[kTmp++] = jTmp;						
		}
		SetCommMask(m_hCom, 0);// 关闭文件句柄,后面我们采用控件,不用API
		CloseHandle(m_hCom);
	}
	if (jTmp)
		return TRUE;
	else
		return FALSE;
}

二、串口数据的发收:

        1、发送数据:

         通过新建发送按钮 IDC_BTN_SEND,在其 OnBnClickedBtnSend() 添加代码,实现按键发送ASCII字符或者16进制字符的功能。

void CMcomDlg::OnBnClickedBtnSend()
{
	BYTE p[1000];
	long ilen = 0;

	GetDlgItem(IDC_EDIT1)->GetWindowText(str);//获取发送数据
	if (str.IsEmpty())
	{
		AfxMessageBox(_T("请输入发送数据!"));
	}
	else
	{
		if (m_radioascii.GetCheck())//选择为Ascii码字符发送
		{
			m_com.put_Output(COleVariant(str));//发送数据
		}
		else//十六进制发送
		{
			COleVariant VArray = HexM2OleVariant(str, p, ilen);//字符转16进制
			m_com.put_Output(VArray);//发送数据
		}
		iFlag = 1;//标志发送
	}
}

ASCII转十六进制字符,由函数HexM2OleVariant()实现。 

//Cstring转16进制字符
COleVariant CMcomDlg::HexM2OleVariant(CString strHexM, BYTE* bt, long& iLen)
{
	CString HexStr;
	short int len = strHexM.GetLength();
	iLen = 0;
	short int intDec = 0;
	char* a = (char*)(LPCTSTR)strHexM;
	for (int n = 0, i = 0; n < len - 1; n++, i++)
	{
		//高4位
		while (a[n] == ' ')//去掉空格
			n++;
		if (a[n] >= 'A' && a[n] <= 'F')
			bt[i] = a[n] - 'A' + 10;
		else if (a[n] >= 'a' && a[n] <= 'f')
			bt[i] = a[n] - 'a' + 10;
		else bt[i] = a[n] - '0';
		n++;
		//低4位
		while (a[n] == ' ')//去掉空格
			n++;
		bt[i] *= 16;
		if (a[n] >= 'A' && a[n] <= 'F')
			bt[i] += a[n] - 'A' + 10;
		else if (a[n] >= 'a' && a[n] <= 'f')
			bt[i] += a[n] - 'a' + 10;
		else bt[i] += a[n] - '0';
		iLen = i + 1;
	}
	CByteArray m_Array;
	m_Array.RemoveAll();
	m_Array.SetSize(iLen);
	for (int i = 0; i < iLen; i++)
		m_Array.SetAt(i, bt[i]);
	return COleVariant(m_Array);
}
        2、接收数据:

        串口数据的接收在中断响应程序里面实现,通过控件IDC_MSCOMM1,添加OnComm中断响应函数,并且在里面添加代码(代码主要实现ACSCII码字符和16进制字符显示,如果ASCII码为无效字符的乱码,则转换成16进制字符显示):

void CMcomDlg::OnOncommMscomm1()
{
    // TODO: 在此处添加消息处理程序代码
    VARIANT variant_inp;
    COleSafeArray safearray_inp;
    long len = 0, k;
    byte rxdata[1024]; //设置byte数组
    CString Hstrtemp;
    ComResFlag = 0;
    m_com.put_RThreshold(0);//不触发中断  
  
    //List开辟行空间
    iiRow = m_list.GetItemCount();//读取行号
	Hstrtemp.Format(_T("%d"), iiRow + 1);
	m_list.InsertItem(iiRow, Hstrtemp);//写入行号

    if (m_com.get_CommEvent() == 2)//值为2表示接收缓冲区内有字符
    {        
        for (k = 0; k < len; k++)
        {
            //m_list.InsertItem(0, "1");        
            safearray_inp.GetElement(&k, rxdata + k);// 转换为BYTE型数组
            StrTemp = *(rxdata + k);
            if (m_radioascii2.GetCheck())//转为ASCII字符
            {
                if (*(rxdata + k) > 32 && *(rxdata + k) < 126)//转为有效ASCII字符
                {
                    Hstrtemp.Format(_T("%c"), *(rxdata + k));
                    m_list.SetItemText(iiRow, k + 2, Hstrtemp);
                }
                else//如果为乱码则转为16进制字符显示
                {
                    Hstrtemp.Format(_T("%02xH"), *(rxdata + k));
                    m_list.SetItemText(iiRow, k + 2, Hstrtemp);
                }
            }
            else//转为十六进制字符
            {
                Hstrtemp.Format(_T("%02xH"), *(rxdata + k));
                m_list.SetItemText(iiRow, k + 2, Hstrtemp);
            }
        }
        ComResFlag = 1;
    }
    m_com.put_RThreshold(1);//触发中断
}

         在界面上添加list control控件,用于显示接收数据:        

        初始化列表:

//初始化List Control
void CMcomDlg::InitList(CListCtrl& m_List, CStringArray& ColumName)
{
	LONG lStyle;
	lStyle = GetWindowLong(m_List.m_hWnd, GWL_STYLE);//获取当前窗口style
	lStyle &= ~LVS_TYPEMASK;//清除显示方式位
	lStyle |= LVS_REPORT;   //设置style这里用的是report模式,在资源控件中也可以选择
	SetWindowLong(m_List.m_hWnd, GWL_STYLE, lStyle);//设置style
	//设置List风格
	DWORD dwStyle = m_List.GetExtendedStyle();
	dwStyle |= LVS_EX_FULLROWSELECT; //选中某行使整行高亮(只适用与report风格的listctrl)
	dwStyle |= LVS_EX_GRIDLINES;     //网格线(只适用与report风格的listctrl)
	dwStyle |= LVS_EX_TWOCLICKACTIVATE;//双击激活,这里可以用于编辑List单元格
	dwStyle |= LVS_EX_DOUBLEBUFFER;//添加双缓冲样式,解决数据写入闪烁问题
	m_List.SetExtendedStyle(dwStyle);//设置扩展风格
	
	//设置单元列宽度
	m_List.InsertColumn(0, ColumName.GetAt(0), LVCFMT_CENTER, 50);
	m_List.InsertColumn(1, ColumName.GetAt(1), LVCFMT_CENTER, 80);

	for (int i = 2; i < ColumName.GetSize(); i++)
	{
		m_List.InsertColumn(i, ColumName.GetAt(i), LVCFMT_CENTER, 40);
	}
}

然后通过InsertItem()和SetItemText()函数配合,将需要显示的数据写入:

        iiRow = m_list.GetItemCount();//读取行号
        Hstrtemp.Format(_T("%d"), iiRow + 1);
        m_list.InsertItem(iiRow, Hstrtemp);//写入行号
        ......
        m_list.SetItemText(iiRow, k + 2, Hstrtemp);//为(iiRow行, k+2列写入数据

三、定时搜索串口和定时发送数据:

        通过OnTimer函数来设定定时发送和定时搜索功能。

void CMcomDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nIDEvent)//nIDEvent 为定时器事件ID,1,2,3  
	{
		case 1:			
			if (ComNum[iNum])//存在串口号,则发送数据
			{
				SetComPort(ComNum[iNum]);//设置串口
				CString str;
				BYTE p[1000];
				long ilen = 0;
                COleVariant VArray;
				GetDlgItem(IDC_EDIT1)->GetWindowText(str);//读取发送数据
				
				if (m_radioascii.GetCheck())//选择ASCII字符发送
				{
					//SendMscommData(str);
					m_com.put_Output(COleVariant(str));//发送数据
				}
				else//选择16进制字符发送
				{
					VArray = HexM2OleVariant(str, p, ilen);//转换发送数据
                    m_com.put_Output(VArray);
				}
				iFlag = 1;
				iNum++;
			}
			else
			{
				iNum = 0;
				KillTimer(1);//关闭定时器
				if (m_com.get_PortOpen()) //释放串口
				{
					m_com.put_PortOpen(FALSE);
				}
				displayFlag = 1;
				ClkBtnFlag = 0;
			}
			break;		
		default:
			break;
	}
	CDialogEx::OnTimer(nIDEvent);
}

四、最后编写出来的样子:

        如果需要可以通过SkinMagic进行美化。可参看:【MFC串口】对话框界面的美化——基于SkinMagic_代码日日记的博客-CSDN博客MFC窗口界面一般都比较粗糙,这里采用SkinMagic来进行美化。SkinMagic 好用的第三方界面库,通过加载皮肤的方式来美化界面。如果对现有皮肤不满意,还可以通过SkinMagicBuilder修改皮肤。https://blog.csdn.net/wjy27/article/details/133275396

 

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐