使用MFC来编写串口程序,需要有一定的c++语言功底,要清楚MFC代码的组织方式。

鉴于绝大多数的教程还停留在vc6.0这个骨灰级的环境,特在此说明一下VC2012下的代码组织方式,和大家一起交流下~

本文略去建立窗体的步骤,但是给出了窗体的样式,不会建立窗体的童鞋可以百度一下就知道了,很简单的,所以就不多说啦 ~

0、准备工作

使用的通讯控件是:Microsoft Communications Control, Version 6.0

需要安装同时VC6.0和VS2012才可以使用这个通讯控件。

工具->选择工具箱->COM组件 添加到工具条中,然后再添加到窗体上,任何位置都OK,编译运行以后不显示。

新建MFC窗体,win32下,基于对话框,命名为MFC(建议和我一样,这样方便些。)。应该都知道(不知道的可以参考百度文库里,好多,不多说了)

本代码是最简单的串口程序,参数设置都在代码中提醒。只需要设置COM号

窗体样式:

可以核对一下,头文件名:

  1. MFC.h
  2. MFCDlg.h // main frame 主要窗体、父窗体的头文件,主要的修改和添加代码区
  3. Resource.h
  4. stdafx.h
  5. targetver.h
  6. 源文件名:
  7. MFC.cpp
  8. MFCDlg.cpp // main frame 主要窗体、父窗体的cpp文件,主要的修改和添加代码区
  9. stdafx.cpp

一、准备工作

1.设置控件属性

各ID如下:

IDC_COMBO_CommSeclect 属性里面的Data:COM1;COM2;COM3;COM4;COM5;COM6; // 注意使用;分隔

2.使用  项目->类向导  定义变量如下

编译一下,应该不会有错。

下面准备添加代码

主要的代码区是 MFCDlg.h 和 MFCDlg.cpp 特别要注意,不要乱 ~

二、准备添加代码,接收下位机发来的数据并显示

1.MFCDlg.cpp中初始化

函数名:
BOOL CMFCDlg::OnInitDialog()

添加如下:

  1. // TODO: 在此添加额外的初始化代码
  2. m_ComboBox.SetCurSel(2);//打开软件时串口选择框默认显示COM1 子选项编号的排序是从0开始的。

2.MFCDlg.cpp中BUTTON1消息响应函数 // 打开串口

双击绘图窗口里的BUTTON1控件进入代码编辑区添加代码如下:(想省事,直接看下面的代码)

或者进入类向导(建议此方法,有利于体会MFC的代码组织方式)

