终于搞定了!我想我只能这么说,这两天熬夜的成果啊。
自从去年校电子竞赛之后一直想要做些计算机控制的东西,和郑子含老师聊了聊,她推荐我去学习VB,但是我对C++情有独钟,因为学过C上手可能快。一直没有时间搞。这次比赛搞小车,想把上学期的想法实现一下。但是电脑控制小车必须要一个良好的人机交换界面。于是开学想要系统的学习下C++语言,看了半本郑阿奇的书结果发现内容竟然和C一模一样,关键的后半本介绍了类的概念,我粗略的看了。找了孙鑫老师的视频和书,了解了下MFC的编程方式,但是这个都和串口没什么关系,我知道MFC封装了WIN API,但是谁可以告诉我用哪个函数打开PC上的串口?
终于找了一本书叫做《Visual C++实践与提高——串口通信与工程应用篇》(中国铁道出版社,张筠莉 刘书智主编)。首先按着上面的程序做了个串口调试助手,理解了下,但是好用吗?不见得,单片机端的程序的设计和书上的不同(书上汇编,写了3页还不如我用C十几行就够了)加上频繁的启动PC定时器,导致老是检测延时,我测过只要按上7下,就会直接卡死。
做了一个星期左右时间的准备工作,做坏了几个程序,蓝屏很多次之后,前天晚上断网后开工,搞到凌晨四点钟,终于把雏形做出来了,昨天下午主要的目的是要把编辑框里的数据(人工设置小车速度)读到程序中再发到单片机上,但是书上并没有把编辑框的数据读到程序中的函数,找了很久找到
UpdateData(true);
GetDlgItemText(IDC_EDIT4,a);
用了一下不错,可以用。最难的就是发送了,因为我使用了很多按钮每次执行按钮都要有返回值确认单片机是否是正常收到,为了简便起见我PC发到单片机上的都是1个字节,而单片机发到PC上的都是2个字节。而发送小车的速度值是不能超过1个字节,为了不和之前确认数发生检测错误,所以我将编辑框的数据先加上11再在单片机里减去11。这个涉及到数据类型转换的问题,int char VARIANT CString CByteArray,我终于明白了,为什么数据类型也是一门课。。。
当时比较混乱问了很多学计算机的同学,但是没有得到答案,最后问了下潇,她给了我C++老师的qq,叫“曾哥”。我*,曾哥如此闪亮名字刺瞎了我的狗眼!于是速度加为好友,不过最后都没有拜托他们,潇今早给程序前我已经想好怎么解决了,这个方法避开了CString 和CByteArray的转换。
不废话了,现在就贴制作过程(使用工具Visual C++ 6.0,9针RS232串口线一条,P0口带上拉的LED的51最小系统版。)
打开软件,新建——>MFC AppWizard[exe]再填写工程名和路径——>基于对话框(不需要太好看,对话框足矣)——>确定。
最右边是可以添加的控件,但是我们可以看到没有MSCOMM控件,因为对于搞高级软件的程序员来说一般用不到的,所以串口不是默认控件,需要添加。
工程——>增加到工程——>Components and Controls会弹出一个叫做Components and Controls Gallery的对话框,打开Registered ActiveX Controls文件夹会看到很多可以添加的控件,找到Microsoft Communications Comtrol,version 6.0选项,单击Insert——>确定。跳出Confirm Classes对话框,这个不需要修改,直接点OK就好了,然后就可以看到控件已经加到空间框里了。
这个电话状的就是MSComm控件,你可以右击这个图标点属性修改ID,当然也可以用默认的。再把编辑框和按钮等等拖到界面上,生成的就是最后界面的样子,所以可以随意搞(下图是我做的界面)
再在对话框的头文件集中的×××Dlg.h(×××是你的文件名)的最开头添加mscomm头文件#include "mscomm.h"(如下图)
然后找到对话框 类的定义代码中加入CMSComm类变量声明
class CxxxDlg.h : public CDialog
{
...
protected:
CMSComm myComm;//(你也可以用其他的)
}
如下图
接着在xxxDlg.cpp中所有函数外面添加全局变量(注意是全局),当然也可以不添加。
因为我做的比较简单,所以对于串口的选择并没有设置,直接是com1,,9600bps,0(无校验),8(位),1(停止位)当然这个都在程序中写入。并在点开exe文件之后就设置好了。这个只需在初始化函数CxxxDlg :: OnInitDialog()中写入以下程序即可。
DWORD style=WS_VISIBLE|WS_CHILD;
if(!myComm.Create(NULL,style,CRect(0,0,0,0),this,IDC_MSCOMM1))
{
AfxMessageBox("创建MSComm控件失败!");
return -1;
}
else//创建成功则执行初始化及打开串口操作
{
myComm.SetCommPort(1);//指定串口号
myComm.SetSettings("9600,N,8,1");//通信参数设置
myComm.SetInBufferSize(1024);//指定接收缓冲区大小
myComm.SetInBufferCount(0);//清空接收缓冲区
myComm.SetInputMode(1);//设置数据获取方式
myComm.SetInputLen(0);//设置每次读取长度,0为全部读取
myComm.SetRThreshold(2);//设置接收OnComm事件字符门限值在收到2字节时候触发事件
myComm.SetPortOpen(1);//打开串口
}
这样就创建串口了,接下里就处理按键、显示和处理收发数据的事情了。
其做法和做普通的MFC一样就不赘述了,我介绍一下一些用到的MSComm控件的一些函数属性。
输入(串口接收)函数VARIANT GetInput();
例子:VARIANT int1;
int1 = myComm.GetInput();
即可接收来自串口的数据,但是注意变量,想要显示就要转化。
输出(串口发送)函数void SetOutput(const VARIANT&newValue);
例子:CSting aa;
aa="at";
myComm.SetOutput(COleVariant(aa));
即可将数据写入输出缓冲区,COleVariant(aa)是将CString 转换为VARIANT类型数据。
关于串口事件CommEvent的属性就像单片机中的中断一样。只要串口有事件发生,就会去响应,当然必须设置响应的条件和方式。下图是事件发生时返回到CommEvent的值
如果要添加处理方式就在xxxDlg.app中的void CxxxDlg :: OnOnCommMscomm1()
{
...
}
这个函数中添加。
最重要的是不要忘记使用完串口之后要关闭它。在界面按Ctrl+W弹出MFC ClassWizard类向导对话框
点击Object IDs列表中的xxxDlg再点Messages列表框中的WM_CLOSE单击Add Function单击Edit Code
就会跳到void CxxxDlg :: OnClose()函数在里面添加
if(myComm.GetPortOpen())
myComm.SetPortOpen(0);
CDialog::OnClose();
即可。
这样一个串口通信的基本流程就做完了。当然如果你不喜欢MFC这三个正方体组成的图标你可以改,最简单的方法是找一张图转成ico格式的,将工程文件夹里res文件夹下的默认ico换掉,只要名字改成一样即可。
看一下效果图。
按下【串口匹配】匹配串口
按下【收发模式】这是工作在小车自动跑的模式下,将当前的数据发动到上位机显示出来。每隔半秒收发一次。
按下【遥控模式】可以看到右边的编辑框内显示:启动遥控模式,并且按左边的按钮出现相对应的前进、左转、右转、停止(此时自动收发已经停止)。
【左转】左边有一盏亮了
【右转】右边有一盏亮了
【停止】全灭了,桌子有点乱,呵呵。看全图更乱,线奇多。。。— —!
修改“设置速度”编辑框里的数据,按下【发送】,看到上面显示“速度已修改!”,即将23发送到单片机上。
从图中可以看到。本来有8个LED灯,现在是“亮亮亮灭亮灭灭灭”因为是共阳极的,所以亮的是'0'灭的是'1'转化成二进制就是00010111可以用科学计算器算一下就是十进制23
后记:还得搞一下无线发送和接受。。。还有一个礼拜答辩。。明天FPGA实验课,老师这么看好我,还得搞一下FPGA。。。悲剧。。。