【53】MFC入门到精通——MFC串口助手(二)---通信版(发送数据 、发送文件、数据转换、清空发送区、打开/关闭文件),附源码

发布于:2025-07-21 ⋅ 阅读:(11) ⋅ 点赞:(0)

1 完整 功能展示

串口通信助手 页面展示,功能齐全,
还增加了串口打开/关闭状态变色,发送按钮状态变色等功能。
发送/接收时,相应按钮,功能禁用/可用等保护措施。

在这里插入图片描述

2 添加控件变量及声明

串口设置等基本功能实现 教程MFC串口助手(一)—初级版(初始化、串口设置、修改参数、打开/关闭、状态显示)

本节将接着上一节教程继续实现 发送数据 、发送文件

2.1 添加控件及变量

  • 添加控件
发送数据的编辑框、按钮;
打开文件、发送文件 按钮;
显示文件路径静态文本框;
 清空发送区 按钮;

在这里插入图片描述
修改ID如下

发送数据
IDC_EDIT_TXDATA
IDC_BUTTON_SEND
//发送文件
IDC_BUTTON_OPEN_FILE
IDC_BUTTON_SEND_FILE
IDC_STATIC_FILE_PATH
//清空发送区
IDC_BUTTON_CLEAN_SEND
  • 添加控件变量
    (注意下下面 m_sendbutten 控件,类型添加为CButton类型,就可以了。我这里是自己写的一个可变色CCustomButton类型)

方法:选中控件,右键选择添加变量

//CButton m_sendbutten;
CCustomButton m_sendbutten;
CButton m_transfile;
CButton m_emptySendArea;
CStatic m_sfilePath;

2.2 SerialPortDlg.h: 头文件

头文件的其他 变量机函数声明
(下面是 串口助手 全部功能的头文件内容)


// SerialPortDlg.h: 头文件
//

#pragma once

#include "CCustomButton.h"

//UINT ComProce(LPVOID pParam);


// CSerialPortDlg 对话框
class CSerialPortDlg : public CDialogEx
{
// 构造
public:
	CSerialPortDlg(CWnd* pParent = nullptr);	// 标准构造函数
	

//变量=====================================================================================
public:
	//自定义变量
	HANDLE			m_hCom;					//串口句柄
	volatile int	m_bConnected;		//串口连接成功指示
	BOOL			m_COMStatu;				//串口状态指示
	long			m_rxlen;				//接收数据个数
	long			m_txlen;				//发送数据个数

	//列表框变量
	CComboBox	m_Combo_Com;				//列表框:串口
	CComboBox	m_Combo_Baud;				//列表框:波特率
	CComboBox	m_Combo_Check;				//列表框:校验位
	CComboBox	m_Combo_Data;				//列表框:数据位
	CComboBox	m_Combo_Stop;				//列表框:停止位

	//字符变量
	CString		m_Str_Com;					//字符变量:串口
	CString		m_Str_Baud;					//字符变量:波特率
	CString		m_Str_Check;				//字符变量:校验位
	CString		m_Str_Data;					//字符变量:数据位
	CString		m_Str_Stop;					//字符变量:停止位

//函数=====================================================================================
public:
	//串口相关函数
	BOOL InitComm();								    //打开串口(不创建线程)
	BOOL OpenComm(int Num);								//打开串口
	BOOL SetCommParameter();							//设置串口参数

	void DisplayStatus();								//显示串口状态
	void CloseConnection();								//关闭串口

	//按钮函数
	afx_msg void OnBnClickedOpencomButton();			//串口状态显示