添加代码如下:  //  接收下位机的数据  看懂了注释你就可以按照需要自由组织啦。当下只是最简单的,8位数据,1停止,9600,无校验

  1. void CMFCDlg::OnBnClickedButton1()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. //m_Index_int = ((CComboBox*)GetDlgItem(IDC_COMBO_CommSelect))->GetCurSel();//当前选中的
  5. /***********
  6. GetCurSel() 函数:用以得到用户选中下拉列表框中数据的索引值.返回的值是重0开始的,如果没有选择任何选项将会返回-1
  7. ************/
  8. //返回的是打开的端口号
  9. switch(m_ctrlComm.get_PortOpen())//点击打开或关闭串口按键时,根据当前的串口是否打开经行相应操作
  10. {
  11. case 0://当前串口是关闭的,则进行打开串口操作
  12. m_Index = ((CComboBox*)GetDlgItem(IDC_COMBO_CommSeclect))->GetCurSel();//当前选中的行
  13. m_ctrlComm.put_CommPort(m_Index + 1);//如果要打开串口则应先选择哪个串口
  14. m_ctrlComm.put_PortOpen(TRUE);//打开串口
  15. UpdateData(FALSE);//更新按键状态
  16. if(m_ctrlComm.get_PortOpen())//如过已经打开串口,
  17. {
  18. SetDlgItemText(IDC_BUTTON1,_T("关闭串口"));//更改按键提示
  19. m_ctrlComm.put_Settings(_T("9600,n,8,1"));//打开软件时端口设置默认波特率9600,无校验位,8位数据,1位停止
  20. m_ctrlComm.put_InputMode(1);//1:表示以二进制方式捡取数据;
  21. //0:表示以文本方式捡取数据
  22. m_ctrlComm.put_RThreshold(1);//参数1 表示每当串口接收缓冲区中有多余或等于一个字符时将引发一个接收数据的OnComm事件
  23. //参数0 表示数据传输事件不会引发OnComm事件,即不响应。
  24. m_ctrlComm.put_InputLen(0);//0: 缺省值。表示使MSComm控件读取接收缓冲区中的所有内容。
  25. m_ctrlComm.get_Input();//先预读缓冲区以清除残留数据
  26. UpdateData(FALSE);
  27. }
  28. else
  29. AfxMessageBox(_T("串口打开失败"));
  30. break;
  31. case 1:
  32. //当前串口是打开的则进行关串口操作
  33. // m_ctrlComm.SetCommPort(m_Index_int + 1);//如果要打开串口则应先选择哪个串口
  34. m_ctrlComm.put_PortOpen(FALSE);
  35. if(!m_ctrlComm.get_PortOpen())//如果已经关闭串口,
  36. {
  37. SetDlgItemText(IDC_BUTTON1,_T("打开串口"));
  38. UpdateData(FALSE);
  39. }
  40. else
  41. AfxMessageBox(_T("串口关闭失败"));
  42. break;
  43. default:
  44. AfxMessageBox(_T("cannot open Serial Port"));
  45. break;
  46. }
  47. m_ComboBox.SetCurSel(m_Index);//打开软件时串口选择框默认显示COM1 子选项编号的排序是从0开始的。
  48. // m_BaudRate_M.SetCurSel(m_BaudRate);//打开软件时波特率选择框默认显示9600
  49. // m_Data_Select_M.SetCurSel(m_Data_Select);//打开软件时数据位选择框默认显示8
  50. // m_StopBit_M.SetCurSel(m_StopBit);//打开软件时停止位选择框默认显示N 无停止位
  51. // m_ParityCheck_M.SetCurSel(m_ParityCheck);// 奇偶校验
  52. }

3.MFCDlg.cpp中Oncomm事件响应(就是那个“电话”控件的事件响应)

注意,此刻的头文件和源文件的内容有一些变化了,可以看一下,这两个是IDC_MSCOMM1控件带来的(添加OnComm事件后才会出现)。

添加代码如下:

  1. void CMFCDlg::OnOncommMscomm1()
  2. {
  3. // TODO: 在此处添加消息处理程序代码
  4. VARIANT variant_inp;
  5. COleSafeArray safearry_inp;
  6. LONG len,k;
  7. BYTE rxdata[2048];
  8. CString strtemp;
  9. int order;
  10. if(m_ctrlComm.get_CommEvent() == 2)//事件值为2表示接收缓冲区内有数据
  11. {
  12. //以下根据自己的通讯协议添加处理代码
  13. variant_inp = m_ctrlComm.get_Input();//读缓冲区
  14. safearry_inp = variant_inp;//VARIANT转化为COleSafeArray
  15. len = safearry_inp.GetOneDimSize();//字符长度
  16. for(k=0;k<len;k++)
  17. {
  18. safearry_inp.GetElement(&k,rxdata+k);//转化为BYTE型数组
  19. }
  20. for(k=0;k<len;k++)//将数组转化成Cstring型变量
  21. {
  22. BYTE bt = *(char*)(rxdata+k);
  23. //if(m_ctrlHexSend.GetCheck())
  24. // strtemp.Format("%02x",bt);
  25. //else
  26. strtemp.Format(_T("%c"),bt);//将字符送入临时变量strtemp中存放
  27. m_strRXData+=strtemp;//加入接收编辑框对应字符串
  28. /*******************
  29. 以上的语句可以进行对sbuf的读取。
  30. ***********************/
  31. order = _ttoi(strtemp);//order是字符转化后的int值
  32. }
  33. UpdateData(FALSE);//更新编辑框内容(主要是接收编辑框中的)
  34. }
  35. m_ComboBox.SetCurSel(m_Index);//打开软件时串口选择框默认显示COM1 子选项编号的排序是从0开始的。
  36. // m_BaudRate_M.SetCurSel(m_BaudRate);//打开软件时波特率选择框默认显示9600
  37. // m_Data_Select_M.SetCurSel(m_Data_Select);//打开软件时数据位选择框默认显示8
  38. // m_StopBit_M.SetCurSel(m_StopBit);//打开软件时停止位选择框默认显示N 无停止位
  39. // m_ParityCheck_M.SetCurSel(m_ParityCheck);// 奇偶校验
  40. }

