串口调试助手代码分析42.docx
《串口调试助手代码分析42.docx》由会员分享,可在线阅读,更多相关《串口调试助手代码分析42.docx(25页珍藏版)》请在冰豆网上搜索。
串口调试助手代码分析42
第5章串口调试助手代码分析
1、建立基于对话框的工程SCOMM
2、绘制界面,如下图:
接收区
串口组合框:
IDC_COMBO_COMSELECT,m_Com
波特率组合框:
IDC_COMBO_SPEED,m_Speed
停止位组合框:
IDC_COMBO_STOPBITS,m_StopBits
数据位组合框:
IDC_COMBO_DATABITS,m_DataBits
校验位组合框:
IDC_COMBO_PARITY,m_Parity
十六进制显示(接收):
IDC_CHECK_HEXRECIEVE,m_ctrlHexReceieve
接收编辑框:
IDC_EDIT_RECIVE,m_ReceiveDatam_ctrlReceiveDataStyle:
VerticalScrollMultiLine
打开串口IDC_BUTTON_OPENPORT,m_ctrlOpenPort
串口开关标志图标IDC _STATIC_OPENOFF,m_ctrlIconOpenoff
数据文件保存路径IDC _EDIT_SAVEPATH,m_strCurPath
保存显示数据文件路径IDC _EDIT_SAVEPATH,m_ctrlSavePath
接收计数IDC_STATIC_RXCOUNT,m_ctrlRXCOUNT
发送区
…。
。
。
。
。
。
。
。
。
。
。
。
。
。
3、添加CSeraiPort类文件
将类文件SerialPort.hSerialPort.cpp复制到工程所在文件夹中(选择改进后的类),然后单击VC6.0菜单Projrct->AddtoProjrct->Files…,再在打开的文件选择对话框中选择SerialPort.h和SerialPort.cpp,点击OK,就把类文件加入当前工程,并在SCOMMDlg.h中加入头文件,#include"SerialPort.h",通过上述步骤就在当前工程中加入了CSeraiPort类。
4、完成串口消息处理函数OnCommunicatiom
在CserailPort类中有多个串口事件可以响应。
在一般串口编程中,只需要处理WM_COMM_RXCHAR消息就可以了,该类所有的消息均需要人工添加消息处理函数。
我们将处理函数名定义为OnComm()。
首先在SCOMMDlg.h中添加串口字符接收消息WM_COMM_RXCHAR(串口接收缓冲区内有一个字符)响应函数的声明:
如下图
然后,在SCOMMDlg.cpp文件中进行WM_COMM_RXCHAR消息映射:
如下图;
接着,在SCOMMDlg.cpp文件中加入函数OnCommunication(WPARAMch,LPARAMport)的实现,暂不添加代码。
LONGCSCOMMDlg:
:
OnCommunication(WPARAMch,LPARAMport)
{
return0;
}
以上步骤需要手工完成。
至此完成了程序的对话框模板,在工程中插入了串口操作类CserailPort类。
5、添加串口初始化及关闭
程序中有两种方法大开串口,一是程序启动,调用OnInitDialog()函数,就可以打开串口,缺省的串口号为COM1,如果COM1不存在或占用,就会给出提示;另外,单击“打开串口”按钮也可以打开串口。
//在初始化中打开串口
BOOLCSCOMMDlg:
:
OnInitDialog()
{
m_nBaud=9600;//波特率
m_nCom=1;//串口号
m_cParity='N';//奇偶校验
m_nDatabits=8;//数据位
m_nStopbits=1;//停止位
m_dwCommEvents=EV_RXFLAG|EV_RXCHAR;//串口事件
//if(m_Port.InitPort(this,1,9600,'N',8,1,dwCommEvents,512))
CStringstrStatus;
if(m_Port.InitPort(this,m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits,m_dwCommEvents,512))//如果启动串口成功
{
m_Port.StartMonitoring();//启动监测辅助线程
strStatus.Format("STATUS:
COM%dOPENED,%d,%c,%d,%d",m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits);//打印串口状态及参数
m_ctrlIconOpenoff.SetIcon(m_hIconRed);//设置串口状态灯
//m_ctrlIconOpenoff.SetIcon(m_hIconOff);
//"当前状态:
串口打开,无奇偶校验,8数据位,1停止位");
}
else//如果启动失败
{
AfxMessageBox("没有发现此串口");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);
}
m_ctrlPortStatus.SetWindowText(strStatus);//显示串口的状态及参数
returnb;
}
6、在ClassWizard中为按钮“打开串口”控制IDC_BUTTON_OPENPORT添加单击响应函数。
//打开,关闭串口
voidCSCOMMDlg:
:
OnButtonOpenport()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
m_bOpenPort=!
m_bOpenPort;//取反
if(m_bOpenPort)//如果串口是打开的
{
if(m_ctrlAutoSend.GetCheck())//检测自动发送标志是否打开
{
m_bOpenPort=!
m_bOpenPort;//恢复原来的标志
AfxMessageBox("请先关掉自动发送");
return;//返回
}
m_ctrlOpenPort.SetWindowText("打开串口");
m_Port.ClosePort();//关闭串口
m_ctrlPortStatus.SetWindowText("STATUS:
COMPortClosed");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);
//m_hIconRed;//串口打开时的红灯图标句柄
//HICONm_hIconOff;//串口关闭时的指示图标句柄}
else//打开串口
{
m_ctrlOpenPort.SetWindowText("关闭串口");
CStringstrStatus;
if(m_Port.InitPort(this,m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits,m_dwCommEvents,512))
{
m_Port.StartMonitoring();//启动监视线程
m_ctrlIconOpenoff.SetIcon(m_hIconRed);//m_hIconRed;串口打开时的红灯图标句柄
strStatus.Format("STATUS:
COM%dOPENED,%d,%c,%d,%d",m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits);
//"当前状态:
串口打开,无奇偶校验,8数据位,1停止位");
}
else//如果没有打开串口成功
{
AfxMessageBox("没有发现此串口或被占用");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);//m_hIconOff串口关闭时的红灯指示图标句柄
}
m_ctrlPortStatus.SetWindowText(strStatus);
}
}
7、为了在程序关闭时通过关闭串口并释放占用资源,在ClassWizard中为CSCOMMDlg添加了WM_DISTROY的消息响应函数OnDestroy(),函数在主窗口即将销毁时调用。
//为主窗口关闭时添加关闭响应函数
voidCSCOMMDlg:
:
OnDestroy()
{
CDialog:
:
OnDestroy();
m_ctrlAutoSend.SetCheck(0);//强行关闭自动发送
KillTimer
(1);//关闭定时器
KillTimer(4);
m_Port.ClosePort();//关闭串口
m_ReceiveData.Empty();//清空接收数据字符串
}
8、十六进制数据发送处理
首先为CSCOMMDlg类添加两个成员函数Str2Hex()和HexChar,前者对后者进行了调用,Str2Hex()的作用是将一个字符串作为十六进制转化为一个字符组,其中,data即为返回的数组,函数的返回值为data数组的长度。
//将一个字符串作为十六进制转化为一个字符数组,字节间可用空格分隔,
//返回转换后的字节数组长度,同时字节数组长度自动设置
intCSCOMMDlg:
:
Str2Hex(CStringstr,char*data)
{
intt,t1;
intrlen=0,len=str.GetLength();//获取字符串的长度
//data.SetSize(len/2);
for(inti=0;i{
charl,h=str[i];
if(h=='')//如果有空格
{
i++;
continue;//跳出本次循环,进入for语句下次循环
}
i++;
if(i>=len)break;//跳出for循环
l=str[i];
t=HexChar(h);
t1=HexChar(l);
if((t==16)||(t1==16))
break;
else
t=t*16+t1;
i++;
data[rlen]=(char)t;
rlen++;
}
returnrlen;
}
charCSCOMMDlg:
:
HexChar(charc)
{
if((c>='0')&&(c<='9'))
returnc-0x30;
elseif((c>='A')&&(c<='F'))
returnc-'A'+10;
elseif((c>='a')&&(c<='f'))
returnc-'a'+10;
else
return0x10;
}
9、手动发送处理
在ClassWizard中为手动发送按钮IDC_BUTTON_MANUALSEND添加单击处理函数(或直接在对话框模板中双击该控件),在OnButtonManualsend()添加如下代码:
longTX_count=0;
voidCSCOMMDlg:
:
OnButtonManualsend()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
if(m_Port.m_hComm==NULL)//发送时要检验串口是否打开,否则会出错
{
m_ctrlAutoSend.SetCheck(0);
AfxMessageBox("串口没有打开,请打开串口");
return;
}
else//如果串口已经打开
{
UpdateData(TRUE);//读取编辑框内容
if(m_ctrlHexSend.GetCheck())//发送十六进制数据
{
chardata[512];//定义一个512字节的数组
intlen=Str2Hex(m_strSendData,data);
m_Port.WriteToPort(data,len);
TX_count+=(long)((m_strSendData.GetLength()+1)/3);
//计数发送的十六进制数据,注意这里的计算方法,
//只有严格按照规则输入才能正确计算
//m_Port.WriteToPort(hexdata);
}
else//发送ASCII文本
{
m_Port.WriteToPort((LPCTSTR)m_strSendData);//发送数据
TX_count+=m_strSendData.GetLength();//发送计数
}
CStringstrTemp;
strTemp.Format("TX:
%d",TX_count);
m_ctrlTXCount.SetWindowText(strTemp);//显示计数
}
}
10、自动发送处理
自动发送时,需要用到定时器。
打开ClassWizard,为CSCOMMDlg类添加WM_TIMER消息处理函数OnTimer(UINTnIDEvent)。
需要注意的是,在VC6.0中,每个定时器都有自己的ID号,所有定时器处理均要在该函数中,因此,必须事先为相应的定时器设置ID号,OnTimer(UINTnIDEvent)函数则根据调用的nIDEvent值来确定是哪个定时器的定时时间到,再做相应的处理。
voidCSCOMMDlg:
:
OnTimer(UINTnIDEvent)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
CStringstrStatus;
switch(nIDEvent)
{
case1:
//定时器ID=1为自动发送时间到
OnButtonManualsend();//调用手动发送处的理函数即可
break;
case2:
//其他定时器
m_ctrlSavePath.SetWindowText(m_strCurPath);
KillTimer
(2);
break;
case3:
m_ctrlManualSend.EnableWindow(TRUE);
m_ctrlAutoSend.EnableWindow(TRUE);
m_ctrlSendFile.EnableWindow(TRUE);
m_strSendFilePathName=m_strTempSendFilePathName;
m_ctrlEditSendFile.SetWindowText(m_strSendFilePathName);//m_strSendFilePathName
KillTimer(3);
if(!
(m_ctrlAutoSend.GetCheck()))
{
if(m_Port.InitPort(this,m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits,m_dwCommEvents,512))
{
m_Port.StartMonitoring();
strStatus.Format("STATUS:
COM%dOPENED,%d,%c,%d,%d",m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits);
m_ctrlIconOpenoff.SetIcon(m_hIconRed);
}
else
{
AfxMessageBox("Failedtoresetsendbuffersize!
");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);
}
m_ctrlPortStatus.SetWindowText(strStatus);
}
break;
case4:
m_animIcon.ShowNextImage();
break;
default:
break;
}
CDialog:
:
OnTimer(nIDEvent);
}
我们再来看看如何设置定时器1,在ClassWizar中,为自动发送选项IDC_CHECK_AUTOSEND添加响应函数OnCheckAutosend(),或者在对话框模板中直接双击该控件。
在OnCheckAutosend()中添加如下代码:
voidCSCOMMDlg:
:
OnCheckAutosend()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
m_bAutoSend=!
m_bAutoSend;//标志是否打开自动发送
if(m_bAutoSend)
{
if(m_Port.m_hComm==NULL)//先检测串口是不是打开
{
m_bAutoSend=!
m_bAutoSend;
m_ctrlAutoSend.SetCheck(0);
AfxMessageBox("串口没有打开,请打开串口");
return;
}
else
SetTimer(1,m_nCycleTime,NULL);//设置定时器1,启动
}
else//如果自动发送标志没有打开
{
KillTimer
(1);//关掉定时器1
}
}
在自动发送时,如果输入编辑框中的内容改变时,要及时将串口发送的内容改变,在ClassWizar中编辑框IDC_EDIT_SEND添加响应函数。
voidCSCOMMDlg:
:
OnChangeEditSend()
{
//TODO:
IfthisisaRICHEDITcontrol,thecontrolwillnot
//sendthisnotificationunlessyouoverridetheCDialog:
:
OnInitDialog()
//functionandcallCRichEditCtrl().SetEventMask()
//withtheENM_CHANGEflagORedintothemask.
//TODO:
Addyourcontrolnotificationhandlercodehere
UpdateData(TRUE);//在编辑内部改变时,及时响应读取编辑框的内容
}
同样,当自动发送周期改变后,也需要将发送周期更新,但实际的发送周期需要重新用SetTimer()函数对定时器的定时间进行设置,在这个程序里,必须先关闭串口的“自动发送”然后再打开,新设的自动发送周期才能生效。
voidCSCOMMDlg:
:
OnChangeEditCycletime()
{
//TODO:
IfthisisaRICHEDITcontrol,thecontrolwillnot
//sendthisnotificationunlessyouoverridetheCDialog:
:
OnInitDialog()
//functionandcallCRichEditCtrl().SetEventMask()
//withtheENM_CHANGEflagORedintothemask.
//TODO:
Addyourcontrolnotificationhandlercodehere
CEdit*pEdit=(CEdit*)GetDlgItem(IDC_EDIT_CYCLETIME);
CStringstrText;
pEdit->GetWindowText(strText);
m_nCycleTime=atoi(strText);
}
11、接收处理及十六进制显示
接收处理均在串口事件消息处理函数OnCommunication(WPARAMch,LPARAMport)函数中实现。
其中,十六进制的接收显示时并不像发送那样麻烦,只要将数据直接以十六进制打印输出就可以了,注意中间插入一个空格。
LONGCSCOMMDlg:
:
OnCommunication(WPARAMch,LPARAMport)
{
if(port<=0||port>4)
return-1;
rxdatacount++;//接收的字节计数
CStringstrTemp;
strTemp.Format("%ld",rxdatacount);
strTemp="RX:
"+strTemp;
m_ctrlRXCOUNT.SetWindowText(strTemp);//显示接收计数
if(m_bStopDispRXData)//如果选择了“停止显示”接收数据,则返回
return-1;//注意,这种情况下,计数仍在继续,只是不显示
//若设置了“自动清空”,则达到50行后,自动清空接收编辑框中显示的数据
if((m_ctrlAutoClear.GetCheck())&&(m_ctrlReceiveData.GetLineCount()>=50))
{
m_ReceiveData.Empty();
UpdateData(FALSE);
}
//如果没有“自动清空”,数据行达到400后,也自动清空
//因为数据过多,影响接收速度,显示是最费CPU时间的操作
if(m_ctrlReceiveData.GetLineCount()>400)
{
m_ReceiveData.Empty();
m_ReceiveData="***TheLengthoftheTextistoolong,EmptiedAutomaticly!
!
!
***\r\n";
UpdateData(FALSE);
}
//如果选择了"十六进制显示",则显示十六进制值
CStringstr;
if(m_ctrlHexReceieve.GetCheck())
str.Format("%02X",ch);
else
str.Format("%c",ch);
//以下是将接收的字符加在字符串的最后,这里费时很多
//但考虑到数据需要保存成文件,所以没有用ListControl
intnLen=m_ctrlReceiveData.GetWindowTextLength();
m_ctrlReceiveData.SetSel(nLen,nLen);
m_ctrlReceive