注(部分图片没有显示,请下载附件)
VC上位机For51
济南职业学院 电子工程系 朱志强
2010年8月17日
概述:此文章作为学习上位机的复习资料和总结性资料。是《VC++MSComm串口接收程序制做》的后继制作,在前文的基础上添加了部分新的功能,可以实现接收十六进制单个字符。但是还是没有完成上位机对下位机的发送。对于图片的应用和静态文本的更换,这此也没有解决。
第一步制作上位机程序,参考《VC++MSComm串口接收程序制做》。步骤如下:新建一个基于对话框的程序,全部选择默认选项。得到如下的对话框。
图1 新创建的对话框
我们去掉上面的所有的控件,向工程中添加入MSCOMM控件,添加变量为m_ctrlComm。再添加一个编辑框,作为接收框,使用类向导添加变量为m_strRXData。同时,我们也给对话框添加上最小化按钮(方法:右键对话框,属性,样式里勾选上最小化按钮)。双击MSCOMM控件,添加入如下的代码:
void CFor51Dlg::OnOnCommMscomm1()
{
// TODO: Add your control notification handler code here
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
CString strtemp;
if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符
{
////////以下你可以根据自己的通信协议加入处理代码
variant_inp=m_ctrlComm.GetInput(); //读缓冲区
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("%02X",bt); //将字符送入临时变量strtemp存放
strtemp=" 按下的按钮编号是 "+strtemp+"\r\n";
m_strRXData+=strtemp; //加入接收编辑框对应字符串
}
}
UpdateData(FALSE); //更新编辑框内容
}
编译可以通过。下面我们就要为初始化串口做工作了。以前的程序,初始化的串口是死的,无法变化,我们在这里添加上一些其他控件来实现对串口的选择。在对话框上添加一个组框,在组框中添加5个单选按钮,标题为串口1~串口5。在右边添加上两个按钮,分别为打开串口,关闭串口。添加后如下图所示:
图2 接收部分控件图
我们给单选串口1添加属性“组”。这样我们编译一下,看看是否实现了单选的功能。打开类向导,给IDC_RADIO1添加成员变量int型m_portnum,用来记录我们选择的串口号是多少。使用类向导,找到CFor51Dlg,给IDC_RADIO1添加单击响应函数。代码如下:
void CFor51Dlg::OnRadio1()
{
// TODO: Add your control notification handler code here
m_portnum=1//标记为串口1
}
其余的四个代码类似。只是给变量m_portnum一个对应串口号的值即可。
下面我们给“打开串口”按钮添加响应函数。思路是,首先判断是否选择的串口,然后,在引入串口号,初始化串口,最后打开串口。我们先来查看一下m_portnum的初值。如果你找不到的话可以在初始化对话框函数(BOOL CFor51Dlg::OnInitDialog())里给m_portnum赋一个0值。
CFor51Dlg::CFor51Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CFor51Dlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CFor51Dlg)
m_strRXData = _T("");
m_portnum = -1;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
看到没?在上面地方中我们看到了系统初始化的值是-1。我们双击按钮,就可以添加按钮单击响应函数了。代码如下:
void CFor51Dlg::OnButton1()
{
// TODO: Add your control notification handler code here
if(m_portnum==(-1))
AfxMessageBox("请选择端口号!");
else
{
m_ctrlComm.SetCommPort(m_portnum); //
if( !m_ctrlComm.GetPortOpen())
{
m_ctrlComm.SetPortOpen(TRUE);//打开串口
AfxMessageBox("串口"+CString(m_portnum+48)+"已经打开!");
//由ASCII码表得到增加值为48
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE); //打开按钮无效
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); //关闭按钮有效
}
else
AfxMessageBox("cannot open serial port");
m_ctrlComm.SetSettings("9600,n,8,1");
//波特率9600,无校验,8个数据位,1个停止位
//m_ctrlComm.SetInputModel(1); //1:表示以二进制方式检取数据
m_ctrlComm.SetRThreshold(1);
//参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件
m_ctrlComm.SetInputLen(0); //设置当前接收区数据长度为0
m_ctrlComm.GetInput();//先预读缓冲区以清除残留数据
}
}
注意这句AfxMessageBox("串口"+CString(m_portnum+48)+"已经打开!");
输出的是字符串,我们查看了ASCII码表之后,找到了数字1和字符1的对应关系,加上48之后强制转换为字符格式输出。还有要注意的一点是数据的格式那句我们给注释掉了。我们
下面我们又想到了一个问题,那就是我们在刚开始的时候,没有打开串口,那么按理说关闭串口按钮是不能用的。那么我们就要让它不能用,初始化后禁止它!我们在对话框初始化函数中添加上GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE); //关闭按钮无效
就可以了。实现的效果如下所示。
图3 禁止关闭按钮和串口打开提示
我们此时已经看出,打开串口按钮的函数中,就有了使能关闭串口按钮,并禁止自身的代码了。我们只需给关闭串口按钮添加如下的代码:
void CFor51Dlg::OnButton2()
{
// TODO: Add your control notification handler code here
m_ctrlComm.SetPortOpen(FALSE);//关闭串口
GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE); //关闭按钮无效
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE); //打开按钮无效
//m_portnum=-1;
if( !m_ctrlComm.GetPortOpen())
AfxMessageBox("串口已经关闭");
}
我们就可以实现手动打开和关闭串口了。但是我在随后的测试时,出现了下面的错误,提示说串口已经被打开了,而实际上我并没有打开串口。我们的这个程序应该首先判断一下串口是否打开了,如果是打开了,我们提前关闭它。把这段程序放哪比较好呢?当然是对话框初识话的时候最好了啊。我们在对话框初识话函数中添加如下的代码:
if(m_ctrlComm.GetPortOpen()) //如果串口打开,则关闭
m_ctrlComm.SetPortOpen(FALSE);
图4 打开串口时出现的错误
添加上这些代码之后,我们在重新编译一下。最后使用下位机发送字符测试得到了下面的结果:
图5 第一次运行时出现的效果
我们发现了俩个BUG,首先是我们的数据没有换行。我们看看程序,找到了有关于这部分的代码如下:
图6 检查输入字符
我们看到我们在程序中添加上了“\r\n”,所以错误不在这。哦,是我们没有设置编辑框的属性。现在我们勾上“多行”和“垂直滚动”、“自动垂直滚动”就可以了。
(注:\r 是回车换行 \n 为另起一行输出)
还有一个BUG是我们使用的明明是串口4,可是在串口接收完数据之后,却变成了串口5。是我们通信的时候影响到了单选的标志m_portnum。所以我们直接去掉这个‘善变的’单选变量。打开类向导,删除IDC_RADIO1的成员变量m_portnum。编译后有一个错误,说是没有定义m_portnum,因为我们本来是在对户框界面使用类向导添加的变量,这个变量系统会给他自动做初始化处理。那么我们就在头文件(上位机For51Dlg.h)中定义一个int型的变量m_portnum。然后在对话框初始化函数中添加初始化代码m_portnum=0; 同时我们也要修改相应的打开串口判断函数。
图7 在头文件中定义变量m_portnum
图8 在对话框初始化函数中初始化m_portnum
图9 重新判断m_portnum
这样处理之后,我们在编译运行,测试一次就没有了上面的两个错误了。
下面我们在给对话框添加一个按钮,标题为清空接收区。添加如下的响应代码。
void CFor51Dlg::OnButton3()
{
// TODO: Add your control notification handler code here
CString qingkong;
m_strRXData=qingkong; //清空编辑框
GetDlgItem(IDC_EDIT1)->SetWindowText("清空完毕!"); //显示清空完毕
}
我们是创建了一个新的CString类的变量,初始化的值为空,我们把它赋值给m_strRXData,使得m_strRXData也变为空。效果如下:
图10 显示清空的效果
现在我们在添加上发送按钮和发送编辑框。给IDC_EDIT2添加变量m_data。给发送按钮添加响应函数如下:
void CFor51Dlg::OnButton4()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);//读取编辑框的内容
if(m_data.IsEmpty())
{
AfxMessageBox("发送不能为空!!!");
}
m_ctrlComm.SetOutput(COleVariant(m_data));
}
这个上位机就开发出来了,显示的效果如下:
图11 最后的测试
串口上接了一个51单片机开发板,但是没有上电,这样,就实现了自己发送,自己接受的目的,图中发送的是字符1,显示区显示的是字符1的十六进制的ASCII码。所以要在下位机的程序中添加一段转换码。将字符转换成十六进制的数据。这样就实现了单个数据的发送和接收了。
用户445904 2013-8-21 09:23
用户430277 2012-10-26 12:56
512826028 2011-11-21 18:35
用户359227 2011-7-17 22:39
用户359227 2011-7-17 22:36