编译运行,Bingo!

三、准备添加代码,向下位机发送数据

1.MFCDlg.cpp中 IDC_SendBtn 消息响应函数 // 打开串口

  1. void CMFCDlg::OnBnClickedSendbtn()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. UpdateData(TRUE);
  5. long len;
  6. CByteArray array;
  7. len = m_strTXData.GetLength();//发送数据的长度
  8. array.RemoveAll();
  9. array.SetSize(len);
  10. for(int i=0;i<len;i++)
  11. array.SetAt(i, m_strTXData[i]);
  12. m_ctrlComm.put_Output(COleVariant(array)); // 发送数据
  13. }

然后,就没有然后啦~

四、最后说明下一些tips

1.VC2012 和 VC6.0的一些函数名的变化(坑死人)

比如:在VC6.0下
  1. <span style="white-space:pre"> </span>m_ComPort.SetPortOpen(FALSE);
  2. m_ComPort.SetCommPort(m_comn+1); //设置串口号
  3. m_ComPort.SetInBufferSize(1024); //接收缓冲区
  4. m_ComPort.SetOutBufferSize(1024);//发送缓冲区
  5. m_ComPort.SetInputLen(0);//设置当前接收区数据长度为0,表示全部读取
  6. m_ComPort.SetInputMode(1);//以二进制方式读写数据
  7. m_ComPort.SetRThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnCommMscomm事件

在VC2012下,函数名需要做一下改动:

  1. m_ComPort.put_PortOpen(FALSE);
  2. m_ComPort.put_CommPort(m_comn+1); //设置串口号
  3. m_ComPort.put_InBufferSize(1024); //接收缓冲区
  4. m_ComPort.put_OutBufferSize(1024);//发送缓冲区
  5. m_ComPort.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取
  6. m_ComPort.put_InputMode(1);//以二进制方式读写数据
  7. m_ComPort.put_RThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnCommMscomm事件

还有GetPortOpen()  要改成 get_PortOpen()

以此类推。

2.本方法必须安装VC6.0环境,不然这个控件就不能使用了。

3.分享是这个时代的基本品质 ~ 祝大家成功 ~

五、功能补充,使用十六进制发送和接收(modbus通讯协议)

modbus的串口通讯的发送方式是使用16进制数,和上文的字符发送方式不同,但是本质上都是使用2进制传送。
其最大差别在于比如要传送(01),字符方式:0->48(ASCII值),0x30(ASCII值的十六进制形式)->0010 0000 第一次发送;
1->49(ASCII值),0x31(ASCII值的十六进制形式)->0010 0001第二次发送。结束
16进制方式: 0x01->1(10进制),对应的ASCII字符(SOH(start of headling))->0000 0001 一次发送结束。
注意,这里标出来的不是说要经过这样的转化以后才能发送,本质上串口发出去的都是二进制。差别在于你要选择发什么数据给put_Output()。不理解的话,可以实践一下加深理解。
TOOL 1: 串口调试器,可以监控,不占COM。http://pan.baidu.com/s/1qXTZudm
TOOL 2: 虚拟串口 http://pan.baidu.com/s/1nuUfzT3
参考资料:(串口程序部分)

http://wenku.baidu.com/view/9e9b258c6c175f0e7cd137b6.html?from=search

1.16进制方式发送

添加两个函数(第一个)