	//列表框函数
	afx_msg void OnCbnSelchangeComboCom();				//更改串口
	afx_msg void OnCbnSelchangeComboBaud();				//更改波特率
	afx_msg void OnCbnSelchangeComboCheck();			//更改校验位
	afx_msg void OnCbnSelchangeComboData();				//更改数据位
	afx_msg void OnCbnSelchangeComboStop();				//更改停止位


// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_SERIALPORT_DIALOG };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
	void OnOK();
	afx_msg LRESULT OnRecvMsg(WPARAM dwEvent, LPARAM dwLen);
// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()


//函数=====================================================================================
public:
	//读缓冲区
	int ReadBlock(BYTE* abIn, int MaxLength);
	//串口消息处理
	BOOL ProcessCOMMNotification(UINT wParam, long lParam);	 //串口消息处理
	//打开串口,创建工作线程(失败)
	BOOL OpenComm2(int Num);								//打开串口
	//打开串口,创建工作线程(监听线程,当有数据到达串口时响应)
	BOOL OpenComm3(int Num);								//打开串口
	//写串口
	int WriteBlock(char* abOut, int MaxLength);			//写串口
	//字符串转十六进制
	int String2Hex(CString str, char* SendOut);
	//转十六进制
	char ConvertHexData(char ch);
	//显示当前时间
	int Current_Time();

	//发送数据
	afx_msg void OnBnClickedButtonSend();
	afx_msg void OnTimer(UINT_PTR nIDEvent);

//变量=====================================================================================
public:
	//根据代码中报错未定义的变量,自己补充的,有些类型可能错误
	CString m_savefilename;// = _T("D:\\Projects\\MFC\\SerialPort-2\\test.txt");


	//十六进制显示和保存到文件控件,添加控件变量
	CButton m_writetofile;
	CButton m_ctrlHexDisplay;
	//给IDC_EDIT_RXDATA编辑框,添加控件变量和值变量
	CString m_strRXData;
	CEdit m_EditRXData;
	CButton m_cHexSend;
	OVERLAPPED m_osWrite;
	OVERLAPPED m_osRead;

	//发送按钮,添加控件变量
	// 发送数据、文件
	//CButton m_sendbutten;
	CCustomButton m_sendbutten;
	CButton m_transfile;
	CButton m_emptySendArea;
	CStatic m_sfilePath;
	CButton m_emptyReceiveArea;
	CMFCButton m_button_ComOpenStatus;

	afx_msg void OnBnClickedButtonCleanSend();
	afx_msg void OnBnClickedButtonCleanReceive();
	//发送文件
	afx_msg void OnBnClickedButtonOpenFile();
	afx_msg void OnBnClickedButtonSendFile();
	afx_msg void OnBnClickedButtonClearCount();
	afx_msg void OnBnClickedCheckSvaeFile();
	afx_msg void OnBnClickedButtonSavePath();
};

3 函数实现

#define MAXBLOCK 1024

3.1 数据发送

//数据发送函数
void CSerialPortDlg::OnBnClickedButtonSend()
{
	// TODO: 在此添加控件通知处理程序代码
	char abOut[MAXBLOCK];
	int OutNum, length;
	if (!m_bConnected)
	{
		AfxMessageBox(_T("串口未打开!"));
		return;
	}

	
	//发送触发后,发送、清空按钮 禁用,等待发送完毕后恢复可用
	m_sendbutten.EnableWindow(FALSE);
	m_transfile.EnableWindow(FALSE);
	m_emptyReceiveArea.EnableWindow(FALSE);
	m_emptySendArea.EnableWindow(FALSE);

	memset(abOut, 0, MAXBLOCK);
	//判断是否文件写,若是则打开文件并写入缓冲区
	
	//读文本框内容
	CString str;
	CString strtest;
	GetDlgItem(IDC_EDIT_TXDATA)->GetWindowText(str);
	char SendOut[MAXBLOCK];
	int len = str.GetLength();
	for (int i = 0; i < len; i++)
	{
		abOut[i] = str.GetAt(i);
		//strtest.Format(_T("%c"),abOut[i]);
		//AfxMessageBox(strtest);
	}

	if (m_cHexSend.GetCheck())
	{
		CString StrHexData;
		abOut[len] = NULL;
		StrHexData = CString(abOut);
		len = String2Hex(StrHexData, SendOut);
		length = WriteBlock(SendOut, len);
	}
	else
		length = WriteBlock(abOut, len);
	m_txlen += length;
		if(!length)
			AfxMessageBox(_T("无数据能写入缓冲区!"));

	//发送完毕,发送、清空按钮 恢复可用
	m_sendbutten.EnableWindow(TRUE);
	m_transfile.EnableWindow(TRUE);
	m_emptyReceiveArea.EnableWindow(TRUE);
	m_emptySendArea.EnableWindow(TRUE);

	DisplayStatus();
	return;
}

