[小插曲] vs2013 MFC 串口上位机设计的学习

来源:互联网 发布:微信解封软件免费版 编辑:程序博客网 时间:2024/04/30 23:08

一 基础知识

1 首先,何为上位机?何为下位机?

上位机
上位机是指可以直接发出操控命令的计算机,
一般是PC/host computer/master computer/upper computer,主机、工控机、触摸屏
屏幕上显示各种信号变化(液压,水位,温度等)。
下位机
下位机是直接控制设备获取设备状况的计算机,
一般是PLC/单片机single chip microcomputer/slave computer/lower computer之类的。

2 其次,上下位机之间是如何工作的?

上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。下位机不时读取设备状态数据(一般为模拟量),转换成数字信号反馈给上位机。

3 工作原理:

两机如何通讯,一般取决于下位机,下位机一般具有更可靠的独有通讯协议;
使用一些新的API(API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力;
多语言支持功能模块,一般同时支持数种高级语言为上位机编程。
通常上位机和下位机通讯可以采用不同的通讯协议,有RS232的串口通讯或者RS485串行通讯。
当用计算机和PLC通讯的时候,不但可以采用传统的D形式的串行通讯,还可以采用更适合工业控制的双线的PROFIBUS-DP通讯。采用封装好的程序开发工具就可以实现PLC和上位机的通讯,当然可以自己编写驱动类的接口协议控制上位机和下位机的通讯。
上下位机都需要编程,都有专门的开发系统。
在概念上,控制者和提供服务者是上位机,被控制者和被服务者是下位机,也可以理解为主机和从机的关系,但上位机和下位机是可以转换的。

4 工具选择

首选的语言是C++,这样的话单片机和上位机之间有很多代码是可以共用的. 现有的GUI库有很多, 例如:
1)上位机开发比较经典的是MFC, 但是近年来微软并不重视MFC, 而且它并不是跨平台的GUI库, 库本身的设计(对比其他一些GUI库)也相对比较糟糕 <虽然我就用的是MFC>
2) Qt 是个不错的选择, 跨平台, 功能相当强大, 设计的也很漂亮. 但是它对标准的C/C++做了一点改动, 这就意味着Qt不是一个纯代码的GUI库, 要编译它的代码必须用Qt提供的编译工具.
3) GTK+中的GDKPIXBUF库是专门用来处理图像的,它支持多种图像格式和动画,如常见的TIFF、JPEG、PNG、GIF等图像格式
4)wxWidgets , 开源跨平台, 和 CodeBlocks (开源跨平台的IDE)配合的很好, 而且可以用 wxSmith 很方便的设计界面. 目前感觉不错. 学过MFC上手 wxWidgets 很快.

二 准备工作

1 安装vs

安装注册vs2013: http://jingyan.baidu.com/article/09ea3ede3b2496c0afde3944.html 这个百度经验里面给的vs2013,是有串口控件(Microsoft communication control)

2 先下载一个文件,下载地址:

http://download.csdn.net/download/rabbit200808/3236756
压缩包包含四个文件:MSCOMM.SRG、MSCOMM32.DEP、MSCOMM32.oca、mscomm32.ocx;
解压后拷贝到:
C:\Windows\System32(WIN7系统);
C:\WINDOWS\system32(XP系统)。
注意:64位win7系统还需要将mscomm32.ocx文件复制到C:\Windows\SysWOW64\目录下,否则后面注册会出错。

3 以管理员身份运行

在C:\Windows\System32里找到cmd.exe以管理员身份运行:
Regsvr32 C:\WINDOWS\system32\MSCOMM32.OCX
注册成功如图所示:
这里写图片描述

4 修改注册表

win+R组合键打开“运行”或者直接在开始菜单里找到“运行”;输入regedit后回车,打开注册表管理器,在其中找到HKEY_CLASSES_ROOT项下的Licenses项,添加主项命名为“4250E830-6AC2-11cf-8ADB-00AA00C00905”,并将键值修改为“kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun”

三 使用

1 新建MFC工程

在VS 2013中新建一个MFC项目,比如基于对话框的一个项目,可参考此教程——VS如何新建一个基于对话框的MFC工程

2 添加MSComm控件

VS2013取消了MSCOMM串口控件,如果还想用该控件写串口程序,就得自己添加写基于MFC的程序
在对话框上右击选择“打开ActiveX控件”,英文版“InsertActiveX Control”找到Microsoft Communications Control,version 6.0,此时在对话框中出现一个电话一下的按钮,这个就是MSComm控件(串口控件),软件运行时是看不见的 。