函数名在.h文件中声明Public:int String2Hex(CString str,CByteArray &senddata);

函数体如下:

  1. int CSerialModbus331Dlg::String2Hex(CString str,CByteArray &senddata)
  2. {
  3. int hexdata,lowhexdata;
  4. int hexdatalen = 0;
  5. int len = str.GetLength();
  6. senddata.SetSize(len/2);
  7. for(int i = 0;i<len;)
  8. {
  9. char lstr;
  10. char hstr = str[i];
  11. if(hstr == ' ')
  12. {
  13. i++;
  14. continue;
  15. }
  16. i++;
  17. if(i>len)
  18. break;
  19. lstr = str[i];
  20. hexdata = ConvertHexChar(hstr);
  21. lowhexdata = ConvertHexChar(lstr);
  22. if((hexdata == 16)||(lowhexdata == 16))
  23. break;
  24. else
  25. hexdata = hexdata*16 + lowhexdata;
  26. i++;
  27. senddata[hexdatalen] = (char)hexdata;
  28. hexdatalen++;
  29. }
  30. senddata.SetSize(hexdatalen);
  31. return hexdatalen;
  32. }

函数(第二个)

函数名在.h文件中声明Public:char ConvertHexChar(char ch);

函数体如下:

  1. char CSerialModbus331Dlg::ConvertHexChar(char ch)
  2. {
  3. if((ch>='0')&&(ch<='9'))
  4. return ch-0x30;
  5. else if((ch>='A')&&(ch<='F'))
  6. return ch-'A'+10;
  7. else if((ch>='a')&&(ch<='f'))
  8. return ch-'a'+10;
  9. else return (-1);
  10. }

最后修改发送函数(使用按钮click事件)

  1. void CSerialModbus331Dlg::OnBnClickedButtonSendhex()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. UpdateData(TRUE); //读取编辑框内容
  5. CByteArray hexdata;
  6. int len = String2Hex(m_strTXData,hexdata); //此处返回的len可以用于计算发送了多少个十六进制数
  7. m_ctrlComm.put_Output(COleVariant(hexdata)); //发送十六进制数据
  8. }

注意一个问题

网络复制的代码常出现    error C3872: "0xa0": 此字符不允许在标识符中使用

参考:http://blog.csdn.net/qqyuanhao163/article/details/40785983

原因是存在中文的空格,解决方法是把提示区的空格全部替换成英文空格。

2.16进制方式显示

在OnComm事件中修改一句代码如下:

  1. // strtemp.Format(_T("%c"),bt);//将字符送入临时变量strtemp中存放
  2. strtemp.Format(_T("%02X"),bt);//将字符送入临时变量strtemp中存放