3.1.2 写数据、字符串转

数据发送中涉及两个函数 :WriteBlock、WriteBlock

//写串口
int CSerialPortDlg::WriteBlock(char* abOut, int MaxLength)
{
	BOOL JudgeWrite;//写入串行端口数据操作的返回值

	COMSTAT ComStat;//通信状态缓冲区的指针

	DWORD dwErrorFlags, dwLength, lentest;
	//接收错误代码变量的指针,要写的字节数,被写入的字节数的变量地址

	m_osWrite.Offset = 0;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);//清除串行端口错误或读取串行端口现在的状态==>
	//串口句柄,接收错误代码变量的指针,通信状态缓冲区的指针


	if (dwErrorFlags > 0) //如果接收到错误代码
	{
		AfxMessageBox(_T("写串口错!请检查参数设置。"));
		PurgeComm(m_hCom, PURGE_TXABORT | PURGE_TXCLEAR); //清空缓冲区==>
		return 0;
	}


	dwLength = MaxLength;//要写的字节数

	lentest = 0;//实际字节数的指针置0
	JudgeWrite = WriteFile(m_hCom, abOut, dwLength, &lentest, &m_osWrite); //写入串行端口数据==>
	//句柄,预发送的数据,写入的字节数,被写入的字节数的变量地址,OVERLAPPED结构体指针(不使用异步传输设为null)

	if (!JudgeWrite)//写失败
	{
		if (GetLastError() == ERROR_IO_PENDING) //重叠 I/O 操作在进行中。
		{
			GetOverlappedResult(m_hCom, &m_osWrite, &lentest, TRUE);//返回重叠操作结果==>
			//句柄;重叠结构的指针;实际字节数的指针;TRUE,那么只有当操作完成才会返回
		}
		else
			lentest = 0;//实际字节数的指针置0
	}
	return lentest;//返回字节数的指针
}
//字符串转十六进制
int CSerialPortDlg::String2Hex(CString str, char* SendOut)
{
	int hexdata, lowhexdata;
	int hexdatalen = 0;
	int len = str.GetLength();
	//SendOut.SetSize(len/2);
	for (int i = 0; i < len;)
	{
		char lstr, hstr = str[i];
		if (hstr == ' ' || hstr == '\r' || hstr == '\n')
		{
			i++;
			continue;
		}
		i++;
		if (i >= len)
			break;
		lstr = str[i];
		hexdata = ConvertHexData(hstr);
		lowhexdata = ConvertHexData(lstr);
		if ((hexdata == 16) || (lowhexdata == 16))
			break;
		else
			hexdata = hexdata * 16 + lowhexdata;
		i++;
		SendOut[hexdatalen] = (char)hexdata;
		hexdatalen++;
	}
	//senddata.SetSize(hexdatalen);
	return hexdatalen;
}

3.2 发送文件

3.2.1 打开文件

//打开文件
void CSerialPortDlg::OnBnClickedButtonOpenFile()
{
	// TODO: 在此添加控件通知处理程序代码

	// 设置过滤器
	TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");

	// 构造打开文件对话框 (TRUE创建打开文件对话框)
	CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);
	CString strFilePath;

	// 显示打开文件对话框
	if (IDOK == fileDlg.DoModal())
	{
		// 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里
		strFilePath = fileDlg.GetPathName();
		SetDlgItemText(IDC_STATIC_FILE_PATH, strFilePath);
	}
}

3.2.2 发送文件

