此文章适合VC++串口通信入门

一、页面布局及添加控件

1, 安装好vs2010如图

2, 新建一个基于VC++的MFC项目comm

注意:点击ok,然后next,这时候要将application type改成dialog base,接着next到最后一个对话框是将generated dasses改成CcommDlg,然后finish

4, 将新生成的项目的对话框默认dialog edit删去,如图

5,在对话框中添加两个static text,两个edit text,两个按钮,

成品图如下

6,添加comm控件

1)在解决方案窗口右击新建的解决方案,点击add->class

2)选择MFC class from activex control如图

点击add,available activex controls选择microsoft communication controls versions 6.0,然后点确定就行

这时候对话框会出现一个电话图标,可能有一半白边去不了,这时候右击电话图标点击edit control就可以去掉了。

7,同时定义各个控件的类型、ID及相关属性

注:此项目只添加了发送和退出程序按钮

这时候得到了完整的串口通信对话框:

8, 添加成员变量,右击对话框,点击class wizard,点击member variables标签,选中需要添加的id,双击即可添加

依次为下表中的ID添加变量

期间,IDC_MSCOMM1控件在标签中没有,则在生成的对话框中右击comm控件点击add variables即可

9, 为mscomm,两个button添加响应事件,切换到class wizard的virtual function双击控件ID,添加响应事件,默认即可,也可改为自己想要的标题

为comm控件添加响应事件可能双击不了(我就是遇到这种问题),这时候只要右击comm控件图标,点击add event handler即可。如图

这时候基本界面已经布置好了,开始添加代码了。

二、代码添加

1、找到解决方案(solution explorer)的sources files点开,双击其中的mscommDlg.cpp我们的所有代码将添加到这个源文件中

2、进行串口初始化及其他串口设置

将以下代码添加到oninitialdialog函数

m_ctrlcomm.put_CommPort(3);//选择com3口
m_ctrlcomm.put_InputMode(1);//输入方式为二进制方式
m_ctrlcomm.put_InBufferSize(1024);//输入缓冲区大小为1024byte
m_ctrlcomm.put_OutBufferSize(512);//输出缓冲区大小为512byte
m_ctrlcomm.put_Settings(_T("9600,n,8,1"));//设置串口参数:9600波特率,无奇偶校验,8个数据位,1个停止位
if(!m_ctrlcomm.get_PortOpen())
    m_ctrlcomm.put_PortOpen(1);//打开串口
m_ctrlcomm.put_RThreshold(1);//每当串口接收缓冲区有多余或等于1个字符时将引发一个接收数据的oncomm事件
m_ctrlcomm.put_InputLen(0);//设置当前接收区数据长度为0
m_ctrlcomm.get_Input();//预读缓冲区以清空残留数据

m_ctrlcomm.put_CommPort(3);//选择com3口
m_ctrlcomm.put_InputMode(1);//输入方式为二进制方式
m_ctrlcomm.put_InBufferSize(1024);//输入缓冲区大小为1024byte
m_ctrlcomm.put_OutBufferSize(512);//输出缓冲区大小为512byte
m_ctrlcomm.put_Settings(_T("9600,n,8,1"));//设置串口参数:9600波特率,无奇偶校验,8个数据位,1个停止位
if(!m_ctrlcomm.get_PortOpen())
    m_ctrlcomm.put_PortOpen(1);//打开串口
m_ctrlcomm.put_RThreshold(1);//每当串口接收缓冲区有多余或等于1个字符时将引发一个接收数据的oncomm事件
m_ctrlcomm.put_InputLen(0);//设置当前接收区数据长度为0
m_ctrlcomm.get_Input();//预读缓冲区以清空残留数据
2、实现发送按钮,退出按钮相应的响应函数

void CmscommDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
CDialogEx::OnOK();
UpdateData(1);//读取编辑框内容
m_ctrlcomm.put_Output(COleVariant(m_strsend));//发送数据
}

void CmscommDlg::OnBnClickedExit()
{
// TODO: Add your control notification handler code here
m_ctrlcomm.put_PortOpen(0);//关闭串口
CDialog::OnCancel();//退出程序
}
3、实现MSComm控件相应的响应函数OnOnCommMsComm1()

VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048];//设置byte数组
CString strtemp;
if(m_ctrlcomm.get_CommEvent()==2)//事件2表示接受缓冲区有字符
{
variant_inp=m_ctrlcomm.get_Input();//读缓冲区
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((char) bt);//将字符送入临时变量strtemp存放
m_strreceive+=strtemp;//加入接收编辑框相应字符串
}
}
UpdateData(0);//更新编辑框内容

