串口通信简介

  一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与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属性:向发送缓冲区传递待发送的数据。
  • InBufferCountOutBufferCount属性:分别确定当前驻留在接收缓冲区等待被取出和发送缓冲区准备发送的字符数量,这两个属性设置为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();//输入方式为二进制方式
m_ctrlComm.put_InBufferSize();//输入缓冲区大小为1024byte
m_ctrlComm.put_OutBufferSize();//输出缓冲区大小为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个字符时将引发一个接收数据的oncomm事件
m_ctrlComm.put_InputLen();//设置当前接收区数据长度为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表示接收事件
{
BYTE rxdata[]={};//设置BYTE数组
VARIANT variant_inp=m_ctrlComm.get_Input();//读缓冲区
COleSafeArray safearray_inp = variant_inp;//VARIANT型变量转换为COleSafeArray变量
long len=safearray_inp.GetOneDimSize();//得到有效数据长度
for(long k=;k<len;k++)
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE数组
m_ctrlComm.put_OutBufferCount();//清空发送缓冲区
m_ctrlComm.put_InBufferCount();//滑空接收缓冲区
safearray_inp.Clear(); for(long k=;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[];
sprintf(charHex,"%x",intDec);
strHex=charHex;
if(strHex.GetLength()==)
strHex=_T("")+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[];
short int len=strHexM.GetLength();
short int length=;
short int intDec;
for(int n=,i=;n<len-;n+=,i++)
{
intDec=Hex2Dec(strHexM.Mid(n,));
bt[i]=char(intDec);
length=i+;
}
CByteArray m_Array;
m_Array.RemoveAll();
m_Array.SetSize(length);
for(int i=;i<length;i++)
m_Array.SetAt(i,bt[i]);
return COleVariant(m_Array);
}

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

相关链接:

  深入浅出VC++串口编程(五) 基于第三方类库:http://blog.csdn.net/nash635/article/details/5339704

VS2010下MFC的串口编程的更多相关文章

  1. 【转】VS2010下MFC的串口编程

    串口通信简介 一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与PC进行数据传输和通信的通道,在CPU和外设之间充当解释器的角色.当字符数据从CPU发送给外设时,这些字符数据将被转换成串 ...

  2. linux下怎样对串口编程

    Linux操作系统从一開始就对串行口提供了非常好的支持,本文就Linux下的串行口通讯编程进行简单的介绍. 串口简单介绍串行口是计算机一种经常使用的接口.具有连接线少.通讯简单.得到广泛的使用. 经常 ...

  3. Linux下串口编程【转】

    本文转载自:http://blog.csdn.net/w282529350/article/details/7378388 /************声明:本人只是见到这篇文章对我帮助很大才转载的,但 ...

  4. Linux下串口编程入门

    简介: Linux操作系统从一开始就对串行口提供了很好的支持,本文就Linux下的串行口通讯编程进行简单的介绍. 串口简介  串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用.常用 ...

  5. 多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)

    老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧.确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如.但我一直没有停止过用 ...

  6. [转]VS 2012环境下使用MFC进行OpenGL编程

    我就不黏贴复制了,直接给出原文链接:VS 2012环境下使用MFC进行OpenGL编程 其它好文链接: 1.OpenGL系列教程之十二:OpenGL Windows图形界面应用程序

  7. win7系统下用vspd软件进行串口编程实例

    http://blog.csdn.net/qiusuo800/article/details/8299777 目前,我在学习C#串口编程类的基础知识,在网上也找了一些资料,但都存在一些问题,现在他们基 ...

  8. Windows下串口编程

     造冰箱的大熊猫@cnblogs 2019/1/27 将Windows下串口编程相关信息进行下简单小结,以备后用. 1.打开串口 打开串口使用CreateFile()函数.以打开COM6为例: HAN ...

  9. Linux下的串口编程及非阻塞模式

    本篇介绍了如何在linux系统下向串口发送数据.包括read的阻塞和非阻塞.以及select方法. 打开串口 在Linux系统下,打开串口是通过使用标准的文件打开函数操作的. #include < ...

随机推荐

  1. Ajax异步提交造成变量undefined

    在使用jQuery的get方法或post方法向后台发ajax请求时,在其中定义一个变量htmlcollectionlst,但是在循环结束后却发现是undifined $.get("GetPl ...

  2. MVC003之调用BLL层方法(BLL层调用了WebService)

    项目又BLL类库,在类库中引用了webservice.在web层调用BLL的方法时 错误如下: 在 ServiceModel 客户端配置部分中,找不到引用协定“OAService.IntranetSe ...

  3. commit 流程

    COMMIT是一个非常快的操作,当我们发布commit命令时,真正困难的动作已经完成,在数据库中已经执行了数据更改,所以已经完成了99%的任务,例如:下列操作已经产生: 1.在SGA(Buffer C ...

  4. StringBuffer类和String类(原文地址 : http://www.cnblogs.com/springcsc/archive/2009/12/03/1616330.html)

    StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存 ...

  5. 配置完php.ini中的扩展库后,重启apache出现错误1067

    网上有很多解决办法,比如更改环境变量,重装apache等等,但没有一个是符合我的.最后发现只是犯了一个低级错误,因为是第一次配置php.ini中的扩展库,忘记配置扩展库的路径. 解决办法:需要先加上扩 ...

  6. RelativeLayout 相对布局

    根据父容器来定位: 想位于哪,哪个属性就设置为true 左对齐:android:layout_alighParentLeft 右对齐:android:layout_alighParentRight 顶 ...

  7. Cisco 4507R+E四引擎VSS故障解决

    如果可以要做双引擎VSS(每个机箱1个引擎), 3.6.7版本可以实现 如果需要做4引擎VSS(每个机箱2个引擎) 请使用3.8.x和之后的版本.

  8. const和volatile分析

    c语言中const修饰的变量是只读的,不能直接作为赋值号的左值,其本质还是变量:会占用内存空间:本质上const在编译器有用,运行时无用(还是可以通过指针改变它的值) ; int *p=&ab ...

  9. JMeter 连接MySQL

     第一步:添加JDBC 驱动 第二步:在线程组 下面添加一个“JDBC Connection Configuration” 第三步:在“线程组”,在下面添加一个“JDBC request”

  10. linux自旋锁、互斥锁、信号量

    为了避免并发,防止竞争.内核提供了一组同步方法来提供对共享数据的保护. 我们的重点不是介绍这些方法的详细用法,而是强调为什么使用这些方法和它们之间的差别. Linux 使用的同步机制可以说从2.0到2 ...