上一节中我们完成了串口监听线程的创建和线程响应函数的创建,在线程响应函数中,有这样一条语句:
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;
}
else
dwLength=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);
}
else
length=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);
}
else
length=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);
}
else
lentest=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;
else
hexdata=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,需要的可以自己下载。