VC2012/VS2012 MFC串口通讯上位机程序教程笔记(转)相关推荐

  1. matlab制作以太网数据接收上位机_Python制作串口通讯上位机

    串口通讯具有简单易用的特点广泛应用于测试设备的通讯和数据传递.单片机与计算机的通讯等,本案例基于Python语言制作一个用于接收燃油质量流量计的串口通讯上位机,实现数据的读取和保存. 1. 相关知识点 ...

  2. python 串口_如何使用Python开发串口通讯上位机(一)

    用Python开发串口通讯型上位机,其实并非最优解,本系列更新只为个人学习与总结.如果有C语言底子,嵌入式层面的上位机开发,C++ Builder,C#才是更加好用的利器. 1 什么是上位机 从事过嵌 ...

  3. python编程与下位机通讯_如何使用Python开发串口通讯上位机(一)

    用Python开发串口通讯型上位机,其实并非最优解,本系列更新只为个人学习与总结.如果有C语言底子,嵌入式层面的上位机开发,C++ Builder,C#才是更加好用的利器.1 什么是上位机 从事过嵌入 ...

  4. stm32f103上位机程序编写笔记

    CSDN话题挑战赛第2期 参赛话题:学习笔记 学习之路,长路漫漫,写学习笔记的过程就是把知识讲给自己听的过程.这个过程中,我们去记录思考的过程,便于日后复习,梳理自己的思路.学习之乐,独乐乐,不如众乐 ...

  5. python 串口上位机_如何使用Python开发串口通讯上位机(二)

    黑色的dos窗口对于大部分来说,页面极为不友好,且操作不方便,因此本篇主要讲讲如何结合QtDesigner创建一个UI并初步与串口Api链接.1 QtDesigner进行上位机页面设计 Python下 ...

  6. python做上位机好不_如何使用Python开发串口通讯上位机(完结)

    2021的第一份更新,比以往时候来得早一点! 前面两期讲了如何使用pyserial和做好了基本的上位机界面以及与API的联动,今天我们接着完善下代码.1 编码规范的使用 前面两期只是做基础展示用,所以 ...

  7. MFC串口通信上位机(采用静态库编译生成的)不能在其他电脑运行的问题

    导致这个问题的原因是因为其他电脑系统没有加载mscomm的控件,所以可以通过手动加载的办法来解决 1.建一个.bat文件 ,输入如下内容 @echo off copy .\MSCOMM32.OCX % ...

  8. C#写的控制三菱FX5U PLC(三菱任何系列都通用,网口,串口都行)的上位机程序

    自己用C#写的控制三菱FX5U PLC(三菱任何系列都通用,网口,串口都行)的上位机程序,PLC源程序也附上,是学习C#和三菱PLC通信的好例子,有对辅助继电器M,对单字,双子D的读写,IO的监控,报 ...

  9. FX5U项目程序,用的三菱FX5U的PLC,里面有CC- LINK通讯机器人.自由口通讯仪表.以太网套接字通讯上位机

    FX5U项目程序,用的三菱FX5U的PLC,里面有CC- LINK通讯机器人.自由口通讯仪表.以太网套接字通讯上位机,脉冲控制3个步进,还有配方. PLC程序有完整的注释,结构清晰,PROFACE的触 ...

最新文章

  1. 「完结」你对深度学习模型的理解是否足够深刻,这12篇文章了解下
  2. C++中友元函数和友元类
  3. android开发学习 ------- 弹出框
  4. SQL Server 堆heap 非聚集索引 Nonclustered index 行号键查找RID loopup结合执行计划过程详解
  5. struts2配置中Action的name 和package的name和namespace作用
  6. linux动态库查找路径以及依赖关系梳理
  7. [实验手册]MPLS/×××分解:防止PE-CE的路由环路
  8. 安装Sql server 2008遇到无法安装.net 3.5的问题解决办法
  9. VS2019的动态库dll在lua中调用及lua.exe和luac.exe的使用方法
  10. 3dmax中slice plane切割平面的作用
  11. python如何拼读英语单词怎么写_浅谈如何拼读英语新单词
  12. 单独使用Quartz 2.1.7 时Job属性Spring无法注入
  13. hdu-2072 单词数(“水”解法)
  14. CSS通用样式2——Flex布局
  15. 音视频开发系列--H264编解码总结
  16. 六、总线(一)总线概述
  17. 放大电路中的反馈(电压串联负反馈,电流串联负反馈,电压并联负反馈,电压并联负反馈)
  18. Oracle创建表空间 管理表空间
  19. 谷歌宣布退出中国内地 网民何去何从?
  20. 串讲数字电路与系统设计提前预告

热门文章

  1. 2021年一级建造师全科视频课件下载方法!
  2. ubuntu16.04系统下谷歌浏览器不能拖入下载好的扩展程序离线包
  3. 在群晖上使用小米或者360随身WiFi
  4. 《操作系统真象还原》第二章 ---- 编写MBR主引导记录 初尝编写的快乐
  5. 正点原子Stm32学习资料
  6. kotlin sealed 中_Kotlin的密封(Sealed)类:超强的枚举(KAD 28)
  7. Java后端返回多级分类的树形结构
  8. navicate导入sql时出现错误error:1300 - Invalid utf8 character string: 'D7D4D4'
  9. AutoCAD2021 下载及安装教程
  10. 虚拟机交叉编译openCV详细步骤及bug解决详解