【MFC 实战项目】No.1 串口调试助手
程序员文章站
2022-05-23 12:26:19
...
前 言
基于MFC对话框开发了一款串口调试助手,基础功能已经实现了,还有一些扩展功能没实现。文中代码注释已经很丰富,这里不做过多讲解了。
VS版本:
Microsoft Visual Studio 2008 9.0.30729.1 SP
目 录
1. 软件成果展示
这里展示了与著名的铭心软体工作室出品的串口调试助手(V 3.7.2)进行通信,串口1和2是VSPD软件添加的虚拟串口。项目下载地址附文末,这里附上上述两个软件的下载地址:
串口调试助手(CM精装版 V3.7.2) VSPD 6.9下载
图1 串口调试助手 V1.0成果展示
2. 源代码:
(1)头文件:SerialPortDlg.h
// SerialPortDlg.h : 头文件
#ifndef SERIALPORTDLG_H
#define SERIALPORTDLG_H
#pragma once
#include "afxwin.h"
#include "BasCommPort.h"
// CSerialPortDlg 对话框
class CSerialPortDlg : public CDialog
{
// 构造
public:
CSerialPortDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_SERIALPORT_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
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:
/* 初始化菜单、控件 */
void InitMenu(); // 初始化菜单栏
void InitSerialSetting(); // 初始化串口设置
void InitReciveSetting(); // 初始化接收区设置
void InitSendSetting(); // 初始化发送区设置
/* 其它辅助函数 */
void FindCommPort(CComboBox *pComboBox); // 查找计算机可用串口并将之设置到Combo Box控件上
void SetButtonState(BOOL bOpen, UINT ID_ICON); // 设置<打开/断开>按钮的文本
void SetSerialStateStatus(BOOL bOpen = TRUE); // 设置状态栏串口状态
void SetSendNumStatus(); // 设置状态栏发送字节数
void SetRecvNumStatus(); // 设置状态栏接收字节数
long GetCommPortN(); // 获取Combo Box上的串口编号
CString GetCommPortParameter(); // 获取串口参数
void SetRecvData(CString strData); // 设置接收区内容
/* 文件IO */
CString GetFilePathFromFileDlg(BOOL bOpenFileDlg, LPCTSTR lpszFileName); // 从文件对话框创建文件,返回文件路径+文件名
void SaveRecvData(const char *szFilePath, const char *szData); // 保存接收区文件
CString ReadFileData(const char *szFilePath); // 读取文件信息,返回读到的数据
/* 基类成员函数重写 */
afx_msg void OnClose(); // 串口关闭之前的处理
public:
// 串口设置
CComboBox m_cb_serNum; // 串口号
CComboBox m_cb_btRate; // 波波特率
CComboBox m_cb_ckBit; // 校验位
CComboBox m_cb_datBit; // 数据位
CComboBox m_cb_stpBit; // 停止位
CButton m_bt_onOff; // 打开/关闭按钮
CStatic m_picCtrl_OnOffFlag; // 图片控件,标识串口状态
afx_msg void OnBnClickedBtOnoff(); // 打开/断开串口
// 发送区
CButton m_ck_send1; // 启用文件数据源
CButton m_ck_send2; // 自动发送附加位
CButton m_ck_send3; // 发送完自动清空
CButton m_ck_send4; // 按十六进制发送
CButton m_ck_send5; // 数据流循环发送
CEdit m_et_time; // 发送间隔
CEdit m_et_dataSend; // 数据输入框
afx_msg void OnBnClickedCkSend1(); // 启用文件数据源
afx_msg void OnBnClickedBtLoadfile(); // 文件载入
afx_msg void OnBnClickedBtClsend(); // 清除输入
afx_msg void OnBnClickedBtSenddata(); // 发送数据
// 接收区
CButton m_ck_recv1; // 接收转向至文件
CButton m_ck_recv2; // 自动换行显示
CButton m_ck_recv3; // 十六进制显示
CButton m_ck_recv4; // 暂停接收显示
CEdit m_et_dataRecv; // 串口数据接收框
afx_msg void OnRecvAutoToFile(); // 接收转向至文件
afx_msg void OnBnClickedBtSavedata(); // 保存数据
afx_msg void OnBnClickedBtClrecv(); // 清除显示
// 菜单栏
afx_msg void OnMenuOnoff(); // 打开/断开
afx_msg void OnMenuAbout(); // 关于
afx_msg void OnMenuExit(); // 退出
// 模拟状态栏 Static Edit
CStatic m_st_serState; // 串口状态
CStatic m_st_sendNum; // 发送字节数
CStatic m_st_recvNum; // 接收字节数
afx_msg void OnBnClickedBtClearnum(); // 复位按钮
public:
CMenu m_menu; // 菜单栏
CBasCommPort m_CommPort; // 对MSCOmm控件的封装
ULONG m_nSendNum; // 发送字节数
ULONG m_nRecvNum; // 接收字节数
BOOL m_bIsOpen; // 打开/关闭标识符
CString m_strAutoSaveFilePath; // 自动保存至文件时,传递的文件路径
CString m_strFileDatSrcFilePath; // 启用文件数据源时,传递的文件路径
}; //class CSerialPortDlg
#endif //SERIALPORTDLG_H
(2)源文件:SerialPortDlg.cpp
// SerialPortDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "SerialPort.h"
#include "SerialPortDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 回调函数
void SetRecv(RecivedCallback pCallBack, void *pUserData);
//void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nTimerId, DWORD dwTime);
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CSerialPortDlg 对话框
CSerialPortDlg::CSerialPortDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSerialPortDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CSerialPortDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_CB_SERNUM, m_cb_serNum);
DDX_Control(pDX, IDC_CB_BOTRAT, m_cb_btRate);
DDX_Control(pDX, IDC_CB_CKBT, m_cb_ckBit);
DDX_Control(pDX, IDC_CB_DATBT, m_cb_datBit);
DDX_Control(pDX, IDC_CB_STPBT, m_cb_stpBit);
DDX_Control(pDX, IDC_BT_ONOFF, m_bt_onOff);
DDX_Control(pDX, IDC_ET_TIME, m_et_time);
DDX_Control(pDX, IDC_CK_RECV2, m_ck_recv2);
DDX_Control(pDX, IDC_CK_RECV1, m_ck_recv1);
DDX_Control(pDX, IDC_CK_RECV3, m_ck_recv3);
DDX_Control(pDX, IDC_CK_RECV4, m_ck_recv4);
DDX_Control(pDX, IDC_CK_SEND1, m_ck_send1);
DDX_Control(pDX, IDC_CK_SEND2, m_ck_send2);
DDX_Control(pDX, IDC_CK_SEND3, m_ck_send3);
DDX_Control(pDX, IDC_CK_SEND4, m_ck_send4);
DDX_Control(pDX, IDC_CK_SEND5, m_ck_send5);
DDX_Control(pDX, IDC_ET_DATARECV, m_et_dataRecv);
DDX_Control(pDX, IDC_ET_DATASEND, m_et_dataSend);
DDX_Control(pDX, IDC_STATIC_PIC, m_picCtrl_OnOffFlag);
DDX_Control(pDX, IDC_ST_SERSTATE, m_st_serState);
DDX_Control(pDX, IDC_ST_SENDNUM, m_st_sendNum);
DDX_Control(pDX, IDC_ST_RECVNUM, m_st_recvNum);
}
BEGIN_MESSAGE_MAP(CSerialPortDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BT_SENDDATA, &CSerialPortDlg::OnBnClickedBtSenddata)
ON_BN_CLICKED(IDC_BT_ONOFF, &CSerialPortDlg::OnBnClickedBtOnoff)
ON_COMMAND(ID_MENU_EXIT, &CSerialPortDlg::OnMenuExit)
ON_BN_CLICKED(IDC_BT_CLEARNUM, &CSerialPortDlg::OnBnClickedBtClearnum)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_BT_SAVEDATA, &CSerialPortDlg::OnBnClickedBtSavedata)
ON_BN_CLICKED(IDC_BT_CLRECV, &CSerialPortDlg::OnBnClickedBtClrecv)
ON_BN_CLICKED(IDC_BT_LOADFILE, &CSerialPortDlg::OnBnClickedBtLoadfile)
ON_BN_CLICKED(IDC_BT_CLSEND, &CSerialPortDlg::OnBnClickedBtClsend)
ON_COMMAND(ID_ABOUT, &CSerialPortDlg::OnMenuAbout)
ON_BN_CLICKED(IDC_CK_RECV1, &CSerialPortDlg::OnRecvAutoToFile)
ON_COMMAND(ID_MENU_ONOFF, &CSerialPortDlg::OnMenuOnoff)
ON_BN_CLICKED(IDC_CK_SEND1, &CSerialPortDlg::OnBnClickedCkSend1)
END_MESSAGE_MAP()
// CSerialPortDlg 消息处理程序
BOOL CSerialPortDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
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);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
/************************************************************************/
/* 添加的初始化变量/函数 */
/************************************************************************/
m_nSendNum = 0;
m_nRecvNum = 0;
m_bIsOpen = FALSE;
this->InitMenu();
this->InitSerialSetting();
this->InitReciveSetting();
this->InitSendSetting();
m_CommPort.SetRecCallBack(RecivedCallback(SetRecv), this);
/************************************************************************/
/* end */
/************************************************************************/
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CSerialPortDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CSerialPortDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSerialPortDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
/************************************************************************/
/* 回调函数 */
/************************************************************************/
// 回调函数
void SetRecv(RecivedCallback pCallBack, void *pUserData)
{
CSerialPortDlg *pDlg = NULL;
pDlg = (CSerialPortDlg*)pUserData;
// 将串口缓冲区数据读入buff
char buff[1024] = {0};
pDlg->m_CommPort.Read(buff, sizeof(buff));
pDlg->m_CommPort.ClearInputBuffer(); // 清除接收缓冲区
// 设置接收区内容
CString strDataRecv;
strDataRecv.Format("%s", buff);
pDlg->SetRecvData(strDataRecv);
// 设置接收字节数
pDlg->m_nRecvNum += strDataRecv.GetLength();
pDlg->SetRecvNumStatus();
}
/************************************************************************/
/* 初始化菜单、控件 */
/************************************************************************/
// 菜单栏设置
void CSerialPortDlg::InitMenu()
{
m_menu.LoadMenu( IDR_MENU );
this->SetMenu(&m_menu);
}
// 串口设置
void CSerialPortDlg::InitSerialSetting()
{
// 串口号
FindCommPort(&m_cb_serNum);
for (int i = 1; i < 25; i++)
{
CString strNum;
strNum.Format("%d", i);
m_cb_serNum.AddString(_T("COM") + strNum);
}
m_cb_serNum.SetCurSel(0);
// 波特率
m_cb_btRate.AddString(_T("110"));
m_cb_btRate.AddString(_T("300"));
m_cb_btRate.AddString(_T("600"));
m_cb_btRate.AddString(_T("1200"));
m_cb_btRate.AddString(_T("2400"));
m_cb_btRate.AddString(_T("4800"));
m_cb_btRate.AddString(_T("9600"));
m_cb_btRate.AddString(_T("14400"));
m_cb_btRate.AddString(_T("19200"));
m_cb_btRate.AddString(_T("38400"));
m_cb_btRate.AddString(_T("56000"));
m_cb_btRate.AddString(_T("57600"));
m_cb_btRate.AddString(_T("115200"));
m_cb_btRate.AddString(_T("128000"));
m_cb_btRate.AddString(_T("256000"));
m_cb_btRate.SetCurSel(6);
// 校验位
m_cb_ckBit.AddString(_T("NONE"));
m_cb_ckBit.AddString(_T("ODD"));
m_cb_ckBit.AddString(_T("EVEN"));
m_cb_ckBit.AddString(_T("MARK"));
m_cb_ckBit.AddString(_T("SPACE"));
m_cb_ckBit.SetCurSel(0);
// 数据位
for (int i = 5; i <=8 ; i++)
{
CString strBit;
strBit.Format("%d", i);
m_cb_datBit.AddString(strBit);
}
m_cb_datBit.SetCurSel(3);
// 停止位
m_cb_stpBit.AddString(_T("1"));
m_cb_stpBit.AddString(_T("1.5"));
m_cb_stpBit.AddString(_T("2"));
m_cb_stpBit.SetCurSel(0);
}
// 接收区设置
void CSerialPortDlg::InitReciveSetting()
{
m_ck_recv2.SetCheck(FALSE);
}
// 发送区设置
void CSerialPortDlg::InitSendSetting()
{
m_et_time.SetWindowText(_T("1000"));
m_ck_send3.SetCheck(TRUE);
}
/************************************************************************/
/* 其它辅助函数 */
/************************************************************************/
// 查找计算机可用串口并将串口号设置到Combo Box控件上
void CSerialPortDlg::FindCommPort( CComboBox *pComboBox )
{
HKEY hKey;
#ifdef _DEBUG
ASSERT( pComboBox != NULL );
pComboBox->AssertValid();
#endif
LONG nRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Hardware\\DeviceMap\\SerialComm", NULL,
KEY_READ, &hKey);
if ( nRetVal == ERROR_SUCCESS )
{
int i = 0;
char portName[256], commName[256];
DWORD dwLong, dwSize;
while ( 1 )
{
dwLong = dwSize = sizeof(portName);
nRetVal = RegEnumValue(hKey, i, portName, &dwLong, NULL, NULL, (PUCHAR)commName, &dwSize);
if( nRetVal == ERROR_NO_MORE_ITEMS ) // 枚举串口
break;
CString strCommName;
strCommName.Format("%s", commName);
pComboBox->AddString( strCommName ); // commName:串口名字
i++;
}
if( pComboBox->GetCount() == 0 )
{
//AfxMessageBox("HKEY_LOCAL_MACHINE:Hardware\\DeviceMap\\SerialComm里找不到串口!!!" );
}
RegCloseKey(hKey);
}
}
// 设置状态栏串口状态
void CSerialPortDlg::SetButtonState(BOOL bOpen, UINT ID_ICON)
{
HICON hIcon;
hIcon = AfxGetApp()->LoadIcon( ID_ICON );
m_picCtrl_OnOffFlag.SetIcon(hIcon);
if ( bOpen )
m_bt_onOff.SetWindowText(_T("断开(&O)"));
else
m_bt_onOff.SetWindowText(_T("打开(&O)"));
}
// 设置发送字节数
void CSerialPortDlg::SetSendNumStatus()
{
CString strNum;
strNum.Format("%d", m_nSendNum);
m_st_sendNum.SetWindowText(_T("发送字节数:") + strNum);
}
// 设置状态栏串口状态
void CSerialPortDlg::SetSerialStateStatus(BOOL bOpen)
{
if ( bOpen )
m_st_serState.SetWindowText(_T("串口状态:打开"));
else
m_st_serState.SetWindowText(_T("串口状态:关闭"));
}
// 设置接收字节数
void CSerialPortDlg::SetRecvNumStatus()
{
CString strNum;
strNum.Format("%d", m_nRecvNum);
m_st_recvNum.SetWindowText(_T("接收字节数:") + strNum);
}
// 获取Combo Box控件上的COM数字
long CSerialPortDlg::GetCommPortN()
{
CString strCommPort;
m_cb_serNum.GetWindowText(strCommPort);
strCommPort = strCommPort.Mid(3);
return _ttol(strCommPort);
}
// 获取串口参数
CString CSerialPortDlg::GetCommPortParameter()
{
CString strParameter = "";
CString strBaudRate; // 波特率
CString strDataBit; // 数据位:5, 6, 7, 8
CString strCheckBit; // 校验位:NONE, ODD, EVEN, MARK, SPACE
CString strStopBit; // 停止位:1, 1.5, 2
m_cb_btRate.GetWindowText(strBaudRate);
m_cb_datBit.GetWindowText(strDataBit);
m_cb_ckBit.GetWindowText(strCheckBit);
m_cb_stpBit.GetWindowText(strStopBit);
strCheckBit = strCheckBit.Left(1);
strCheckBit.MakeLower();
strParameter += strBaudRate;
strParameter += _T(",");
strParameter += strDataBit;
strParameter += _T(",");
strParameter += strCheckBit;
strParameter += _T(",");
strParameter += strStopBit;
return strParameter;
}
// 将接收到的内容设置到接收区控件上
void CSerialPortDlg::SetRecvData(CString strData)
{
// 接收转向至文件
if ( m_ck_recv1.GetCheck() == BST_CHECKED )
{
char *szFilePath = m_strAutoSaveFilePath.GetBuffer(0);
m_strAutoSaveFilePath.ReleaseBuffer();
char *szData = strData.GetBuffer(0);
strData.ReleaseBuffer();
SaveRecvData(szFilePath, szData);
return;
}
CString strDataRecvOld;
m_et_dataRecv.GetWindowText(strDataRecvOld);
strDataRecvOld += strData;
// 自动换行显示
if ( m_ck_recv2.GetCheck() && m_CommPort.GetInputSize() == 0)
strDataRecvOld += _T("\r\n");
// 暂停接收显示
if ( !m_ck_recv4.GetCheck() )
m_et_dataRecv.SetWindowText(strDataRecvOld);
// 设置滚动条一直在底部
int nLineCount = m_et_dataRecv.GetLineCount();
m_et_dataRecv.LineScroll(nLineCount - 1);
}
/************************************************************************/
/* 文件IO */
/************************************************************************/
// 从文件对话框获取文件名
CString CSerialPortDlg::GetFilePathFromFileDlg(BOOL bOpenFileDlg, LPCTSTR lpszFileName)
{
char szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");
CFileDialog fileDlg( bOpenFileDlg, _T("txt"), lpszFileName,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
szFilter );
CString strFilePath = _T("");
if ( IDOK == fileDlg.DoModal() )
strFilePath = fileDlg.GetPathName();
return strFilePath;
}
// 保存文件
void CSerialPortDlg::SaveRecvData(const char *szFilePath, const char *szData)
{
if (szFilePath == NULL)
{
MessageBox(_T("文件路径有误,请重新选择!"),
_T("Error"),
MB_ICONERROR | MB_OK);
return;
}
CFile fileOpr;
BOOL bOpen = fileOpr.Open( szFilePath, CFile::shareDenyNone
| CFile::modeWrite
| CFile::modeCreate
| CFile::modeNoTruncate /* 文件存在,则不清空 */
/*| CFile::typeBinary*/ );
if ( bOpen )
{
fileOpr.SeekToEnd();
fileOpr.Write(szData, strlen(szData));
fileOpr.SeekToBegin();
fileOpr.Close();
}
else
MessageBox(_T("文件打开失败!"), _T("Error!"), MB_ICONERROR | MB_OK);
}
// 读取文件信息
CString CSerialPortDlg::ReadFileData(const char *szFilePath)
{
CString strDataRet = _T("");
if (szFilePath == NULL)
return strDataRet;
CFile fileOpr;
BOOL bOpen = fileOpr.Open( szFilePath, CFile::shareDenyNone
| CFile::modeRead
| CFile::modeCreate
| CFile::modeNoTruncate /* 文件存在,则不清空 */
/*| CFile::typeBinary*/ );
if ( bOpen ) // CFile::Open的返回值
{
ULONG nLen = (ULONG)fileOpr.GetLength();
if (nLen < 1)
return strDataRet;
char *buff = new char[nLen+1];
memset(buff, 0, nLen);
fileOpr.Read(buff, nLen);
buff[nLen] = '\0';
strDataRet.Format("%s", buff);
delete []buff;
buff = NULL;
}
return strDataRet;
}
/************************************************************************/
/* 基类成员函数重写 */
/************************************************************************/
// 重写OnClose()函数,窗口关闭时关闭串口,防止一直占用
void CSerialPortDlg::OnClose()
{
if ( m_CommPort.IsOpen() )
m_CommPort.Close();
CDialog::OnClose();
}
/************************************************************************/
/* 串口设置 */
/************************************************************************/
// 打开/关闭按钮
void CSerialPortDlg::OnBnClickedBtOnoff()
{
/* 关闭串口 */
if ( m_bIsOpen )
{
m_CommPort.Close();
if ( !m_CommPort.IsOpen() ) // 已关闭
{
SetButtonState( FALSE, IDI_ICONOFF );
SetSerialStateStatus(FALSE);
CMenu *subMenu = AfxGetMainWnd()->GetMenu()->GetSubMenu(0); // 0:第一列菜单
subMenu->ModifyMenu(0, MF_BYPOSITION, ID_MENU_ONOFF, _T("打开(&O)")); // 0:第一列菜单下第1个子菜单
m_bIsOpen = FALSE;
}
else
{
MessageBox(_T("断开连接错误!"), _T("Error!"), MB_ICONERROR | MB_OK);
}
return;
}//if
/* 打开串口 */
DWORD nCommPort; // 串口号, ULONG型
nCommPort = (unsigned)GetCommPortN();
// 获取串口参数pszPara
CString strPara = GetCommPortParameter();
if (strPara.GetLength() <= 0)
MessageBox(_T("参数获取错误!"), _T("Error"), MB_ICONERROR | MB_OK);
char *pszPara = strPara.GetBuffer(0);
strPara.ReleaseBuffer();
m_CommPort.SetInitParam(); // 设置初始化参数
if ( pszPara != NULL )
m_CommPort.Open(nCommPort, pszPara); // 打开串口
if ( m_CommPort.IsOpen() )
{
SetButtonState( TRUE, IDI_ICONON );
SetSerialStateStatus(TRUE);
CMenu *subMenu = AfxGetMainWnd()->GetMenu()->GetSubMenu(0); // 第一列菜单
subMenu->ModifyMenu(0, MF_BYPOSITION, ID_MENU_ONOFF, _T("断开(&O)"));
m_bIsOpen = TRUE;
}
else
{
MessageBox(_T("串口不存在或者被其他应用程序占用!"), _T("Information"), MB_ICONINFORMATION | MB_OK);
CString strMsg;
m_et_dataRecv.GetWindowText(strMsg);
strMsg += _T("【ERROR】Cannot open COM port\r\n");
m_et_dataRecv.SetWindowText(strMsg);
}//else
}
/************************************************************************/
/* 发送区 */
/************************************************************************/
// 启用文件数据源
void CSerialPortDlg::OnBnClickedCkSend1()
{
if ( m_ck_send1.GetCheck() == BST_CHECKED )
{
CString strFilePath = GetFilePathFromFileDlg(TRUE, NULL);
if (strFilePath == _T(""))
{
m_ck_send1.SetCheck(FALSE);
return;
}
m_strFileDatSrcFilePath = strFilePath;
char *szFilePath = strFilePath.GetBuffer(0);
strFilePath.ReleaseBuffer();
CString strShowSendEdStatic;
strShowSendEdStatic = _T("启用外部文件数据源:\r\n");
strShowSendEdStatic += strFilePath;
m_et_dataSend.SetWindowText(strShowSendEdStatic);
m_et_dataSend.SetReadOnly(TRUE);
}
else
{
OnBnClickedBtClsend(); // 清空发送区内容
m_et_dataSend.SetReadOnly(FALSE);
}
}
// 文件载入按钮
void CSerialPortDlg::OnBnClickedBtLoadfile()
{
if ( m_ck_send1.GetCheck() == BST_CHECKED )
{
MessageBox(_T("启用外部数据源时,无法执行该操作!"), _T("Warning"), MB_ICONWARNING | MB_OK);
return;
}
CString strFilePath = GetFilePathFromFileDlg(TRUE, NULL);
char *szFilePath = strFilePath.GetBuffer(0);
strFilePath.ReleaseBuffer();
CString strFileData = ReadFileData(szFilePath);
m_et_dataSend.SetWindowText(strFileData);
}
// 清除输入按钮
void CSerialPortDlg::OnBnClickedBtClsend()
{
if ( m_ck_send1.GetCheck() == BST_CHECKED )
{
MessageBox(_T("启用外部数据源时,无法执行该操作!"), _T("Warning"), MB_ICONWARNING | MB_OK);
return;
}
m_et_dataSend.SetWindowText(_T(""));
}
// 数据发送按钮
void CSerialPortDlg::OnBnClickedBtSenddata()
{
if ( !m_bIsOpen )
{
MessageBox(_T("串口尚未连接!\r\n发送失败!"), _T("Warning"),
MB_ICONERROR | MB_OK);
return;
}
m_CommPort.ClearOutputBuffer(); // 清除发送缓存区
CString strDataSend;
// 启用文件数据源
if ( m_ck_send1.GetCheck() == BST_CHECKED )
{
char *szFilePath = m_strFileDatSrcFilePath.GetBuffer(0);
m_strFileDatSrcFilePath.ReleaseBuffer();
strDataSend = ReadFileData(szFilePath);
}
else
m_et_dataSend.GetWindowText(strDataSend);
if (strDataSend == _T(""))
return;
char *pszDataSend = strDataSend.GetBuffer(0);
strDataSend.ReleaseBuffer();
long nLen = strlen(pszDataSend);
m_CommPort.Write(pszDataSend, nLen); // 写串口,向串口中写入数据
m_nSendNum += strlen(pszDataSend);
this->SetSendNumStatus();
// 发送完后自动清空
if (m_ck_send3.GetCheck() == BST_CHECKED && m_ck_send1.GetCheck() != BST_CHECKED)
m_et_dataSend.SetWindowText(_T(""));
}
/************************************************************************/
/* 接收区 */
/************************************************************************/
// 接收自动保存至文件
void CSerialPortDlg::OnRecvAutoToFile()
{
if ( m_ck_recv1.GetCheck() == BST_CHECKED )
{
CString strFilePath = GetFilePathFromFileDlg(FALSE, NULL);
if (strFilePath == _T(""))
{
m_ck_recv1.SetCheck(FALSE);
return;
}
m_strAutoSaveFilePath = strFilePath;
CString strShowStatic = _T("接收转向至文件:\r\n");
strShowStatic += strFilePath;
m_et_dataRecv.SetWindowText(strShowStatic);
}
else if ( m_ck_recv1.GetCheck() == BST_UNCHECKED )
{
OnBnClickedBtClrecv();
}
else
{
return;
}
}
// 保存数据按钮
void CSerialPortDlg::OnBnClickedBtSavedata()
{
CString strData;
m_et_dataRecv.GetWindowText(strData);
char *szData = strData.GetBuffer(0);
strData.ReleaseBuffer();
CString strFilePath = GetFilePathFromFileDlg(FALSE, _T("ReciveData.txt"));
char *szFilePath = strFilePath.GetBuffer(0);
strFilePath.ReleaseBuffer();
SaveRecvData(szFilePath, szData);
}
// 清除显示按钮
void CSerialPortDlg::OnBnClickedBtClrecv()
{
m_et_dataRecv.SetWindowText(_T(""));
}
/************************************************************************/
/* 菜单项 */
/************************************************************************/
// 菜单项关闭/打开
void CSerialPortDlg::OnMenuOnoff()
{
OnBnClickedBtOnoff();
}
// 菜单栏关于
void CSerialPortDlg::OnMenuAbout()
{
CAboutDlg AbtDlg;
AbtDlg.DoModal();
}
// 菜单退出按钮
void CSerialPortDlg::OnMenuExit()
{
this->OnOK();
}
/************************************************************************/
/* 模拟状态栏 */
/************************************************************************/
// 复位计数按钮
void CSerialPortDlg::OnBnClickedBtClearnum()
{
m_nSendNum = 0;
m_nRecvNum = 0;
this->SetSendNumStatus();
this->SetRecvNumStatus();
}
3. 说明
CBasCommPort类是对MSCOmm控件的封装,该类由动态链接库导出,这里不提供源码,项目里有该动态链接库的头文件和相应的DLL和LIB文件,下载地址请转至文末查看。另外,本次开发的串口调试助手能够正确发送中文,但接收中文时乱码,十六进制的发送和接收消息转为十六进制还没实现。
4. 项目下载地址
串口调试助手C++源码(VS 2008):https://download.csdn.net/download/wu9797/10556936