📄 1585.htm
字号:
<TABLE border=0 cellPadding=0 cellSpacing=0 width=750>
<TBODY>
<TR>
<TD width=144><img src=/images/sina_kjsd.gif width=144 height=34 alt=科技时代></TD>
<TD background=/images/tech_bg.gif vAlign=bottom width=606>
<TABLE background="" border=0 cellPadding=0 cellSpacing=0 width="100%">
<TBODY>
<TR>
<TD bgColor=#004dc0 width=6><IMG height=21
src="/images/c1.gif" width=6></TD>
<TD bgColor=#004dc0 class=sb width=600><IMG height=4
src="/images/c1.gif" width=1><BR><A
class=tech1 href="http://www.sina.com.cn/">新浪首页</A> > <A
class=tech1 href="http://tech.sina.com.cn/">科技时代</A> > <A
class=tech1 href="http://tech.sina.com.cn/introduction/">网上学园</A>
> <A class=tech1 href="/introduction/prog.shtml">高级编程</A>
> <A class=tech1 href="http://www.swm.com.cn">软件世界</A>
>
正文</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!--结束:table 1--><!--开始:新闻内容-->
<TABLE border=0 cellPadding=0 cellSpacing=0 width=750>
<TBODY>
<TR>
<TD colSpan=5 height=25></TD></TR>
<TR vAlign=top>
<TD align=middle vAlign=top width=109>
<TABLE border=0 cellPadding=0 cellSpacing=0 width="100%">
<TBODY>
<TR>
<TD align=middle height=250 vAlign=top><A
href="http://www.swm.com.cn"><img src="/images/meiti_logo/logo_softwareworld.gif" border="0" width=100 height=43></A><BR><BR><BR><BR> <BR></TD></TR>
<TR>
<TD class=title12 height=18>
</TD></TR></TBODY></TABLE></TD>
<TD bgColor=#000000 width=1><BR></TD>
<TD width=8><BR></TD>
<TD vAlign=top width=520>
<CENTER><FONT COLOR=#000066 size=5><B>Windows 95/98下高效率串行通信的实现</B></FONT><br>
<HR WIDTH=520 ALIGN="center" SIZE=1>
<FONT FACE="Arial" SIZE=2>http://tech.sina.com.cn 2001/03/12 11:06 </FONT><FONT COLOR="#990000" class=title12>软件世界</FONT> <FONT COLOR="#000000" class=title12>韩磊 徐磊</FONT></center><font class=confont>
<p> 1. 引言<p> 由于串行通信具有结构简单、可靠性高等优点,因而在工业监控、数据采集等实时系统中得到了广泛的应用。目前,PC与PC之间,PC与单片机之间大都采用这种通信方式。而在Windows下通信程序的设计与在DOS下有着显著的不同,Windows 95/98操作系统将底层的硬件进行了封装,不允许程序员直接对其进行操作,而是提供了大量的WIN32 API函数来作为用户程序与通信硬件的接口。由于在实际的应用中,对串行通信的可靠性、实时性有着较高的要求,因而本文在简单阐述在Windows 95/98平台下实现串行通信的基本方法的基础上,着重介绍如何利用Windows 95/98的多线程机制和重叠I/O实现高效率的串行通信。<p><p> 2. Windows 95/98下重叠I/O(异步)串行通信的实现<p> Windows 95/98系统为串行通信提供了全新的服务。它以WIN32 API作为其应用程序的编程接口,提供了打开及关闭通信资源句柄及读写的基本操作。更为重要的是,它支持重叠式(即异步)输入输出。在用WIN32 API函数读写串口时,既可以同步执行,也可以重叠(异步)执行。同步执行时,函数直到操作完成后才返回,这就有可能导致线程的阻塞,从而使效率下降;重叠执行时,即使操作还未完成,函数也会立即返回,I/O操作将在后台进行,从而极大地提高了CPU的利用率和程序的执行效率。<p> 下面,就分步详细介绍一下如何实现重叠I/O。<p> (1)串口的打开和关闭<p> 在WIN 32系统中串口的打开是用API函数CreateFile实现的,该函数的声明为:<p> HANDLE CreateFile(<p> LPCTSTR lpFileName,//文件名<p> DWORD dwDesiredAcess,//访问模式<p> DWORD dwShareMode,//共享模式<p> LPSECURITY_ATTRIBUTES lpSecurityAttributes,//通常为NULL<p> DWORD dwCreationDistribution,//创建方式<p> DWORD dwFlagsAndAttributes,//文件属性和标志<p> HANDLE hTemplateFile,//临时文件句柄,通常为NULL<p> );<p> 对于I/O方式,必须指定串口的FILE_FLAG_OVERLAPPED属性。使用完串口后应用CloseHandle函数关闭串口。<p> (2)串口的初始化<p> 串口打开后,需要做一些初始化工作,例如:波特率、数据位数、奇偶校验、停止位数等。这些信息都包含在一个DCB结构中。要得到当前的串口配置,可使用GetCommState函数,该函数的声明为:BOOL GetCommState(HANDLE hFile,LPDCB lpDCB),它把当前的配置添充到一个DCB结构中。然后可以根据需要来设置DCB结构中的成员变量,再调用SetCommState函数用该DCB结构来设置串口。此外,程序还需设置I/O缓冲区的大小和超时。输入和输出缓冲区的大小通过调用SetupComm函数来实现,该函数的声明为:BOOL SetupComm(HANDLE hFile,DWORD dwInQueue,DWORD dwOutQueue),其中dwInQueue和dwOutQueue分别为输入和输出缓冲区的大小。超时的查询和设置则通过GetCommTimeouts、SetCommTimeouts函数和COMMTIMEOUTS结构来实现。本文中,为了讨论上的方便,我们不使用超时设置。下面就以一段程序为例,来看一下如何打开并初始化串口。<p> HANDLE hCom;<p> DWORD dwError;<p> DCB dcb;<p> COMMTIMEOUTS timeOuts;<p> If(hCom=CreateFile("COM1",//打开COM1口<p> GENERIC_READ|GENERIC_WRITE,//允许读写<p> 0,//独占方式<p> NULL,<p> OPEN_EXITING,//打开而不是创建<p> FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,<p> //重叠方式<p> NULL)==INVALID_HANDLE_VALUE)<p> dwError=GetLastError();<p> ...... //处理错误<p> SetCommTimeouts(hCom,&timeouts);<p> //不使用超时,将timeouts结构设置为零<p> SetupComm(hCom,1024,1024);//设置缓冲区大小<p> GetCommState(hCom,&dcb);<p> dcb.BaudRate=CBR_9600;//设置波特率<p> dcb.ByteSize=8;<p> ......<p> SetCommState(hCom,&dcb);<p> (3)串口的读写<p> 串口的读写分别用ReadFile和WriteFile函数实现。前面已经指出,串口的读写既可以同步进行,也可以重叠(异步)执行,下面我们就以读串口为例,来讨论一下重叠I/O的实现方法。<p> ReadFile函数的声明为: <p> BOOL ReadFile(<p> HANDLE hFile, //要读取的串口句柄<p> LPVOID lpBuffer, //读缓冲区<p> DWORD nNumberOfBytesToRead, //要求读入的字节数<p> LPDWORD lpNumberOfBytesRead,//实际读入的字节数<p> LPOVERLAPPED lpOverlapped <p> //指向一个OVERLAPPED结构的指针 <p> );<p> 要使用重叠I/O,除了要在CreateFile函数中指定FILE_FLAG_OVERLAPPED标志外,线程还需为ReadFile函数创建一OVERLAPPED结构,该结构包含一个事件对象句柄hEvent,它是读写函数是否完成的标志。如果读写操作还未完成,函数就返回,就把hEvent置于无信号状态,操作完成后,再将它设置成有信号的。为此,线程还要用CreateEvent函数创建一手工重置事件。<p> 读写操作完成后(包括超时),函数返回TRUE,否则返回FALSE。需要注意的是,在使用重叠I/O时,读写操作还未完成,函数就有可能返回,此时函数返回FALSE,但操作并未失败。因此,当函数返回FALSE时,应立即调用GetLastError函数分析返回的结果。若返回结果为ERROR_IO_PENDING,则说明重叠操作还未完成,这时线程可以调用GetOverlappedResult结构等待操作完成。该函数的声明为:<p> BOOL GetOverLappedResult(<p> HANDLE hFile,<p> LPOVERLAPPED lpOverlapped,<p> LPDWORD lpNumberOfBytesTransferred,<p> //实际传输的字节数<p> BOOL bWait<p> );<p> 将bWait设置为TRUE,函数就会等待lpOverLapped所指向的OVERLAPPED结构的hEvent事件,并报告实际传输的字节数等信息。<p> 下面我们就以一程序为例,来看一下以重叠方式读串口的基本方法:<p> OVERLAPPED overlapped;<p> DWORD dwWantLength,dwRealLength,dwErrorFlags;<p> COMSATE ComSate;<p> overlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);<p> if(!ReadFile(hCom,lpBuffer,dwWantLength,<p> &dwRealLength,&overlapped));//读取串口<p> ///返回FALSE<p> {<p> if(GetLastError()==ERROR_IO_PENDING) <p> //重叠操作正在进行 <p> while(GetOverlappedResult(hCom,<p> &overlappedresult, //等待重叠操作 <p> &dwLength,TRUE))<p> AfxMessageBox("Reading has finished!");<p> else //发生错误<p> {<p> dwLength=0;<p> ClearCommError(hCom,&dwErrorFlags,&ComSate);<p> If(dwErrorFlags>0)<p> AfxMessageBox("Reading error!");<p> ......//处理错误<p> }<p> else<p> {......}<p><p> 3. 使用多线程机制<p> 在实际的串行通信应用中,通信请求的产生往往是随机的、突发性的。而Windows系统在接收到通信请求后,只和通信缓冲区进行数据交换,并不通知用户进程。这样,用户进程只能通过定时查询串口等方式进行通信,但往往会由于对通信请求反应的不及时而造成通信的错误及不可靠。<p> Windows 95/98中,WIN32应用程序组采取的抢占式多任务方式与Windows 3.1下WIN16应用程序的协同多任务方式不同,它可以同时执行多个进程和多个线程。线程是一个代码单元,在操作系统运行时标志着代码运行流。每个进程都有私有的虚拟地址空间,并可以拥有多个线程,进程的所有线程共享同一地址空间。每个线程被CPU分配一个时间片,一旦被激活,它正常运行到时间片耗尽并被挂起,然后操作系统选择另一线程进行工作。通过时间片轮转,又由于时间片很小,大约20毫秒级,就像多个线程在同时运行,这样可以充分挖掘CPU的潜力,增强用户进程的反应能力,以及进行后台处理,从而提高用户进程的效率。<p> 为此,我们可以利用Windows 95/98系统的抢占式多任务的特点,在用户进程的主线程之外,再建立一个通信监视线程,来监视通信事件的发生。<p> 在WIN32应用程序中,线程被分为两类:工作线程(Worker Thread)和用户界面线程(User Interface Thread)。前者常用于处理后台任务,执行这些后台任务并不会耽搁用户对应用程序的使用。鉴于工作线程的这些特点,我们就利用它来监视串口。工作线程的建立分两步来进行:首先编写线程控制函数,然后再启动线程。控制函数定义了线程。通常线程启动时,这个函数就被执行了。该函数的声明为:<p> UINT CommProc(LPVOID pParam);<p> 线程的启动可使用全局函数AfxBeginThread。在调用该函数的时候,需要将控制函数的名称和控制函数的参数作为参数传递给它。<p> 还有一点需要声明,当我们建立专门的线程来监视串口时,需要指定被监视事件,即建立事件屏蔽。这就要调用SetCommMask函数。如果要得到当前的被监视变量,则可调用GetCommMask函数。然后,线程调用WaitCommEvent函数来监视被指定的事件。当指定范围内的某一事件发生后,线程就结束等待并把该事件的屏蔽码设置到事件屏蔽变量中。需要注意的是,WaitCommEvent只检测调用该函数后发生的事件。<p> 在具体的编程中,我们可以把串口的初始化、事件屏蔽变量的建立及启动监视线程的工作放在主线程中进行,监视线程主要负责监视串口事件并向主线程发送消息(读写工作也可放在监视线程中)。这样,要实现多线程异步输入输出,首先需要在串口的初始化代码中加入如下代码(这里,我们只以读串口为例):<p> SetCommMask(hCom,EV_ERR|EV_RXCHAR);<p> //监视错误和输入缓冲区接收到新字符事件<p> 在初始化代码的最后添加:<p> AfxBeginThread(WatchProc,pParam);<p> 然后,再构建线程控制函数:<p> UINT WatchProc(LPVOID pParam)<p> { DWORD dwEventMask=0;//事件屏蔽变量<p> WaitCommEvent(hCom,&dwEventMask,NULL);<p> //等待指定事件的发生<p> if((dwEventMask & EV_RXCHAR)==EV_RXCHAR)<p> {......}//读缓冲区<p> if((dwEventMask &EV_ERR)==EV_ERR)<p> {......}//处理错误<p> return 0;<p> }<p><p> 4. 结束语<p> 以上给出了利用多线程机制和重叠I/O在Windows 95/98平台下实现串行通信的基本思想。在实际的工程应用中,如果能够灵活地加以利用,则对增强应用程序的灵活性、实时反应能力,以及提高程序的运行效率有很大的帮助。<p></font>
<table width=90% border=0 align=center>
<tr></tr>
</table>
<br>
<DIV align=right class=title12>【<a href=http://comment.sina.com.cn/cgi-bin/news/release1/news_face1?Windows%2095%2F98%CF%C2%B8%DF%D0%A7%C2%CA%B4%AE%D0%D0%CD%A8%D0%C5%B5%C4%CA%B5%CF%D6+http://tech.sina.com.cn/c/1585.html+1585+1>发表评论</a>】【<a href=http://bbs2.sina.com.cn/show.shtml?tech:newbie>初学者园地</a>】【<a href=http://chat27.sina.com.cn/cgi-bin/chat/go?channel=科技聊天室>科技聊天</a>】【<A
href="javascript:window.close()">关闭窗口</A>】</DIV><BR>
<table width=100% border=0 cellspacing=0 cellpadding=0><tr><td bgcolor=#455ca2><table width=100% border=0 cellspacing=1 cellpadding=3><tr><td bgcolor=#dcdcdc class=title12> 相关链接</td></tr><tr><td bgcolor=#ffffff class=title14><a href=http://tech.sina.com.cn/it2/55882.shtml target=_blank>Cypress推出了EZ-USB™SX2智能串行接口引擎</a><font COLOR=#6666cc> (2001/03/01 14:21)</font><br><a href=http://tech.sina.com.cn/news/computer/2000-02-16/17537.shtml target=_blank>经验杂谈:如何寻找丢失的串口</a><font COLOR=#6666cc> (2000/02/16 11:50)</font><br><a href=http://games.sina.com.cn/handbook/9908/081284.shtml target=_blank>答疑解难:CM3怎么联机玩?我用串口,谢谢!</a><font COLOR=#6666cc> (1999/08/12 00:00)</font></td></tr></table></td></tr></table>
<P>
<!--广告--><!--广告结束--><!--
<div align=right><a href=http://tech.sina.com.cn/hotnews/cnnic/index.shtml target=_blank><font size=2 color=red>资料:中国互联网权威管理机构CNNIC</font></a><div>
--></TD>
<TD width=112><BR></TD></TR></TBODY></TABLE><!--结束:新闻内容-->
<CENTER>
<!--标准尾-->
<p class=tail12><a href=http://home.sina.com.cn/intro/intro.shtml >网站简介</a> | <a href=http://members.sina.com.cn>用户注册</a> | <a href=http://home.sina.com.cn/intro/ads.shtml>广告服务</a> | <a href=http://www.sina.com.cn/intro/recruit.shtml>招聘信息</a> | <a href=http://home.sina.com.cn/intro/chinesereading.shtml>中文阅读</a> | <a href=http://richwin.sina.com.cn>Richwin</a> | <a href=http://home.sina.com.cn/intro/contacts.shtml>联系方式</a> | <a href=http://home.sina.com.cn/intro/help.shtml>帮助信息</a> | <a href=http://home.sina.com.cn/intro/lawfirm.shtml>网站律师</a>
<p class=tail12>Copyright © 1996 - 2001 SINA.com, Stone Rich Sight. All Rights Reserved<br><br><img src=http://image2.sina.com.cn/home/image/c.gif
width=1 height=1><a href=http://home.sina.com.cn/intro/copyright.shtml target=_blank>版权所有</a> 四通利方 新浪网</center><LAYER
height="60" width="468" visibility="hidden"
onLoad="moveToAbsolute(layer1.pageX,layer1.pageY);clip.height=60;clip.width=468; visibility='show';"
SRC="http://ad.cn.doubleclick.net/adl/tech.sina.com.cn/;;num=364658647415139.2?"></LAYER></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -