【MFC串口】具有收发和搜索功能的串口工具的实现
一款具备串口端口搜索、串口数据的收发、定时搜索串口和定时发送数据等功能的串口(调试)工具。方便串口的调试和串口设备的安装。
前言:
为了方便串口的调试和串口设备的安装,这里编写了一款具备串口端口搜索、串口数据的收发、定时搜索串口和定时发送数据等功能的串口(调试)工具。
工具下载:https://download.csdn.net/download/wjy27/88371929https://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
更多推荐
所有评论(0)