Serial Communications in Win32中文版.docx
- 文档编号:4164606
- 上传时间:2022-11-28
- 格式:DOCX
- 页数:22
- 大小:36.69KB
Serial Communications in Win32中文版.docx
《Serial Communications in Win32中文版.docx》由会员分享,可在线阅读,更多相关《Serial Communications in Win32中文版.docx(22页珍藏版)》请在冰豆网上搜索。
SerialCommunicationsinWin32中文版
翻译:
SerialCommunicationsinWin32
艾伦很少吃早餐,但是如果他必须挑一个喜欢的事物,Win32串口通讯将会是最好的选择。
摘要:
微软Win32下的串口通讯完全不同于Windows下16位的串口通讯,那些熟悉16位下串口通讯功能的将不得不去适当的重新学习许多Win32下串口通讯的系统部分,这篇文章将会帮助你完成这些。
对串口通讯不熟悉的那些人通过这篇文章的学习将会为自己以后的研究发展奠定坚实的基础。
这篇文章主要以读者熟悉的多线程和Win32下的并行操作为例。
除此之外,基于对Win32堆栈功能的熟悉了解在理解内存管理机制中是非常有用的,比如这篇文章所提到的MTTTY。
对于较多关于这些功能的信息,商议平台SDK文件的编写,微软公司Win32SDK知识
库,或微软公司网络开发者程序馆。
那些控制用户接口和会话视窗的功能界面(APIs)尽管在这里并不做讨论,但是对于完全了解此篇文章提供的例子是非常有用的。
对于不熟悉一般微软视窗操作系统的应该在学习串口通讯之前学习一些基本的微软程序设计。
换句话说,在潜水之前先沾湿你的脚。
绪论:
这篇文章的焦点主要是应用程序设计界面(API)和微软公司兼容的方法上,WindowsNT和Windows95;因此,API在两个平台上的支持是唯一的探讨。
Windows95支持Win32,电话API(TAPI)和WindowsNT3.x不支持;因此,这里我们不讨论TAPI。
然而,TAPI确实值得一提,它在调制解调器接口和呼叫控制中的应用是非常有用的。
一个用调制解调器工作和电话程序应该实现这些TAPI功能界面。
这将允许用另一个TAPI应用程序接口与之紧密结合。
此外,这篇文章不讨论Win32下象GetCommProperties的一些配置功能。
这篇文章从如下几段入手:
打开串口、读写操作(非重叠和重叠)、串口状态(事件和错误)和串口设置(DCB,流量控制和通信超时)。
这篇文章所包含的例子MTTTY:
多线程TTY诸多功能实现的讨论。
它使用了三个线程来实现:
一个内存管理用户界面的线程,一个控制所有写操作的线程,一个控制读和改变串口状态的线程。
这个例子中的内存管理使用了一些不同的数据堆。
它也广泛的利用同步方法促进线程间的通信。
打开串口:
用CreateFile功能打开一个通信端口。
有两种方法调用CreateFile打开端口:
重叠的和非重叠。
下面是用重叠的方法打开通信资源操作的一个例子:
HANDLEhComm;
hComm=CreateFile(gszPort,
GENERIC_READ|GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if(hComm==INVALID_HANDLE_VALUE)
//打开错误;中断
移除标记FILE_FLAG_OVERLAPPED在调用CreateFile非重叠运算中的应用,下一区段将讨论重叠和非重叠运算。
当用SDK文件陈述平台打开一个通信端口的时候,CreateFile的调用有下列各项需求:
lfdw共享模式必须为0,通信端口与共享文件用相同的共享方式。
可以使用TAPI的功能去共享应用程序间的资源。
由于Win32应用程序不能使用TAPI,句柄遗传或者副本对共享通信来说是必需的。
副本处理在这篇文章讨论到了;关于更多的信息请先参阅SDK文件平台。
lfdwCreate必须指定OPEN_EXISTING标志。
lhTemplateFile参数必须为0。
传统意义上关于端口的命名是COM1,COM2,COM3,或者COM4。
Win32API不给系统上的端口提供任何确定的机制。
WindowsNT和Windows95保留了不同系统间的端口共性,所以任何一种方法应用在Win32平台上都比较方便。
一些系统甚至比传统的最大4个端口还要多。
硬件厂商和驱动程序开发者可以他们喜欢的方式自由命名端口。
正是由于这一原因,它可以让使用者更好的去指定他们所需端口的名字。
如果一个端口不存在,一个错误将会在尝试打开端口厚出错,并且使用者应该标明此端口不可利用。
串口的读写:
利用Win32进行串口间通讯的读写操作和Win32下文件的输入输出(I/O)操作是非常相似的。
实际上,完成文件输入输出和串口间的输入输出功能是相同的。
Win32下的I/O操作可以用两种方法实现:
重叠或者非重叠。
SDk文件编写平台采用异步和同步方式来完成这些I/O间的操作。
在这篇文章中,经常提到重叠和非重叠。
非重叠方式的I/O对大多数开发者来说是比较熟悉的,因为它是传统的I/O方式,当功能返回的时候被请求的操作完成。
在重叠I/O情况下,当一个操作没有完成和操作完成信号出现时系统将立即调用。
程序将利用I/O请求和完成之间的时间去执行后台作业。
Win32下串口通讯的读写操作明显不同与16位,16位Windows操作系统仅仅具有ReadComm和WriteComm功能。
Win32下的读写包括更多的功能和选择。
我们将在下面讨论这些问题。
非重叠I/O:
非重叠I/O尽管有些限制但是是非常简单的。
当调用线程阻塞的时候一个操作发生。
一旦操作完成,功能返回并当前且线程可以继续以前的作业。
这种类型I/O在多线程应用中是非常有用的,因为当一个线程阻塞时其他的线程仍旧可以完成当前作业。
它担负着恰当使用端口应用的职责,如果一个线程为了等待她的I/O操作完成而阻塞,随后所有其它的线程调用API将被阻塞直到最初的操作完成。
举例来说,如果一个线程正在等待ReadFile的返回,任何其它执行WriteFile功能的线程将被阻塞。
便携与否是是否选用重叠和非重叠众多原因中主要的一个。
因为大多数操作系统不支持重叠的,而大多数操作系统支持多线程,因而多线程非重叠I/O是最好的选择。
重叠I/O:
重叠I/O不像非重叠I/O那样简单但是有更多的适应性和有效性。
一个为重叠操作打开的端口允许在同一时刻使用多线程操作并且当操作没有确定的时候仍然可以执行其它作业。
此外,重叠操作在操作不可确定的情况下允许单一线程在后台响应不同的请求和完成作业。
在单线程和多线程应用中,一些同步操作在响应请求和处理结果的时候一定发生。
一个线程一直阻塞直到得到可用的操作结果。
重叠I/O的优势就是在请求和它完成这段时间之间允许一个线程完成一些作业。
如果没有可能的工作被做,那么唯一的情况就是重叠I/O允许更好的用户响应。
MTTTY就是使用重叠I/O操作的一个简单例子。
它创造一个线程来负责端口数据的写状态,它同时周期性执行后台工作。
程序创造另外一个专有的线程来从端口写数据。
注意:
应用软件有时候会滥用多线程系统创造太多的线程以至降低软件效率,但是使用多线程可以解决许多比较难的问题。
线程的减少可以缓解系统资源使用可是仍然需要诸如CPU时钟和内存的资源使用。
一个可以产生多余线程的应用软件可能影响整个操作系统的运行。
一个较好利用线程的方法就是为不同的作业创建不同的请求队列和不同的作业流来适应I/O操作,这种方法在MTTTY样本中的利用在这篇文章中提及。
一个重叠的I/O操作有两个部分:
操作的创建和检测的完成。
操作的创建必须限定使用OVERLAPPED结构,为同步操作创建一个手动操作并且调用适当的功能(读文件或者写文件)。
I/O操作肯定被立刻完成或者不可能。
假定一个重叠的操作总是依赖于另外一个重叠操作应用程序将会出错。
如果一个操作被立刻完成,那么这个操作需要继续正常执行。
重叠操作的第二部分直接探测它的完成。
检波探测的完成包括等待事件处理,检查重叠结果和处理数据。
重叠操作将会产生更多作业的原因就是产生许多错误,如果一个非重叠操作发生错误,仅仅返回一个错误结果。
但是如果一个重叠操作错误发生,它会在操作创建中失败或者使程序的执行不可预见,此时你必须设定一个操作时间段或者等待操作完成的信号。
串口的读操作:
ReadFile的功能就是发出一个读操作。
ReadFileEx也发出一个读操作,但是它在Windows95上不可用,在这篇文章中我们不讨论它。
这儿有详细的代码描述怎样去发出一个读请求,当ReadFile返回值为真的时候将调用此功能去处理数据,在重叠操作下有同样的调用功能。
我们用fWaitingOnRead来定义代码段,用它来指明读操作是否是重叠方式,它通常用来避免产生一个新的读操作。
DWORDdwRead;
BOOLfWaitingOnRead=FALSE;
OVERLAPPEDosReader={0};
//Createtheoverlappedevent.Mustbeclosedbeforeexiting
//toavoidahandleleak.
osReader.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(osReader.hEvent==NULL)
//Errorcreatingoverlappedevent;abort.
if(!
fWaitingOnRead){
//Issuereadoperation.
if(!
ReadFile(hComm,lpBuf,READ_BUF_SIZE,&dwRead,&osReader)){
if(GetLastError()!
=ERROR_IO_PENDING)//readnotdelayed?
//Errorincommunications;reportit.
else
fWaitingOnRead=TRUE;
}
else{
//readcompletedimmediately
HandleASuccessfulRead(lpBuf,dwRead);
}
}
重叠操作的第二步分是完成检波。
在OVERLAPPED结构中事件的处理被传送到WaitForSingleObject功能模块中,它将一直等到对象发出执行信号。
一旦操作完成,事件发出信号。
这并不意味着它被成功执行,仅仅表明执行完成。
GetOverlappedResult功能报告操作结果,如果发生一个错误,GetOverlappedResult返回出错并且GetLastError返回错误码。
如果操作成功完成,GetOverlappedResult返回值为真。
注意:
ERROR_IO_INCOMPLETE能检测操作是否完成同时可以返回操作失败的状态。
当操作没有完成时GetOverlappedResult返回出错并且GetLastError返回ERROR_IO_INCOMPLETE。
除此之外,ERROR_IO_INCOMPLETE可能被阻塞直到操作完成。
这就有效的将重叠操作转变成非重叠操作并且当完成时通过bWait参数来传递真值。
这里有一段演示重叠方式下完成读操作的代码。
当操作立刻完成的时候代码调用相同的功能来处理数据。
请注意fWaitingOnRead标志的使用,当一操作正在运行的时候它被调用来控制检测代码。
#defineREAD_TIMEOUT500//milliseconds
DWORDdwRes;
if(fWaitingOnRead){
dwRes=WaitForSingleObject(osReader.hEvent,READ_TIMEOUT);
switch(dwRes)
{
//Readcompleted.
caseWAIT_OBJECT_0:
if(!
GetOverlappedResult(hComm,&osReader,&dwRead,FALSE))
//Errorincommunications;reportit.
else
//Readcompletedsuccessfully.
HandleASuccessfulRead(lpBuf,dwRead);
//Resetflagsothatanotheropertioncanbeissued.
fWaitingOnRead=FALSE;
break;
caseWAIT_TIMEOUT:
//Operationisn’tcompleteyet.fWaitingOnReadflagisn’t
//changedsinceI’llloopbackaround,andIdon’twant
//toissueanotherreaduntilthefirstonefinishes.
//
//Thisisagoodtimetodosomebackgroundwork.
break;
default:
//ErrorintheWaitForSingleObject;abort.
//ThisindicatesaproblemwiththeOVERLAPPEDstructure’s
//eventhandle.
break;
}
}
串口的写操作:
在串口通讯中数据的传送和API下数据的读出有很多相似之处。
下面摘录的代码显示了如何执行和等待写操作的完成。
BOOLWriteABuffer(char*lpBuf,DWORDdwToWrite)
{
OVERLAPPEDosWrite={0};
DWORDdwWritten;
DWORDdwRes;
BOOLfRes;
//Createthiswriteoperation’sOVERLAPPEDstructure’shEvent.
osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(osWrite.hEvent==NULL)
//errorcreatingoverlappedeventhandle
returnFALSE;
//Issuewrite.
if(!
WriteFile(hComm,lpBuf,dwToWrite,&dwWritten,&osWrite)){
if(GetLastError()!
=ERROR_IO_PENDING){
//WriteFilefailed,butisn’tdelayed.Reporterrorandabort.
fRes=FALSE;
}
else
//Writeispending.
dwRes=WaitForSingleObject(osWrite.hEvent,INFINITE);
switch(dwRes)
{
//OVERLAPPEDstructure’seventhasbeensignaled.
caseWAIT_OBJECT_0:
if(!
GetOverlappedResult(hComm,&osWrite,&dwWritten,FALSE))
fRes=FALSE;
else
//Writeoperationcompletedsuccessfully.
fRes=TRUE;
break;
default:
//AnerrorhasoccurredinWaitForSingleObject.
//Thisusuallyindicatesaproblemwiththe
//OVERLAPPEDstructure’seventhandle.
fRes=FALSE;
break;
}
}
}
else
//WriteFilecompletedimmediately.
fRes=TRUE;
CloseHandle(osWrite.hEvent);
returnfRes;
}
我们注意到上面的代码使用了WaitForSingleObject功能和INFINITE定时值。
这将引起WaitForSingleObject功能一直等待到操作完成;这还可能使线程或者程序刮起。
事实上,写控制的执行只不过花费了较长的一段时间去完成或者使流控制阻塞了传输。
稍后讨论的状态检测可以检测到这种状态,但是不能使WaitForSingleObject返回,三个事件可以缓解这种状态:
l在单一线程中放置这些代码,可以使其它线程去执行任何我们所希望的功能而不是等待其它写线程执行完成,这正是MTTTY所具有的。
l使用COMMTIMEOUTS在一个暂停周期后来完成写的操作。
在这篇文章的后一段完全讨论
了“通信暂停”,这也是MTTTY所允许的。
l改变WaitForSingleObject的调用包括一个时时暂停值。
由于当旧的操作仍不能预见时这个程序执行另外一个操作,那么行的重叠结构和重叠时间需要被分配,这将导致许多问题。
在操作设计方面同使用“作业队列存储”相比这种记录保存就困难多了。
“作业队列存储”的方法被使用在MTTTY中。
注意:
同步功能中的暂停数值不是暂停通信,同步暂停引起WaitForSingleObject或WaitF
orMultipleObjects返回WAIT_TIMEOUT。
这不同与读写操作的暂停,这篇文章后面描述了通信暂停。
由于在上段摘录的代码中WaitForSingleObject功能使用了一个永久暂停,它相当于为fWait参数使用GetOverlappedResult返回真。
在下面这个简单的表格中使用了相同的代码。
BOOLWriteABuffer(char*lpBuf,DWORDdwToWrite)
{
OVERLAPPEDosWrite={0};
DWORDdwWritten;
BOOLfRes;
//CreatethiswritesOVERLAPPEDstructurehEvent.
osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(osWrite.hEvent==NULL)
//Errorcreatingoverlappedeventhandle.
returnFALSE;
//Issuewrite.
if(!
WriteFile(hComm,lpBuf,dwToWrite,&dwWritten,&osWrite)){
if(GetLastError()!
=ERROR_IO_PENDING){
//WriteFilefailed,butitisn’tdelayed.Reporterrorandabort.
fRes=FALSE;
}
else{
//Writeispending.
if(!
GetOverlappedResult(hComm,&osWrite,&dwWritten,TRUE))
fRes=FALSE;
else
//Writeoperationcompletedsuccessfully.
fRes=TRUE;
}
}
else
//WriteFilecompletedimmediately.
fRes=TRUE;
CloseHandle(osWrite.hEvent);
returnfRes;
}
GetOverlappedResult在等待一个重叠操作时并不总是最好的选择,举例来说,如果一
个应用程序需要依附于另外一个事件的处理,那么第一个代码段币第二个要好多了。
在等
待其它事件处理时WaitForSingle的调用是很容易转换成WaitForMultipleObjects。
这正是MTTTY程序本身所具有的。
在以前的重叠操作完成前,重叠I/O会产生一个普通错误。
如果一个重叠操作在先前的一个操作完成前执行,那么必须给他分配一个新的重叠结构,同时也为重叠结构的hEvent成员创建一个手动事件。
一旦重叠操作完成,重叠结构和它的事件变可以重新自由使用。
在串口通讯中唯一一个要被修改的重叠结构的成员就是hEvent,其它的重叠结构成员
应该初始化为0或者单独使用。
我们不需要为串口通讯设备更改其它重叠结构成员。
重叠结构中ReadFile和WriteFile的文件状态Offset和OffsetHigh必须随着程序更新,否则结果将不可预见。
这一规则在重叠结构的其它资源类型中应用,就像文件结构。
串口状态:
有两种方法取回端口状态,第一种是设置一个事件标志当期望事件发生时通知程序,
用SetCommMask设置事件标志,WaitCommEvent标明期望事件的发生。
这些功能与16位下SetCommEventMask和EnableCommNotification功能类似,区别在于Win32不发送WM_COMMNOTIFY信息。
事实上,WM_COMMNOTIFY信息甚至不是Win32下API的部分。
第二种方法就是周期性地调用一些不同的状态功能,轮流检测既非有效的或者不被推荐的。
通讯事件:
在串口通讯中通讯事件可能发生在任何时刻,在接收到通讯事件通知会按照以下两个
步骤:
l用SetCommMask设定预期事件的通知。
l用WaitCommEvent发出状态检查,状态检查可能是重叠或者非重叠的,正如读和写操作
注意:
上下文所提及的事件仅仅指通讯事件,它不作为任何一个同步的事务事件。
这儿有一个SetCommMask功能的例子:
DWORDdwStoredFlags;
dwStoredFlags=EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|\
EV_RLSD|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY;
if(!
SetCommMask(hComm,dwStoredFlags))
//errorsettingcommunicationsmask
在详细描述这一事件后,来看WaitCommEvent功能对发生事件的探测。
如果打开了非重叠操作端口,那么WaitCommEvent功能不包含重叠结构。
在一个事件发生前阻塞功能将组织调用线程,如果一个事件发生了,线程可能不确定的阻塞。
这儿有一个显示当一个端口为非重叠操作打开时如何去等待EV_RING事件:
DWORDdwCommEvent;
if(!
SetCommMask(hComm,EV_RING))
//Errorsettingcommunicationsmask
returnFALSE;
if(!
WaitCommEvent(hComm,&dwCommEvent,NULL))
//Anerroroccurredwaitingfortheevent.
returnFALSE;
else
//Eventhasoccurred.
returnTRUE;
注意:
微软Win32SDK基础文件和Windows95在EV_RING标志有相同的问题。
上面的代码在Windows95下不会返回结果因为EV_RING事件不对Windows95系统进行检测。
WindowsNT则完全保留EV_RING事件,请通过Win32SDK只是来获取更多的这方面的信息。
正如所说的,如果一个事件发生上面的代码将被永久
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Serial Communications in Win32中文版 Win32 中文版