3 删除默认的按钮以及静态文本框,添加如下对话框控件:

A:打开串口控件:工具栏中拖一个botton 到 diadlg 上 ,并修改Caption 为 ”打开串口“,修改ID 为 IDC_BOTTON_OPEN

B: 关闭串口控件:工具栏中拖一个botton 到 diadlg 上 ,并修改Caption 为 ”关闭串口“,修改ID 为 IDC_BOTTON_CLOSE

C:发送控件,工具栏中拖一个botton 到 diadlg 上 ,并修改Caption 为 ”发送“,修改ID 为 IDC_BOTTON_SEND
D:发送编辑框 和 接受编辑框

参照百度经验http://jingyan.baidu.com/article/0f5fb09919363e6d8234ea7a.html

1)错误:”const char *” 类型的实参与 “LPCTSTR” 类型的形参不兼容

解决:http://jingyan.baidu.com/article/7c6fb428629c3a80652c907e.html

2)错误:error MSB8031: Building an MFC project for a non-Unicode character set is deprecated. You must change the project property to Unicode or download an additional library. See http://go.microsoft.com/fwlink/p/?LinkId=286820 for more information.

解决:出现这样的问题,微软的解释是:用于多字节字符编码 (MBCS) 的 MFC 库 (DLL) 不再包含于 Visual Studio 中,但是可用作插件,您可以在任何装有 Visual Studio Professional、Visual Studio Premium 或 Visual Studio Ultimate 的计算机上下载和安装。 参阅[此篇百度经验] http://jingyan.baidu.com/article/6181c3e06ab30f152ff1534d.html

4 切换工作窗口为解决方案资源管理器,打开MFCTestDlg.h头文件

在CMFCTestDlg类中添加代码:

int gllen;//定义整型标量gllen,用于记录接收数据的个数  CString strRXDdata; //编辑框显示的文本,记录历次转换值    

这里写图片描述

5往按钮的事件处理函数中添加代码

备注:以下的m_mscomm均替换了m_comm1

// TODO:  在此添加控件通知处理程序代码       CByteArray m_Array; //定义字节数组      m_Array.RemoveAll(); //字节数组清空      m_Array.SetSize(1); //设定维数为1      m_Array.SetAt(0,0x15); //给m_array[0]赋值发送的字节为0x15      m_comm1.put_Output(COleVariant(m_Array));//由于SetOutput函数的参数为VARIANT型,必须强制转换后才能发送  

这里写图片描述
往另一个按钮中添加代码:

