MFC之路 串口通信篇(之三)

来源:互联网 发布:win7摄像头软件ecap 编辑:程序博客网 时间:2024/05/22 04:36


上一节中我们完成了串口监听线程的创建和线程响应函数的创建,在线程响应函数中,有这样一条语句:

if(ComStat.cbInQue)  //如果有数据到达{//AfxMessageBox("receive");pDlg->ProcessCOMMNotification(EV_RXCHAR,0);}

从上面的语句知道,当监听线程检测到有数据到达读缓冲区时,调用ProcessCOMMNotification()函数。那么,我们对数据的处理操作将会在此函数中来完成。此函数是主对话框类下的成员函数。

要涉及到数据的显示、处理、发送等操作了,我们必须首先把我们的界面和相应的变量完善起来。

1、添加界面控件

控件添加如上图片所示,并为各个控件关联变量。

2、数据处理函数。 之后就可以开始数据处理的函数了:

//当有数据到达串口时响应函数BOOL CSerialComSoftwareDlg::ProcessCOMMNotification (UINT wParam,long lParam){CFile getdatafile;  //定义一个文件变量CFileException e;   //与文件相关异常变量//AfxMessageBox("begin read!");BOOL judgefile=m_writetofile.GetCheck();  //写入到文件选项是否选中//判断串口是否已关闭if(!m_bConnected)return FALSE;//判断收到的是否读缓冲消息if((EV_RXCHAR&wParam)!=EV_RXCHAR)return FALSE;//判断是否写入文件,并打开文件,将指针定在文件末尾if(judgefile){getdatafile.Open(m_savefilename,CFile::modeCreate+CFile::modeNoTruncate+CFile::modeWrite,&e);getdatafile.SeekToEnd();}//将数据读入CString变量中CString StrTemp;BYTE abIn[MAXBLOCK];int len;len=ReadBlock(abIn,MAXBLOCK);   //读取缓存区中的数据存入abIn[]//AfxMessageBox("I have getten it!");if(!len){AfxMessageBox(_T("读字符出错,请检查。"));return FALSE;}for(int i=0;i<len;i++){BYTE bt=abIn[i];if(m_ctrlHexDisplay.GetCheck())  //十六进制显示StrTemp.Format(_T("%02x "),bt);  else    StrTemp.Format(_T("%c"),bt);  //字符串显示m_strRXData+=StrTemp;if(judgefile){getdatafile.Write(StrTemp,1);  //写入文件}}m_rxlen+=len;//将字符显示在指定位置if(judgefile)getdatafile.Close();  //关闭文件m_EditRXData.SetWindowText(m_strRXData);DispStatus();  //状态栏显示return TRUE;}


以上的数据处理函数都是一些基本的对接收的数据的显示等基本操作处理,不再过多的解释了。

其中,关键的一个部分是读取串口缓冲区中数据函数ReadBlock()

//读缓冲区int CSerialComSoftwareDlg::ReadBlock(BYTE *abIn, int MaxLength){BOOL JudgeRead;COMSTAT ComStat;DWORD dwErrorFlags,dwLength;ClearCommError(m_hCom,&dwErrorFlags,&ComStat);  //清楚通信的硬件错误并获取串口信息if(dwErrorFlags>0){AfxMessageBox(_T("读串口错,请检查参数设置。"));PurgeComm(m_hCom,PURGE_RXABORT|PURGE_RXCLEAR);return 0;}dwLength=((DWORD)MaxLength<ComStat.cbInQue?MaxLength:ComStat.cbInQue);  //读取指定长度字符或者所有缓冲区数据memset(abIn,0,MaxLength);//如果有字符即读入if(dwLength){JudgeRead=ReadFile(m_hCom,abIn,dwLength,&dwLength,&m_osRead);//读出字符至abIn处if(!JudgeRead){//如果重叠操作未完成,等待直到操作完成if(GetLastError()==ERROR_IO_PENDING){//WaitForSingleObject(m_osRead.hEvent,INFINITE);GetOverlappedResult(m_hCom,&m_osRead,&dwLength,TRUE);m_osRead.Offset=0;//m_osRead.Offset=(m_osRead.Offset+dwLength)%MAXBLOCK;}elsedwLength=0;}}return dwLength;}

至此,对于串口数据的读取工作就全部结束了。

3、数据发送功能

//数据发送函数void CSerialComSoftwareDlg::OnClickedButtonSend(){// TODO: 在此添加控件通知处理程序代码CString filepath;       //文件路径char abOut[MAXBLOCK];int OutNum,length;if(!m_bConnected){AfxMessageBox(_T("串口未打开!"));return;}m_sendbutten.EnableWindow(FALSE);m_transfile.EnableWindow(FALSE);m_emptytx.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);}elselength=WriteBlock(abOut,OutNum);m_txlen+=OutNum;OutNum=fsendout.Read(abOut,MAXBLOCK);//准备下一次循环//continue;}//while(OutNum)fsendout.Close();//if(!length)//{//AfxMessageBox("已写完!");m_sendbutten.EnableWindow(TRUE);m_transfile.EnableWindow(TRUE);m_emptytx.EnableWindow(TRUE);DispStatus();return;//}//continue;//return;}//if(!filepath.IsEmpty())//读文本框内容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("%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);}elselength=WriteBlock(abOut,len);m_txlen+=length;/*if(!length)AfxMessageBox("无数据能写入缓冲区!");*/m_sendbutten.EnableWindow(TRUE);m_transfile.EnableWindow(TRUE);m_emptytx.EnableWindow(TRUE);DispStatus();return;}


此函数需要注意的就是WriteBlock()函数,即写入串口的函数:

//写缓冲区int CSerialComSoftwareDlg::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;}//CString l;dwLength=MaxLength;lentest=0;JudgeWrite=WriteFile(m_hCom,abOut,dwLength,&lentest,&m_osWrite);//l.Format("%d",dwLength);//AfxMessageBox(l);if(!JudgeWrite){if(GetLastError()==ERROR_IO_PENDING){//AfxMessageBox("11111111");GetOverlappedResult(m_hCom,&m_osWrite,&lentest,TRUE);//l.Format("%ld",lentest);//AfxMessageBox(l);}elselentest=0;}return lentest;}


其中,用到了十六进制的数据转换,这里自己写了两个函数:

//字符串转十六进制int CSerialComSoftwareDlg::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;elsehexdata=hexdata*16+lowhexdata;i++;SendOut[hexdatalen]=(char)hexdata;hexdatalen++;}//senddata.SetSize(hexdatalen);return hexdatalen;}//转十六进制char CSerialComSoftwareDlg::ConvertHexData(char ch){if((ch>='0')&&(ch<='9'))return ch-0x30;if((ch>='A')&&(ch<='F'))return ch-'A'+10;if((ch>='a')&&(ch<='f'))return ch-'a'+10;    else return(-1);}


至此,发送功能也就完成了。好了,串口通信的知识就总结到这了。

我的源代码已经上传到个人资源中,http://download.csdn.net/detail/walkman_lfq/9913994,需要的可以自己下载。


原创粉丝点击