S 串口编程 详解5 串口数据的接收
接收数据的文件保存
串口有时要把接收到的数据保存在文件。保存文件的缺省路径是C:\COMDATA.文件名为Rec**.txt.
”保存显示数据“按钮IDC_BUTTON_SAVEDATA添加响应函数OnButtonSavedata().代码如下:
//接收到的数据在文件中进行保存
void CSCOMMDlg::OnButtonSavedata()
{
UpdateData(TRUE);//由屏幕到 内存的
int nLength;
nLength=m_StrCurPath.GetLength();
for(int nCount=0;nCount<nLength;nCount++)
{
if(m_StrCurPath.GetAt(nCount)=='\\')
{
CreateDirectory(m_StrCurPath.Left(nCount+1),NULL);//创建一级目录或二级目录 三……
}
}
CreateDirectory(m_StrCurPath,NULL);//创建目录
CFile m_rFile;
//LPCSTR是Win32和VC++所使用的一种字符串数据类型。LPCSTR被定义成是一个指向以NULL(‘\0’)结尾的常量字符的指针。
LPCSTR lpszPath=m_StrCurPath;// "C:\\comdata"
SetCurrentDirectory(lpszPath);
//文件为Rec**.txt,一下的代码自动检测文件名是否存在,若存在,则后面序号自动增加
char buf[200];
for(int j=0;j<100;j++)//产生一个文件名
{
sprintf(buf,"Rec%02d.txt",j);
if((access(buf,0))==-1)
break;
}
//下面是创建模式 写的模式打开文件(当然,文件不存在的话就先创建)
if(!m_rFile.Open(buf,CFile::modeCreate|CFile::modeWrite))//我的天呀:这里写成这样的CFile::modeCreate||File::modeWrite
{
AfxMessageBox("创建文件记录文件失败");
return ;
}
if((access(buf,0))==-1)
{
AfxMessageBox("failed");
return ;
}
//在文件开始处写上保存日期
CTime t=CTime::GetCurrentTime();
CString str=t.Format("%Y年 %m月 %d日 %H时 %M 分 %S秒\r\n");
m_rFile.Write((LPCTSTR)str,str.GetLength());
//保存显示数据
m_rFile.Write((LPCTSTR)m_strReceiveData,m_strReceiveData.GetLength()); //向文件写数据
m_rFile.Flush();//将缓存的内容写入文件 缓冲区是4k的内容,够4k了,就会自动写入文件,不够的话就需要flush一下,如果不flush的话,//文件的最后有可能会少一些内容
m_rFile.Close();//关闭文件
str="OK";
for(int i=0;i<5;i++)
str+=buf[i];
str+=".txt saved";
m_ctrlSavePath.SetWindowText(str);
SetTimer(2,5000,NULL);//在定时器中显示保存文件状态
}
更改路径:为“更改”按钮IDC_BUTTON_DIRBROWSER添加单击响应函数OnButtonDirbrowser( )
void CSCOMMDlg::OnButtonDirbrowser()
{
static char displayname[MAX_PATH];
static char path[MAX_PATH];
LPITEMIDLIST pidlBrowse; //PIDL selected by user
BROWSEINFO bi;//BROWSEINFO结构中包含有用户选中目录的重要信息
bi.hwndOwner=this->m_hWnd;//获得窗口自己的句柄 浏览文件夹对话框的父窗体句柄。
bi.pidlRoot=NULL; //ITEMIDLIST结构的地址,包含浏览时的初始根目录,而且只有被指定的目录和其子目录才显示在浏览文件夹对话框中///。该成员变量可以是NULL,在此时桌面目录将被使用。
bi.pszDisplayName= displayname;//用来保存用户选中的目录字符串的内存地址。该缓冲区的大小缺省是定义的MAX_PATH常量宏。
bi.lpszTitle ="请选择保存接收数据的文件夹"; //该浏览文件夹对话框对话框的显示文本,用来提示该浏览文件夹对话框的功能、作用和目的。
bi.ulFlags=BIF_EDITBOX;//BIF_EDITBOX:浏览对话框中包含一个编辑框,在该编辑框中用户可以输入选中项的名字。
bi.lpfn=NULL;//lpfn:应用程序定义的浏览对话框回调函数的地址。当对话框中的事件发生时,该对话框将调用回调函数。该参数可用为NULL。
pidlBrowse=SHBrowseForFolder(&bi);
if(pidlBrowse!=NULL)
{
SHGetPathFromIDList(pidlBrowse,path);//功能是把项目标志符列表转换为文档系统路径
}
CString str=path;//得到路径
if(str.IsEmpty()) //没选择则返回
return ;
m_StrCurPath=str; //接收路径编辑框对应的变量
UpdateData(FALSE);
}
代码分析:上面代码主要涉及到路径的选择和变更
BROWSEINFO bi;//BROWSEINFO结构中包含有用户选中目录的重要信息
该结构如下:
typedef struct_browseinfo
{
HWND hwndOwner;
LPCITEMIDLIST pidlRoot;
LPSTR pszDisplayName;
LPCSTR lpszTitle;
UINT ulFlags;
BFFCALLBACK lpfn;
LPARAM lParam;
int iImage;
}BROWSEINFO,*PBROWSEINFO,*LPBROWSEINFO;
这一句
pidlBrowse=SHBrowseForFolder(&bi);就是显示下面这个目录选择对话框。通时,对用户返回用户选择的内容。
而上面的pidlBrowse是这样定义的LPITEMIDLIST pidlBrowse; //PIDL selected by user。
当单击确定时,会执行SHGetPathFromIDList(pidlBrowse,path);这句把pidlBrowse对应的目录复制到字符串path中保存,即功能是把项目标志符列表转换为文档系统路径:。单击确定,则跳过该语句。
实现小文件的发送
为“选择发送文件”IDC_BUTTON_FILEBROWSER添加单击响应函数OnButtonFilebrowser( ).代码如下:
//下面代码是实现文件的小发送
//选择要发送的文件
void CSCOMMDlg::OnButtonFilebrowser()
{
// TODO: Add your control notification handler code here
LPCTSTR lpszPath="c:\\comdata";
SetCurrentDirectory(lpszPath);
static char BASED_CODE szFilter[]="文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||";
CFileDialog FileDlg(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,szFilter)
//OFN_READONLY
Causes the Read Only check box to be selected initially when the dialog box is created. This flag indicates the state of the// Read Only check box when the dialog box is closed.
//OFN_OVERWRITEPROMPT
Causes the Save As dialog box to generate a message box if the selected file already exists. The user must c//onfirm whether to overwrite the file.
FileDlg.m_ofn.lpstrInitialDir=lpszPath;
if(FileDlg.DoModal()==IDOK)
{
CString strFileName =FileDlg.GetFileName();//得到完整的文件名,包括扩展名如:test1.txt
CString strFileExt =FileDlg.GetFileExt();//得到完整的文件扩展名,如:txt
CString lpstrName =FileDlg.GetPathName();//得到完整的文件名,包括目录名和扩展名如:c:\ test\ test1.txt
m_strSendFilePathName=lpstrName;
UpdateData(FALSE);
}
}
代码分析:
CFileDialog FileDlg(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,szFilter)
产生下面这样的一个对话框
选好文件后,就可以发送啦。“发送文件”按钮 IDC_BUTTON_SENDFILE添加单击响应函数OnButtonSendfile( )
//发送文件
void CSCOMMDlg::OnButtonSendfile()
{
// TODO: Add your control notification handler code here
CFile fp;
if(!(fp.Open((LPCSTR)m_strSendFilePathName,CFile::modeRead)))//文件以只读的方式打开
{
AfxMessageBox("Open file failed!");
return ;
}
fp.SeekToEnd(); //这里应该是设置光标到 文本的末尾,一遍统计字符文本字符数
unsigned long fplength=fp.GetLength();
m_nFileLength=fplength;
char* fpBuff;
fpBuff =new char[fplength];
fp.SeekToBegin();
if(fp.Read(fpBuff,fplength)<1)//读文件的数据到fpBuff中
{
fp.Close();//读文件失败 关闭文件
return ;
}
fp.Close();
CString strStatus;
if(m_Port.InitPort(this,m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits,m_dwCommEvents,fplength))
{
m_Port.StartMonitoring();
strStatus.Format("STATUS: COM%d OPENED, %d, %c, %d, %d",m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits);
m_ctrlIconOpenoff.SetIcon(m_hIconRed);
m_bSendFile=TRUE;
// m_strTempSendFilePathName=m_strSendFilePathName;
m_ctrlEditSendFile.SetWindowText("正在发送......");
//发送文件时,一下功能不能使用
m_ctrlManualSend.EnableWindow(FALSE);
m_ctrlAutoSend.EnableWindow(FALSE);
m_ctrlSendFile.EnableWindow(FALSE);
m_Port.WriteToPort((LPCSTR)fpBuff,fplength);
}
else
{
AfxMessageBox("Failed to send file!");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);
}
delete fpBuff;//释放内存,一定得记得
}
怎样才知道文件的内容发送完了呢???还用串口类CSerialPort中有WM_COMM_TXEMPTY_DETECTED消息来告诉我们
添加串口字符发送完毕消息WM_COMM_TXEMPTY_DETECTED,并添加响应函数
函数的声明:
//{{AFX_MSG(CSCOMMDlg)
afx_msg LONG OnFileSendingEnded(WPARAM wParam,LPARAM port);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
添加消息映射:
//消息映射
BEGIN_MESSAGE_MAP(CSCOMMDlg, CDialog)
//{{AFX_MSG_MAP(CSCOMMDlg)
ON_MESSAGE(WM_COMM_RXCHAR,OnCommunication)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
添加消息响应函数:
LONG CSCOMMDlg::OnFileSendingEnded(WPARAM wParam,LPARAM port)
{
if(m_bSendFile)
{
m_ctrlEditSendFile.SetWindowText("发送完毕!");//
TX_count+=m_nFileLength;
SetTimer(3,3000,NULL);
CString strTemp;
strTemp.Format("TX: %d ",TX_count);
m_ctrlTXCount.SetWindowText(strTemp);
}
return 0;
}
看到上面的函数中发送数据结束后启动
SetTimer(3,3000,NULL);以恢复状态。看来还要在定时函数中添加代码:
void CSCOMMDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
switch (nIDEvent)
{
case 1: //定时器ID==1为自动发送时间
OnButtonManualsend(); //周期到后自动发送
break;
case 2:
m_ctrlSavePath.SetWindowText(m_StrCurPath);//重新显示路径
KillTimer(2);//关闭定时器
break;
case 3:
m_ctrlManualSend.EnableWindow(TRUE);
m_ctrlAutoSend.EnableWindow(TRUE);
m_ctrlSendFile.EnableWindow(TRUE);
m_strSendFilePathName=m_strTempSendFilePathName;
KillTimer(3);
if(!(m_ctrlAutoSend.GetCheck()))
{
CString strStatus;
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 %d OPENED, %d, %c, %d, %d",m_nCom,m_nBaud,m_cParity,m_nDatabits,m_nStopbits);
m_ctrlIconOpenoff.SetIcon(m_hIconRed );
}
else
{
AfxMessageBox("Failed to reset send buffer size!");
m_ctrlIconOpenoff.SetIcon(m_hIconOff);
}
m_ctrlPortStatus.SetWindowText(strStatus);
}
break;
default:
break;
}
CDialog::OnTimer(nIDEvent);
}
至此,一个小小的简单的串口调试助手,完毕
下一篇给整个串口的代码。