4、编译运行程序

在调试运行时,必须两台机子同时运行此程序,并且都要开启同一个串口号

VS2010下MFC的串口编程

串口通信简介

  一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与PC进行数据传输和通信的通道,在CPU和外设之间充当解释器的角色。当字符数据从CPU发送给外设时,这些字符数据将被转换成串行比特流数据;当接收数据时,比特流数据被转换为字符数据传递给CPU,再进一步说,在操作系统方面,Windows用通信驱动程序(COMM.DRV)调用API函数发送和接收数据;当用通信控件或声明调用API函数时,它们由COMM.DRV解释并传递给设备驱动程序。作为一个程序员,要编写通信程序,只需知道通信控件提供的Windows API通信函数的接口即可,换句话说,只需设定和监视通信控件的属性和事件即可。

  串口通信方法一般有以下几种:

  1. 利用Windows API通信函数;
  2. 利用Visual C++的标准通信函数_inp、_inpw、_inpd、_outp、_outpw、_outpd等直接对串口进行操作;
  3. 通过微软的串口通信控件MSComm,它是一种ActiveX控件;
  4. 利用第3方编写的通信类,比如MuMega Technologies公司提供的CSerail类;

  我在项目开发过程中用的是第三种方法——通过MSComm控件操作串口,下面是我使用此控件的笔记。

MSComm控件简介

  MSComm 是 Microsoft 公司为简化Windows下串行端口编程而提供的ActiveX控件,它提供了一系列标准通讯命令的使用接口。MSComm 控件通过串行端口(serial port)传送和接收数据,为应用程序提供了串行通讯功能。在可视化编程盛行的今天,我们可以很方便的在Visual Basic(VB)、Visual C++(VC)、Delphi等语言及开发平台中应用。处理数据的方式有事件驱动(Event-driver)、查询法(Inquire)两种。

  事件驱动法:在使用事件驱动法设计程序时,每当有新字符到达、端口状态变化或发生错误时,MSComm控件将触发OnComm事件,而应用程序在捕获该事件后,通过检查MSComm控件的CommEvent属性可以获知所发生的事件或错误,从而采取相应的操作。这种方法的优点是程序响应及时,可靠性高。

  查询法:这种方法适合于较小的应用程序。在这种情况下,每当应用程序执行完某一串行口操作后,将不断检查MSComm控件的CommEvent属性以检查执行结果或者检查某一事件是否发生。例如,当程序向串行设备发送了某个命令后,可能只是在等待收到一个特定的响应字符串,而不是对收到的每一个字符都立刻响应并处理。

  使用的每个MSComm控件都与一个串口对应。如果在应用程序中需要访问多个串口,必须使用多个MSComm控件,可以在Windows 控制面板中修改串口地址的中断地址。

MSComm控件的常用属性

  • CommPort属性:设置或返回通讯端口号,可以设置为1到16之间的任何值;
  • Settings属性:以字符串形式设置或返回波特率、奇偶校验、数据位和停止位;
  • PortOpen属性:设置或返回通讯口的状态以及打开和关闭端口,可通过把该属性设置为true或者false来打开或者关闭端口;
  • InBufferSize和OutBufferSize属性:分别设置接收和发送缓冲区分配的内存数量,单位为字节,缺省值分别为1024byte和512byte;
  • InputLen属性:确定希望从接收缓冲区移出的字符数量,当InputLen=0时,一次把接收缓冲区的字符全部移出;
  • Input属性:从接收缓冲区中读出数据,然后将该数据从缓冲区移走。
  • OutPut属性:向发送缓冲区传递待发送的数据。
  • InBufferCount和OutBufferCount属性:分别确定当前驻留在接收缓冲区等待被取出和发送缓冲区准备发送的字符数量,这两个属性设置为0,接收和发送缓冲区的内容将被清除;
  • InputMode属性:设置接收传入数据的格式,设置为0采用文本形式,设置为1采用二进制格式;
  • SThreshold属性:保存一个产生发送OnComm事件的界限值,本系统设置该属性为0,发送数据时不产生OnComm事件;
  • RThreshold属性:设定当接收几个字符时触发OnComm事件,本系统设置该属性为1,每接收一个字符就产生一个OnComm事件;

