基于VC++开发串口通信的方法

[复制链接]
查看4590 | 回复10 | 2006-8-13 23:13:00 | 显示全部楼层 |阅读模式
串行通信中的关键是串行通信设备的初始化、数据的发送和接收及其实现方式。
  
    在Dos环境下,用户可以直接对通信设备编程,可以通过查询中断的方式使用通信设备。但是Windows不提倡应用程序直接控制硬件,而是通过Windows所提供的设备驱动程序来进行数据传递。在Windows环境中,通信支持中断功能,当通信设备接收到一个输入字符的时候就产生一个硬件中断,该中断暂停应用程序的执行,并把接收到的字符存入到一个叫接收数据队列的内存缓冲区中。而待发数据也先存入到一个叫发送数据队列的内存缓冲区中,再由Windows系统负责在后台进行发送。因此,在Windows下接收和发送数据的关键就是如何从接收队列取数据和如何向发送数据队列发数据。Windows提供了相应的API函数来实现这些功能。
  
    串行口在Win 32中是作为文件来进行处理的,而不是直接对端口进行操作。在Win32环境下,可以把串口看作由文件系统访问的设备。使用标准的CreateFile()函数打开端口,再使用ReadFile()和WriteFile()函数读写数据,就如同端口只是一个文件对象一样。对于串行通信,Win 32提供了相应的文件I/O函数与通信函数,使用这些函数,可以编制出符合不同需要的通信程序。
  
    通常实现串行通信的步骤如下:
  
    1 ) 按协议的设置初始化并打开串行口,这样做就是通知Windows本应用程序需要这个串口,并封锁其他应用程序使它们不能使用此串口。
  
    2) 配置这个串行口。
  
    3) 在串口上往返地传输数据,并在传输过程中进行校验。
  
    4) 不需要此串口时,关闭串口,即释放串口以供其它应用程序使用。
  
    在这四个步骤中,主要的程序代码集中在第3步。
  
    本文就Win32环境下基于VC++开发串口通信三种方式的编程问题展开讨论。
  
    1?采用MFC串口通信编程
  
    Win32系统把文件的概念进行了扩展,无论是文件、通信设备、命名管道、邮件槽、磁盘还是控制台都被看作是文件。因此,可以用MFC中的CFile类来实现对串口的操作,并且Win32中的文件I/O函数支持重叠式输入输出,这使得线程从费时的I/O操作中解放出来。Win32 API包含了一系列访问通信资源的通信函数。
  
    通常采用MFC的串口通信编程主要步骤如下:
  
    1)在MainFrm?cpp中定义全局变量
  
    HANDLE hCom;//准备打开的串口的句柄
  
    2)打开串口
  
    Windows通信会话以调用CreateFile()函数打开串行口开始。通信程序从CreateFile处指定串口设备及相关的操作属性,并返回一个句柄,该句柄将被用于后续的通信操作。
  
    3)串口初始化(设置串口参数及进行串口超时设置)
  
    在打开通信设备句柄后,通常要对串口进行初始化工作(即配置参数如波特率、数据位数、停止位数、奇偶校验以及握手和流控协议等),配置串口通过改变数据结构——设备控制块DCB(Device Control Block)来实现。结构DCB有近30个数据成员,对于采用3线方式的串行通信来说,只要设置好波特率、数据位、停止位、校验位等几个关键参数就可以。
  
    调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。要修改串口配置,应该先修改串口的DCB结构,然后调用SetCommState函数用指定的DCB结构来设置串口。
  
    除了在结构DCB中的设置外,程序一般还需要设置I/O缓冲区的大小和超时。超时设置是为了避免当通信过程中由于数据传输突然中断时而造成对串口的读写操作进入无限期的等待状态。超时设置过程分为两步,首先设置超时结构COMMTIMEOUTS中的五个成员,然后调用SetCommTimeouts()函数设置超时值。
  
    这里介绍一种简捷的方法可以做到不了解DCB的详细内容也可以设置好串行通信参数:即利用BuildCommDCB函数来设置DCB,然后用函数SetCommState()配置串行通信口。
  
    DCB dcb;//定义数据控制块结构
  
    GetCommState(hCom,&dcb);//读串口原来的参数设置
  
    BuildCommDCB(“COM2:9600,N,8,1”,& dcb);//创建数据控制块DCB结构
  
    SetCommState(hCom,&dcb);//将结构DCB的主要参数设置到串口COM2
  
    其中的BuildCommDCB(“COM2:9600,N,8,1”,&dcb)语句可以代替前面4条串口通信参数赋值语句的效果。
  
    4)读写串口API函数
  
    串行口打开后,可以对它进行读写操作。
  
    5)关闭串口API函数
  
    串行口是非共享资源,所以打开串口后,一定要关闭串口。关闭串口函数的原型:BOOL CloseHandle(HANDLE hObject);其中hObject参数为CreateFile()返回的端口句柄。返回值非0,则调用成功。
  
    2?采用ActiveX控件 (MScomm控件)编程
  
    虽然在Windows下Win32API提供了使用CreateFile/WriteFile等文件I/O函数进行串行口操作的方法,但是在编程实现上比较复杂。Windows平台先进的ActiveX技术使得对串行口编程时不再需要处理烦琐的细节。利用已有的ActiveX控件,只需要编写少量的代码,就可以轻松高效地完成任务。
  
    Microsoft Communications Control(以下简称MSComm)是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件,它为应用程序提供了通过串行接口收发数据的简便方法。具体地说,它提供了两种处理通信问题的方法:一是事件驱动(Event-driven)方法:主要是在comEvReceive(接收到数据)事件发生时响应并获取缓冲区的数据。一是查询法:在这种情况下,每当应用程序执行完某一串行口操作后,将不断检查MSComm控件的CommEvent属性以检查执行结果或者检查某一事件是否发生(Microsoft提供的用于串口操作的控件Mscomm共有27个属性)。
  
    以下介绍在VC集成开发环境下对该控件编程的步骤。
  
    (1)在当前的Workspace中插入MSComm控件
  
    在VC环境下,创建基于对话框的MFC应用程序,在设置项目选项时必须选上ActiveX Controls,其他的按照缺省设置。然后进行以下步骤:Project菜单→Add to Project→Components and Controls→Registered ActiveX Controls→选择Components: Microsoft Communications Control,version 6.0插入到当前的Workspace中。
  
    结果添加了类CMSComm(另有相应文件:mscomm.h和mscomm?cpp)。
  
    (2)在MainFrm.h中加入MSComm控件
  
    protected:
  
    CMSComm m_ComPort;
  
    在Mainfrm.cpp::OnCreate()中加入:
  
    DWORD style=WS_VISIBLE|WS_CHILD;
  
    if(!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
  
    TRACE0(“Failed to create OLE Communications Control\n”);
  
    return-1;//创建失败}
  
    (3)初始化串口
  
    以下是通过设置控件属性对串口进行初始化的实例:
  
    m_ComPort.SetCommPort(1);//指定串口号
  
    m_ComPort.SetInBufferSize(1024);//设置输入缓冲区的
  
    大小,Bytes
  
    m_ComPort.SetOutBufferSize(512);//设置输出缓冲区的
  
    Bytes,
  
    if(!m_ComPort.GetPortOpen());//打开串口
  
    m_ComPort.SetPortOpen(TRUE);//打开通信口
  
    m_ComPort.SetInputMode(1);//设置输入方式为二进制
  
    方式
  
    m_ComPort.SetSettings(“9600,n,8,1”);//设置波特率等
  
    参数
  
    m_ComPort.SetRThreshold(1);//为1表示有一个字符引
  
    发一个事件
  
    m_ComPort.SetInputLen(0);
  
    (4)捕捉串口事件
  
    MSComm控件可以采用轮询或事件驱动的两种方法从端口获取数据。比较常用的是事件驱动方法:有事件(如接收到数据)时通知程序。在程序中需要捕获并处理这些通讯事件。
  
    (5)串口读写
  
    完成读写的两个函数的原型是VARIANT GetInput();及void SetOutput(const VARIANT& newValue);都要使用VARIANT类型。首先由函数GetInBufferCount()检查串口接收到的字符数,然后由GetInput()函数读取这些已接收的字节。用SetOutput函数将数据从串口发送出去。
  
    3?利用VC++运行时库的标准通信函数实现串口编程
  
  利用C进行异步通信编程,其实是对UART内部寄存器的读出或写入操作。VC可利用VC++类库中提供的标准通信函数-inp()/-outp()函数对其进行编程,它们的端口地址列于下表。
  
  
  
  
  
  
  
    通过计算机串口进行串行通信之前,必须根据设备的有关通信参数,约定双方的通信方式,包括波特率的设置、奇偶位校验及停止位的设立,确定数据传输帧格式和UART操作方式,逐个对线路控制寄存器、波特率因子寄存器等寄存器写入操作,可以利用VC运行库提供的通信函数_inp,_outp来实现串口通信。
  
    具体的实现步骤为:
  
    确定计算机的通信口地址。假定计算机的通信口地址是2F8,则PORT=0x2F8;利用_outp(PORT,0x60)将波特率设置为2400,数据传输格式则由地址PORT+3确定,如选用七位-停止位偶校验,则采用outp(PORT+3,0x3a)及outp(PORT+3,0x03)两个语句。
  
    计算机通过串行通信口与外设进行数据传输和控制时,首先要对端口的状态进行检测,然后通过UART芯片把传送来的数字信号转换为模拟信号,数据经过移位寄存器将并行的以字节为单位的数据以位为单位串行发送到串行通信端口,当数据到达串行通信端口时,按照RS-232通信协议规定传送数据至外设。这就是主机进行串行通信从而实现控制外设的整个过程。反之,当主机获取外设当前状态和相关数据,或者外设向主机回送数据时,端口检测到有数据信号,则通过RS-232总线协议传送信号至UART芯片,UART芯片把传送来的模拟信号转换为数字信号,数据经过移位寄存器将串行数据合为并行数据,送至CPU进行处理。只要CPU检测到UART发送器保持寄存器为空,即向UART输出一个字符。发送方首先输出RTS和DTR有效,检测MODEM寄存器,只有收到DEC输入的CTS和DTR有效,CPU才向UART输出一发送字符。
  
    只要CPU检测到UART接收器数据准备就绪,就可以从接收器的数据寄存器重读取一个字符。接收方首先输出数据终端就绪(DTR=1),然后检测MODEM状态寄存器,只有DSR=1,CPU才接收字符。
  
  结论:VC++是一个非常优秀的用户平台,提供了多种方法对串口进行通信控制,使用户不必了解具体的硬件原理,简化了编程,使程序透明化,并且适应性好,可移植性高。以上三种实现串口通信的方法是笔者经过长期具体工程实践总结出来的。经过比较,笔者认为:采用MFC串口通信编程是最灵活、最常用并且功能强大的方法,适用于与各种不同的外设进行串行通信,但需要程序员对硬件工作原理有较深入的了解;ActiveX控件编程容易实现,不需要处理烦琐的细节,但是灵活性较差;利用VC运行时库的标准通信函数实现串口编程原理简单,不足之处是实用性和灵活性较差。因此,在具体实践中要结合功能需要,综合考虑复杂程度、灵活性等因素,选择合适的方法,实现串口通信。

zhang999 | 2006-11-2 19:07:00 | 显示全部楼层
很好啊,谢啦
fang1796 | 2006-12-24 16:00:00 | 显示全部楼层
虽然不是很懂!但还是顶了先!偶一定好好学习!
YUANJINGHAO1129 | 2007-1-17 09:23:00 | 显示全部楼层

不错,有水平

xuliang987 | 2007-5-16 20:11:00 | 显示全部楼层
学习中
pengjr3000 | 2007-5-30 09:49:00 | 显示全部楼层

感谢楼主把这些资料上传!

codeceo | 2007-6-4 13:19:00 | 显示全部楼层
感谢
codeceo | 2007-6-5 11:19:00 | 显示全部楼层
学习中...
skyflue | 2008-4-24 17:13:00 | 显示全部楼层

haotie

lomeya | 2008-5-16 21:29:00 | 显示全部楼层
深奥!
liujianlin08 | 2008-7-7 11:55:00 | 显示全部楼层
[em50][em50]学习
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则