<pre name="code" class="cpp">void CMFC_COMMNDlg::OnBnClickedserialstateswitch()  {      // TODO:  在此添加控件通知处理程序代码      if (!m_comm1.get_PortOpen())//判断串口是否已经打开      {          m_comm1.put_PortOpen(TRUE); //如果串口是关闭的,则打开串口          GetDlgItem(ID_serial_state_switch)->SetWindowText("关闭串口");      }      else      {          m_comm1.put_PortOpen(FALSE); //如果已经打开串口,则关闭串口          GetDlgItem(ID_serial_state_switch)->SetWindowText("打开串口");      }  }  

下面是往OnInitDialog中添加代码。。。

//**************添加的串口代码      gllen = 0; //记录转换次数全局变量清零      if (!m_comm1.get_PortOpen())//判断串口是否已经打开      {          m_comm1.put_CommPort(3); //选择串口号3          m_comm1.put_PortOpen(TRUE); //打开串口          m_comm1.put_RThreshold(2); //收到两个字节引发OnComm事件          m_comm1.put_InputMode(1);//输入模式选为二进制          m_comm1.put_Settings("9600,n,8,1"); //设置串口参数,波特率9600,无奇偶校验,1位停止位,8位数据位          MessageBox("串口初始化完毕", "提示"); //提示串口成功初始化      }      else  MessageBox("串口被占用", "提示"); //如果已经打开串口,消息框提醒      GetDlgItem(ID_serial_state_switch)->SetWindowText("关闭串口");//按钮显示状态改变      //*********************************添加的串口代码  

这里写图片描述
最后往串口响应函数OnCommMscomm1中添加代码

// TODO:  在此处添加消息处理程序代码  VARIANT variant1;//定义VARIANT型变量,用于存放接收到的数据  COleSafeArray safearray;//定义safearray型变量  long  k;//定义长整型变量len,k  long len;  BYTE rxdata[2048];//定义BYTE型数组  CString stremp1, stremp2;//定义两个字符串  if (m_comm1.get_CommEvent() == 2) //判断引起OnComm时间的原因,接收事件  {//如果是接收到特定个字节数,则读取接收到的数据      variant1 = m_comm1.get_Input();//把接收到的数据存放到VARIANT型变量里      safearray = variant1;//VARIANT型变量转换为ColeSafeArray型变量      len = safearray.GetOneDimSize();      for (k = 0; k<len; k++)      {          safearray.GetElement(&k, rxdata + k); //得到接接收到的数据放到BYTE型数组rxdata里      }      for (k = 0; k<len; k++)      {          BYTE bt = (*(unsigned char*)(rxdata + k)); //读取AD转换的高字节          if ((k % 2) == 0)          if ((k + 1)<len)          {              gllen++;//全局的变量,对接收到的转换结果的个数进行计算              stremp2.Format("第%d次转换结果:", gllen);//显示第几次转换              int temp = bt * 16 + ((*(unsigned char *)(rxdata + k + 1)) ); //高低字节合并成实际的转换结果,注意转换结果是左对齐              //AllocConsole();//注意检查返回值              //_cprintf("rxdata0=%d\n rxdata1=%d\n temp=%d\n", rxdata[0], rxdata[1],temp);              //_getch();              stremp1.Format("%2.2f", (2.56*temp / 1024));//计算成实际电压值              SetDlgItemText(IDC_STATIC, ("当前电压值为: " + stremp1 + " V")); //更新静态文本控件              strRXDdata += stremp2;//把新的数据放到全局的字符串里                strRXDdata += stremp1;              strRXDdata += " V\r\n";//字符串加单位V后换行          }      }  }  SetDlgItemText(IDC_EDIT2, strRXDdata);//更新文本控件的显示  

这里写图片描述

6 单片机测试代码

<pre name="code" class="cpp"><pre name="code" class="cpp">/********************************************************************************* * 【编写时间】: 2011.07.07 * 【作    者】: 雁翎电子 * 【版    本】: V1.0 * 【网    站】: http://ylelectronic.taobao.com/  * 【Q      Q】: 348439350 * 【声    明】: 此程序仅用于学习与参考,引用请注明版权和作者信息! * 【函数功能】: 串口中断程序实验——通过单片机向电脑发送数据,同时电脑将收到的数据发出                                                    **********************************************************************************/  /*预处理命令*/  #include <reg52.h>          //包含单片机寄存器的头文件  #define uchar unsigned char  #define uint unsigned int  uchar num,a;  /* ******************************************************************************** ** 函数名称 : main(void) ** 函数功能 : 主函数 ******************************************************************************** */  void main()  {      TMOD=0x20;         //用定时器设置串口波特率       9600       TH1=0xfd;      TL1=0xfd;      TR1=1;      REN=1;          //串口初始化      SM0=0;      SM1=1;      EA=1;           //开启总中断      ES=1;      while(1)      {          if(num==1)    //判断是否有串口数据的传送          {              ES=0;              num=0;              SBUF=a;          //发送数据a到SBUF,即将单片机的数据发送到计算机              while(!TI);              TI=0;              SBUF=0xff;  //发送数据0xff到上位机                  while(!TI);              TI=0;              ES=1;          }      }     }  void ser() interrupt 4  {      RI=0;      P2=SBUF;            //接收数据SBUF,即将计算机的数据接收。      a=SBUF;      num=1;  }  

7 安装好单片机的USB驱动,然后再设备管理器显示的是COM3,如果是其他的COM号码,那么你就修改

m_comm1.put_CommPort(3); //选择串口号3 

8 运行软件:

这里写图片描述

为什么是1.48呢,因为发送了0x15,然后 返回0x15和0xff 然后0x15=21 0xff=255 21乘以16加上255等于591,然后591*2.56/1024=1.4775

9 调试:声明#include <conio.h>

在定义temp的代码下面添加

AllocConsole();//注意检查返回值                  _cprintf("rxdata0=%d\n rxdata1=%d\n temp=%d\n", rxdata[0], rxdata[1],temp);                  _getch();  

就可以输出接收到的数据和temp的值了
如图
这里写图片描述
运行程序,点击开始转换,就可以有:
这里写图片描述

0 1