MSComm控件的事件

  MSCOMM控件只使用一个事件OnComm,用属性CommEvent的17个值来区分不同的触发时机,主要有以下几个:

  • CommEvent=1时:传输缓冲区中的字符个数已少于Sthreshold(可设置的属性值)个;
  • CommEvent=2时:接收缓冲区中收到Rthreshold(可设置的属性值)个字符,利用此事件可编写接收数据的过程;
  • CommEvent=3时:CTS线发生变化;
  • CommEvent=4时:DSR线发生变化;
  • CommEvent=5时:CD线发生变化;
  • CommEvent=6时:检测到振铃信号;

  另外十种情况是通信错误时产生,即错误代码。

基于VS2010下MFC的MSComm串口程序的实现

1、注册MSComm控件

  我在网上下载了MSComm控件之后,将其放于项目目录下,并在当前目录建了个.bat批处理文件,其内容如下:

copy .\\MSCOMM\\MSCOMM.SRG %windir%\system32
copy .\\MSCOMM\\MSCOMM32.DEP %windir%\system32
copy .\\MSCOMM\\MSCOMM32.oca %windir%\system32
copy .\\MSCOMM\\mscomm32.ocx %windir%\system32

regsvr32 mscomm32.ocx

双击此文件,即可注册MSComm控件。

2、添加MSComm控件

  首先将MSComm控件添加进VS2010工具箱,再给项目添加该ActiveX控件对应的“基于MFC的ATL类”,最后将工具箱中的MSComm控件(电话图标)拖至对话框即可。在对话框中添加MSComm控件后,其侧面会有白色,右击此控件,选择“编辑控件”,即可去除白色。

3、添加控件变量

  在主对话框中添加与MSComm控件相关联的控件变量(成员对象),通过此成员变量可操作串口。

4、串口信息配置及打开串口

  在对话框模板上右击MSComm控件,选择Property菜单项,即可设置MSComm控件各项属性。在调制解调器通讯的程序中,设置“Control”属性页中Handshaking项为“2-comRTS”,否则国内部分厂家modem不能正常通讯,其它接受缺省设置。另外亦可通过修改对话框类的OnInitDialog()函数来设置控件的属性。具体参考MSDN中的关于Comm Control的详细说明。

  我程序的串口设置代码大致如下:

    //*********************** 串口设置 **************************//
    m_ctrlComm.put_CommPort(port);//选择com口
    m_ctrlComm.put_InputMode(1);//输入方式为二进制方式
    m_ctrlComm.put_InBufferSize(1024);//输入缓冲区大小为1024byte
    m_ctrlComm.put_OutBufferSize(512);//输出缓冲区大小为512byte

    CString strBaudrate;
    strBaudrate.Format(_T("%ld"),baudrate);
    m_ctrlComm.put_Settings(strBaudrate+_T(",n,8,1"));//设置串口参数:9600波特率,无奇偶校验,8个数据位,1个停止位

    if(!m_ctrlComm.get_PortOpen())
    {
        /*
        HANDLE m_hCom;
        CString strCom;
        strCom.Format(_T("\\\\.\\COM%d"),(int)(m_ctrlComm.get__CommPort()));
        // 这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件,
        //如果该设备不存在或者被占用,则会返回一个错误,即下面的 INVALID_HANDLE_VALUE ,
        //据此可以判断可使用性。详细参见MSDN中的介绍。
        m_hCom = CreateFile(strCom, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if(m_hCom == INVALID_HANDLE_VALUE)//如果没有该设备,或者被其他应用程序在用
        {
            int errornum=GetLastError();
            if(errornum==2)
                strCom.Format(_T("端口%d 不存在"),(int)(m_ctrlComm.get__CommPort()));
            else if(errornum==5)
                strCom.Format(_T("端口%d被占用"),(int)(m_ctrlComm.get__CommPort()));
            AfxMessageBox(strCom);
            CloseHandle(m_hCom); // 关闭文件句柄,后面我们采用控件,不用API
            return ;//这是因为串口初始化封装在另一个函数里面在OnInitDialog调用。
        }
        CloseHandle(m_hCom); // 关闭文件句柄,后面我们采用控件,不用API
        */
        try
        {
            m_ctrlComm.put_PortOpen(true);//打开串口
        }
        catch(COleDispatchException *e)
        {
            CString strError;
            strError.Format(_T("打开串口失败!\n\nError Number: %d \nError Message: %s"),
                e->m_wCode,e->m_strDescription);
            MessageBoxW(strError,_T("错误提示"),MB_ICONERROR);
            return;
        }
    }
    else
    {
        //MessageBox(_T("Cannot open serial port!"));
    }

    m_ctrlComm.put_RThreshold(1);//每当串口接收缓冲区有多余或等于1个字符时将引发一个接收数据的oncomm事件
    m_ctrlComm.put_InputLen(0);//设置当前接收区数据长度为0
    m_ctrlComm.get_Input();//预读缓冲区以清空残留数据

