文章目录
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);