学习了MFC测试看着别人的例程写了一个串口调试助手,
分享一下我的学习过程,我调用了一个定时器,一个MSCOMM控件,
我实现了自动搜索可打开串口,串口波特率,奇偶校验,各种配置串口功能的实现,没有编辑16位发送,感觉很鸡肋就没有写,
程序很简单基本上就是在配置MSCOMM控件上花了很多时间。
这是我的可视化界面,首先加入这个串口控件
为MSCOMM建立一个事件:参照这个程序
我为了显示16进制数据在这个函数上进行了修改
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048];
CString strtemp,buffer;
if(m_ctrlComm.get_CommEvent()==2)//等于二表明接收到数据
{
if(BST_UNCHECKED==((CButton*)GetDlgItem(IDC_CHECK16RECEIVE))->GetCheck())
{
以下是正常显示效果处理代码
variant_inp=m_ctrlComm.get_Input(); //读缓冲区
safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
len=safearray_inp.GetOneDimSize(); //得到有效数据长度
for(k=0;k<len;k++)
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
for(k=0;k<len;k++) //将数组转换为Cstring型变量
{
BYTE bt=*(char*)(rxdata+k);//字符型
strtemp.Format(_T("%c"),bt); //将字符送入临时变量strtemp存放
if(strtemp=='\n'||strtemp=='\r')
{
strtemp="\r\n";
}
此处编译有错误,可在项目属性->配置属性->常规里将“字符集”改为使用多字节字符集即可
m_strRXDATA+=strtemp; //加入接收编辑框对应字符串
}
//UpdateData(FALSE); //更新编辑框内容
SetDlgItemText(IDC_RECEIVE_EDIT1,m_strRXDATA);
/*IDC_RECEIVE_EDIT1->ReplaceSel(m_strRXDATA, FALSE);*/
m_recvshw.LineScroll(m_recvshw.GetLineCount());
}
else
{
variant_inp=m_ctrlComm.get_Input(); //读缓冲区
safearray_inp=variant_inp; //VARIA NT型变量转换为ColeSafeArray型变量
len=safearray_inp.GetOneDimSize(); //得到有效数据长度
for(k=0;k<len;k++)
{
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
/* strtemp.Format(_T("%02X"), *(rxdata + k));
buffer += strtemp;*/
}
//buffer.TrimLeft();
//buffer.TrimRight();
//CByteArray receivedata;
//int receivedatalen;
//receivedatalen = Str2Hex(buffer, receivedata);
for(k=0;k<len;k++)
{
switch(rxdata[k]/16)
{
case 0: m_strRXDATA+='0';break;
case 1: m_strRXDATA+='1';break;
case 2: m_strRXDATA+='2';break;
case 3: m_strRXDATA+='3';break;
case 4: m_strRXDATA+='4';break;
case 5: m_strRXDATA+='5';break;
case 6: m_strRXDATA+='6';break;
case 7: m_strRXDATA+='7';break;
case 8: m_strRXDATA+='8';break;
case 9: m_strRXDATA+='9';break;
case 10: m_strRXDATA+='A';break;
case 11: m_strRXDATA+='B';break;
case 12: m_strRXDATA+='C';break;
case 13: m_strRXDATA+='D';break;
case 14: m_strRXDATA+='E';break;
case 15: m_strRXDATA+='F';break;
}
switch(rxdata[k]%16)
{
case 0: m_strRXDATA+='0';break;
case 1: m_strRXDATA+='1';break;
case 2: m_strRXDATA+='2';break;
case 3: m_strRXDATA+='3';break;
case 4: m_strRXDATA+='4';break;
case 5: m_strRXDATA+='5';break;
case 6: m_strRXDATA+='6';break;
case 7: m_strRXDATA+='7';break;
case 8: m_strRXDATA+='8';break;
case 9: m_strRXDATA+='9';break;
case 10: m_strRXDATA+='A';break;
case 11: m_strRXDATA+='B';break;
case 12: m_strRXDATA+='C';break;
case 13: m_strRXDATA+='D';break;
case 14: m_strRXDATA+='E';break;
case 15: m_strRXDATA+='F';break;
}
m_strRXDATA+=' ';
}
UpdateData(FALSE); //更新编辑框内容
m_recvshw.LineScroll(m_recvshw.GetLineCount());
}
//}
//else{;}
}
}
这个函数会获取check就是打对号那种控件的值判断是执行那一句操作
这里注意我是把接受控件的Multiline选为TRUL才能自自动换行,还要把Auto HScroll配置为flase ,Auto VScroll为真,还可以把READ only打开这样就不会再输入数据
我是在类中建立了一个全局变量
bool Uart_Init;//串口初始化完成标志位
一旦我检测到配置变化就把串口关闭
DATASET_BOX dataset={0,1,3,0,1};
CStringArray budecode;
CStringArray datecode;
CStringArray stopcode;
CStringArray testcode;;/*={"n,","o,","e,"};*/
建立了四个动态数组注意检测位的无奇校验,偶校验是用 n, o, e控制的
我得打开串口配置函数如下
if(Uart_Init!=true)
{
dataset.sm_comboCOM = m_comboCOM.GetCurSel();//获取组合框控件的列表框中选中项的索引
dataset.sm_comboSTOP=m_comboSTOP.GetCurSel();
dataset.sm_comboTEST=m_comboTEST.GetCurSel();
dataset.sm_comboDATA=m_comboDATA.GetCurSel();
dataset.sm_comboBUTE = m_comboBUTE.GetCurSel();
//CString comtemp;
//comtemp.Format(_T("COM%d"),comstruct.commom[fori]);
//m_comboCOM.AddString(comtemp);
m_ctrlComm.put_CommPort( comstruct.commom[dataset.sm_comboCOM+1]);//设置com1
char buff[15];
strcpy(buff,budecode[dataset.sm_comboBUTE]);
strcat(buff,testcode[dataset.sm_comboTEST]);
strcat(buff,datecode[dataset.sm_comboDATA]);
strcat(buff,stopcode[dataset.sm_comboSTOP]);
CString cstr(buff);
LPCTSTR pStr = LPCTSTR(cstr);
m_ctrlComm.put_Settings(pStr);//设置串口参数,波特率,无奇偶校验,位停止位,位数据位
m_ctrlComm.put_Settings(_T("9600,n,8,1"));
//m_ctrlComm.put_RThreshold(1);
//m_ctrlComm.put_InBufferSize(2048);
//m_ctrlComm.put_OutBufferSize(1024);
m_ctrlComm.put_PortOpen(TRUE);//打开串口
m_ctrlComm.put_RThreshold(2);//收到两个字节引发OnComm事件
m_ctrlComm.put_InputMode(1);//输入模式选为二进制
CString str=budecode.GetAt(dataset.sm_comboBUTE);
str+=testcode.GetAt(dataset.sm_comboTEST);
str+=datecode.GetAt(dataset.sm_comboDATA);
str+=stopcode.GetAt(dataset.sm_comboSTOP);
LPCTSTR pStr = LPCTSTR(str);
m_ctrlComm.put_Settings(pStr);//设置串口参数,波特率,无奇偶校验,位停止位,位数据位
m_ctrlComm.put_InputMode(1); // 以二进制方式检取数据
m_ctrlComm.put_RThreshold(1); //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件
m_ctrlComm.put_InputLen(0); //设置当前接收区数据长度为0
m_ctrlComm.get_Input();//先预读缓冲区以清除残留数据
m_ctrlComm.put_OutBufferCount(0);
GetDlgItem(IDC_OPENUART)->SetWindowText(_T("关闭串口"));
Uart_Init=1;
if(!m_ctrlComm.get_PortOpen())
{
Uart_Init=1;
GetDlgItem(IDC_OPENUART)->SetWindowText(_T("打开串口"));
//
}
}
else
{
GetDlgItem(IDC_OPENUART)->SetWindowText(_T("打开串口"));
Uart_Init=0;
m_ctrlComm.put_PortOpen(FALSE);
}
}
在网上找了很多都没有我这种配置形式,这样可以随意配置自己的波特率,等等参数只要在参数里就好了
最后就是发送函数
void CUARTDlg::OnBnClickedSend()
{
// TODO: 在此添加控件通知处理程序代码
if (Uart_Init == true) //判断是否打开并初始化串口
{
if(BST_UNCHECKED==((CButton*)GetDlgItem(IDC_CHECK16SEND))->GetCheck())
{
if(BST_UNCHECKED==((CButton*)GetDlgItem(IDC_CHECKNEWLINE))->GetCheck())
{
UpdateData(TRUE); //读取编辑框内容
m_ctrlComm.put_Output(COleVariant(m_strTXDATA)); //发送数据
}
else
{
UpdateData(TRUE); //读取编辑框内容
m_strTXDATA+="\r\n";
m_ctrlComm.put_Output(COleVariant(m_strTXDATA)); //发送数据
}
}
}
else
{
MessageBox(_T("请先打开串口"));
return;
}
}
基本就是这样,还有我是用定时器扫描串口来时时检测串口数量和值的
建立定时器的这个文章很好讲的也很透彻我就把可视化配置图片 给大家看一下
在这里添加类向导
双击加入这个On Timer,我们就会再cXXXDLG.C的函数中找到一下函数
oid CUARTDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDialogEx::OnTimer(nIDEvent) ;
switch(nIDEvent)
{
case 1:{
QueryKey();
if(comstruct.commax>=1)
{
for(int fori=1;fori<=comstruct.commax;fori++)
{
CString comtemp;
comtemp.Format(_T("COM%d"),comstruct.commom[fori]);
m_comboCOM.AddString(comtemp);
m_comboCOM.SetCurSel(0);
}
}
}break;
default:break;
}
}
QueryKey();
这个函数是我自己写的扫描系统串口函数
void CUARTDlg::QueryKey(void)
{
HANDLE hCom;
int i,num;
CString str;
BOOL flag;
((CComboBox *)GetDlgItem(IDC_COMCOM))->ResetContent();
flag = FALSE;
num = 0;
comstruct.commax=0;
for (i = 1;i <= 16;i++)
{//此程序支持16个串口
str.Format("\\\\.\\COM%d",i);
hCom = CreateFile(str, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(INVALID_HANDLE_VALUE != hCom )
{
//能打开该串口,则添加该串口
CloseHandle(hCom);
str = str.Mid(4);
//((CComboBox *)GetDlgItem(IDC_COMCOM))->AddString(str);
comstruct.commax++;
comstruct.commom[comstruct.commax]=i;
if (flag == FALSE)
{
flag = TRUE;
num = i;
}
}
}
/* i = ((CComboBox *)GetDlgItem(IDC_COMCOM))->GetCount();*/
if (num == 0)
{//若找不到可用串口则禁用“打开串口”功能
((CComboBox *)GetDlgItem(IDC_COMCOM))->EnableWindow(FALSE);
}
else
{
//k = ((CComboBox *)GetDlgItem((IDC_COMCOM)))->GetCount();
//((CComboBox *)GetDlgItem(IDC_COMCOM))->SetCurSel(k - 1);
//mCom.BindCommPort(num);
}
}
这个就是扫描函数我在函数中建立一个结构体
struct commmmm
{
int commax;
int commom[20];
} comstruct={0,0};
commax是为了记录一共扫描到几个串口对应的common函数是对应串口号
我在扫描显示时能够体现出来。大概程序就是这个思路我的程序上传了上来,点击打开链接
如果有疑惑,可以留言相互交流,相互学习。如有错误,请各位大佬支出啊