5、串口数据的读写

  MSComm 类的读写函数比较简单:get_Input()和put_Output()。函数原形分别为VARIANT get_Input()和void put_Output(const VARIANT newValue),均使用VARIANT类型。但PC机发送和接收数据时习惯用字符串形式。MSDN中查阅VARIANT类型,可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9X并不支持。所以要完成一个适应各平台的串口应用程序必须解决这个问题,这里使用CByteArray解决之。

  添加接收数据函数,在对话框中双击Comm Control,接受默认函数,则对话框类的成员函数为OnCommMscomm(),其大致代码如下:

   CDataTypeConverter DTC;
    //电话图标可能有一半白边去不了,右击电话图标点击edit control就可以去掉
    if(m_ctrlComm.get_CommEvent()==2)//事件值为2表示接收事件
    {
        BYTE rxdata[255]={0};//设置BYTE数组
        VARIANT variant_inp=m_ctrlComm.get_Input();//读缓冲区
        COleSafeArray safearray_inp = variant_inp;//VARIANT型变量转换为COleSafeArray变量
        long len=safearray_inp.GetOneDimSize();//得到有效数据长度
        for(long k=0;k<len;k++)
            safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE数组
        m_ctrlComm.put_OutBufferCount(0);//清空发送缓冲区
        m_ctrlComm.put_InBufferCount(0);//滑空接收缓冲区
        safearray_inp.Clear();

        for(long k=0;k<len;k++)
        {
            BYTE bt = *(char*)(rxdata+k);//字符型
            short int intDec=(int)bt;
            CString strtemp=DTC.Dec2Hex(intDec);
            m_strDataRXTemp+=strtemp;//加入接收编辑框对应字符串
        }
        m_strDataRX=m_strDataRXTemp;
        m_strDataRXTemp="";
   }

其中,Dec2Hex()函数的代码如下:

CString CDataTypeConverter::Dec2Hex(unsigned int intDec)
{
    CString strHex;
    char charHex[255];
    sprintf(charHex,"%x",intDec);
    strHex=charHex;
    if(strHex.GetLength()==1)
        strHex=_T("0")+strHex;
    return strHex;
}

  发送数据的代码大致如下:

//UpdateData(true);//读取编辑框内容m_strDataTX

//发送的字符串上表面为十六进制格式
CString m_strCtrlLightBL;
m_strCtrlLightBL="55AA0AAA6B4310100000";//"55aa0aaa6b4310100000"

CDataTypeConverter DTC;
COleVariant m_OleVariant=DTC.HexM2OleVariant(m_strCtrlLightBL);

m_ctrlComm.put_Output(m_OleVariant);//发送数据

其中,HexM2OleVariant()函数定义如下:

COleVariant CDataTypeConverter::HexM2OleVariant(CString strHexM)
{
    BYTE bt[255];
    short int len=strHexM.GetLength();
    short int length=0;
    short int intDec;
    for(int n=0,i=0;n<len-1;n+=2,i++)
    {
        intDec=Hex2Dec(strHexM.Mid(n,2));
        bt[i]=char(intDec);
        length=i+1;
    }
    CByteArray m_Array;
    m_Array.RemoveAll();
    m_Array.SetSize(length);
    for(int i=0;i<length;i++)
        m_Array.SetAt(i,bt[i]);
    return COleVariant(m_Array);
}

注意:接收数据时,RThreshold属性很重要,因为它影响着OnComm事件的触发条件,在程序中可以通过put_RThreshold()函数来设定RThreshold属性。