//发送文件
void CSerialPortDlg::OnBnClickedButtonSendFile()
{
	// TODO: 在此添加控件通知处理程序代码

	CString filepath = _T("");       //文件路径
	char abOut[MAXBLOCK];
	int OutNum, length;
	if (!m_bConnected)
	{
		AfxMessageBox(_T("串口未打开!"));
		return;
	}

	else {
		// 如果串口连接成功,串口打开按钮 显示绿色,关闭串口

		m_button_ComOpenStatus.m_bTransparent = FALSE;
		m_button_ComOpenStatus.m_bDontUseWinXPTheme = TRUE;
		//消除黑圈
		//m_button_ComOpenStatus.m_bDrawFocus = FALSE;
		//消除边框
		m_button_ComOpenStatus.m_nFlatStyle = CMFCButton::BUTTONSTYLE_NOBORDERS;
		//设置颜色
		m_button_ComOpenStatus.SetFaceColor(RGB(0, 255, 0), true);
		//串口打开状态下,显示绿色,关闭串口
		m_button_ComOpenStatus.SetWindowTextW(_T("关闭串口"));

	}

	//发送触发后,发送、清空按钮 禁用,等待发送完毕后恢复可用
	m_sendbutten.EnableWindow(FALSE);
	m_transfile.EnableWindow(FALSE);
	m_emptyReceiveArea.EnableWindow(FALSE);
	m_emptySendArea.EnableWindow(FALSE);
	m_sfilePath.GetWindowText(filepath);

	memset(abOut, 0, MAXBLOCK);
	//判断是否文件写,若是则打开文件并写入缓冲区
	if (!filepath.IsEmpty())
	{
		CFile fsendout;
		CFileException e;
		fsendout.Open(filepath, CFile::modeRead, &e);
		OutNum = fsendout.Read(abOut, MAXBLOCK);
		while (OutNum)//执行循环后,文件已读完。
		{
			//如果选择了十六进制发送,则转换为字符形式发送出去,否则直接发送字符数组
			if (m_cHexSend.GetCheck())//十六进制发送复选框选中时
			{
				//如果读到的字符中最后有个单独的数据,则将读取长度退回一
				if (abOut[OutNum - 1] == ' ' || abOut[OutNum - 1] == '\r\n')
					OutNum--;
				if (!OutNum)
				{
					AfxMessageBox(_T("读十六进制文件出错,请检查格式!"));
					return;
				}
				//将十六进制字符串转换为CString类,为十六进制转换成字符的函数作准备
				CString StrHexData;
				abOut[OutNum] = NULL;
				StrHexData = CString(abOut);
				char SendOut[MAXBLOCK];
				int len = String2Hex(StrHexData, SendOut);
				length = WriteBlock(SendOut, len);
			}
			else
				length = WriteBlock(abOut, OutNum);
			m_txlen += OutNum;
			OutNum = fsendout.Read(abOut, MAXBLOCK);//准备下一次循环
			//continue;
		}//while(OutNum)

		fsendout.Close();
		if (!length)
		{
			AfxMessageBox(_T("已写完!"));
			m_sendbutten.EnableWindow(TRUE);

			//发送完毕,发送、清空按钮 恢复可用
			m_sendbutten.EnableWindow(TRUE);
			m_transfile.EnableWindow(TRUE);
			m_emptyReceiveArea.EnableWindow(TRUE);
			m_emptySendArea.EnableWindow(TRUE);

			DisplayStatus();
			return;
		}
	}

	//发送完毕,发送、清空按钮 恢复可用
	m_sendbutten.EnableWindow(TRUE);
	m_transfile.EnableWindow(TRUE);
	m_emptyReceiveArea.EnableWindow(TRUE);
	m_emptySendArea.EnableWindow(TRUE);

	DisplayStatus();
	return;
}

3.3 清空发送区

最简单的方法直接让,你编辑框里的内容置为空;

void CSerialPortDlg::OnBnClickedButtonCleanSend()
{
	// TODO: 在此添加控件通知处理程序代码
	//GetDlgItem(IDC_EDIT_TXDATA)->SetWindowText(_T("")); //获取端口对话框的句柄并清除
	SetDlgItemText(IDC_EDIT_TXDATA,_T("")); //获取端口对话框的句柄并清除
}

或者给编辑框添加一个控件变量,

	m_strTXData.Empty();
	m_EditTXData.SetWindowText(m_strRXData);

4 完整MFC项目项下载


网站公告

今日签到

点亮在社区的每一天
去签到