VS2013开发上位机并调用MSCcommm控件的方式的更多相关文章

  1. 袁创:使用反射动态调用ActiveX控件

    ■■■■前言 目前的基于.NET平台的软件研发中仍然存在大量的对COM及ActiveX控件的调用.使用C#调用ActiveX控件时一般是使用vs.net工具自动生成的互操作性程序集.这种方法操作简单, ...

  2. 用WindowsAppSDK(WASDK)优雅的开发上位机应用

    C#开发上位机应用的一些选择 如果你不想看介绍,可以直接跳到优雅开发示例那里. 1. WASDK(WinUI 3) Windows 应用 SDK 是一组新的开发人员组件和工具,它们代表着 Window ...

  3. WinUI(WASDK)项目实践——优雅的开发上位机应用(新)

    摘要 这就是一个记录自己进行WinUI项目实践的博客,项目开源地址如下,觉得有帮助的可以去看看,因为项目都开源了,所以保姆级的讲解肯定不如直接看代码来的实在了. 电子脑壳项目地址 为什么叫新 因为之前 ...

  4. vs2010开发activex(MFC)控件/ie插件(三),js调用ocx控件的接口函数

    原文:http://blog.csdn.net/yhhyhhyhhyhh/article/details/50802280   js调用ocx控件的接口函数,先看demo效果:      简单测试过程 ...

  5. 【VS开发】使用MFC创建并调用ActiveX控件

    使用MFC创建并调用ActiveX控件 今天做了一下ActiveX的使用测试,总结一下: 首先使用MFC创建一个activeX的控件譬如ActiveXTest,编译成ocx并注册,然后另外编写一个测试 ...

  6. winform快速开发平台 -> 快速绑定ComboBox数据控件

    通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...

  7. iOS开发UI篇—使用picker View控件完成一个简单的选餐应用

    iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明:点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162 ...

  8. 在Web上调用Ocx控件

    原文:http://blog.csdn.net/goodadult2012/article/details/6343369 在HTML页面中使用ActiveX控件包含三个基本操作:将控件放入HTML中 ...

  9. 使用反射动态调用ActiveX控件

    使用反射动态调用ActiveX控件 袁永福 2018-3-2 ■■■■问题描述: 目前的基于.NET平台的软件研发中仍然存在大量的对COM及ActiveX控件的调用.使用C#调用ActiveX控件时一 ...

随机推荐

  1. php服务器代理解决浏览器跨越问题

    详见代码 <?php $url = $_SERVER["QUERY_STRING"]; switch ($_SERVER['REQUEST_METHOD']) { case ...

  2. win7x64上配置 vs2015

    win7 x64配置vs2015成功关键 1. 首先查看win7版本是否是sp1或以上: [计算机]----->[属性] 2. 将IE浏览器升级到IE11: IE11[百度网盘下载],提取码:[ ...

  3. Git应用—01初始化项目

    1.环境变量GIT_HOME      D:\GreenSoftware\PortableGit  Path    %GIT_HOME%\cmd; 2.初始化git config --global u ...

  4. 启动weblogic服务时,还需要输入用户名和密码的解决方法

    当启动weblogic服务时,还需要输入用户名和密码,相当繁琐,如下: 而在生产环境中,一般会要求不要在每次启动时都输入用户名密码, 因此可以通过一些简单的配置达到此目的,通常的做法有两种: 1.修改 ...

  5. eclipse显示代码行数

    最近做的手机APP正在进行最后一部分了,在一个类中估计要写上千行代码,来回的拉动滚动条太麻烦了,于是发现为什么我得eclipse不显示代码行数呢  其他C什么的编译器都显示的. 于是百度了一下,一下子 ...

  6. Kotlin入门(10)七十二变的输入参数

    上一篇文章介绍了Kotlin对函数的基本用法,包括函数的定义.输入参数的声明.输出参数的声明等等,这些足够对付简单的场合了.当然了,倘若一门新语言仅仅满足于这些雕虫小技,那也实在没什么前途.既然Kot ...

  7. 13.1、多进程:进程锁Lock、信号量、事件

    进程锁: 为什么要有进程锁:假如现在有一台打印机,qq要使用打印机,word文档也要使用打印机,如果没有使用进程锁,可能会导致一些问题,比如QQ的任务打印到一半,Word插进来,于是打印出来的结果是各 ...

  8. SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法(2)

    书接上文. 上文中描述了如何在 SpringCloud+Feign环境下上传文件与form-data同时存在的解决办法,实践证明基本可行,但却会引入其他问题. 主要导致的后果是: 1. 无法与普通Fe ...

  9. Unable to load DLL 'SQLite.Interop.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

    在主工程(ASP.NET WEB/WCF等)的NuGet里引用 System.Data.SQLite.Core 不仅仅是Service需要引用,主工程即使不直接使用SQLite的库,也需要引用. 若使 ...

  10. python第九十五天--js正则

    定义正则表达式 /.../ 用于定义正则表达式 /.../g 表示全局匹配 /.../i 表示不区分大小写 /.../m 表示多行匹配 JS正则匹配时本身就是支持多行,此处多行匹配只是影响正则表达式^ ...