win32串口编程文档格式.docx
- 文档编号:18657347
- 上传时间:2022-12-31
- 格式:DOCX
- 页数:21
- 大小:27.27KB
win32串口编程文档格式.docx
《win32串口编程文档格式.docx》由会员分享,可在线阅读,更多相关《win32串口编程文档格式.docx(21页珍藏版)》请在冰豆网上搜索。
去掉参数中的FILE_FLAG_OVERLAPPED就是非重叠操作方式了。
用CreateFile打开通信端口时,有下列限制:
fdwShareMode必须是0。
通信端口不能像文件那样被共享。
要共享通信端口,需要使用句柄继承或者复制操作。
fdwCreate必须指定OPEN_EXISTING标志。
hTemplateFile参数必须是NULL。
端口名通常是COM1、COM2、COM3和COM4。
Win32API不提供确定系统中有哪些端口可用的机制。
WindowsNT和Windows95跟踪系统已安装端口的方法是不同的,所以不太可能提供兼容的确定可用端口的方法。
某些系统可能有多于4个端口,而传统的通信端口最大个数是4。
硬件厂商和串口驱动编写者可以自由地为端口命名。
所以,程序最好可以让用户指定要使用的端口名字。
如果端口不存在,则试图打开端口时会返回ERROR_FILE_NOT_FOUND错误,这时应该提示用户端口不可用。
2读写操作
通信端口的读写操作与文件I/O操作非常相似,它们使用同样的函数。
Win32的I/O操作可分为两种:
重叠(overlapped)的和非重叠的(nonoverlapped)。
平台SDK文档分别使用异步(asynchronous)和同步(synchronous)来表示这两种I/O方式。
很多开发者都熟悉非重叠I/O,因为它就是传统的I/O方式:
函数返回时,所请求的操作已经完成。
然而在重叠I/O的情况下,系统则可能在操作还没有完成的情形下立即返回,随后才通知调用者操作完成。
程序可以在发起I/O请求和请求被完成之间进行一些后台工作。
2.1非重叠I/O
非重叠I/O的工作方式很简单:
I/O操作进行时,调用线程被阻塞;
操作完成后,函数返回,调用线程可以继续执行。
在多线程应用中,这种I/O方式很有用:
一个线程阻塞在某I/O操作上时,其他线程可以继续工作。
应用程序应该保证对端口的串行访问。
某个线程阻塞在等待某I/O操作上时,其他线程后续的通信API调用也都将阻塞。
比如说,一个线程在等待ReadFile调用返回时,另一个线程的WriteFile函数调用将阻塞。
在选择使用非重叠还是重叠方式时,可移植性是要考虑的因素之一。
有时候重叠操作并不是好的选择,因为很多操作系统不支持它;
然而很多操作系统都支持某种形式的多线程。
所以从兼容性方面考虑,多线程非重叠I/O可能是最好的选择。
2.2重叠I/O
重叠I/O不像非重叠I/O那样简单易懂,但却灵活高效。
使用重叠方式打开的端口允许多个线程同时进行I/O操作,并且在操作进行期间可以进行其他的工作。
此外,重叠操作的行为方式还允许单个线程提交多个不同的请求,然后在操作进行期间进行其他后台工作。
在单线程和多线程应用中,都必须在提交I/O请求和处理操作结果间进行一些同步操作。
线程可能需要在操作结果可用前阻塞;
当然也可以进行其他工作。
如果没有其他需要进行的工作,则重叠I/O的优点是更好的用户响应性能。
MTTTY使用了重叠I/O。
它创建用于读取数据和监测端口状态的线程,并且还定时进行一些后台工作;
此外它还另外创建一个线程用于写入数据。
重叠I/O操作分为两个部分:
创建I/O操作和检测操作完成。
创建I/O操作涉及到建立OVERLAPPED结构体、创建用于同步的手动复位事件、调用恰当的函数(ReadFile或者WriteFile)。
I/O操作可能立即完成,也可能不能立即完成,不能认为一个重叠I/O操作请求总是生成一个重叠操作。
如果操作立即完成,程序应该可以继续进行通常的处理。
检测操作完成涉及到等待事件句柄、检查操作完成结果、处理数据。
与重叠I/O相关的工作更多的原因是有更多的失败点。
非重叠操作中,简单地通过函数返回值判断操作是否失败;
而重叠操作中,则可能在创建操作请求时失败,或者操作阻塞期间失败,也可能是操作超时,或者是等待操作完成信号超时。
2.2.1读操作
下面的代码片段展示了提交重叠的读操作请求的方法。
注意,如果ReadFile返回TRUE,调用了一个函数处理数据。
代码定义了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来取得操作结果,它返回TRUE表示操作成功完成;
FALSE表示有错误发生,用GetLastError可以取得具体的错误码。
也可以用GetOverlappedResult来检测操作完成:
GetOverlappedResult返回FALSE,GetLastError返回ERROR_IO_INCOMPLETE表示操作进行中。
如果对bWait参数传入TRUE,则效果就是重叠操作变成了非重叠的,直到操作完成,函数才返回。
下面的代码片段展示了一种检测重叠读取操作完成的方法。
注意fWaitingOnRead标志的使用,它是检测代码的控制入口,只有在某操作进行中时,才应该调用检测代码。
#defineREAD_TIMEOUT500//milliseconds
DWORDdwRes;
if(fWaitingOnRead){
dwRes=WaitForSingleObject(osReader.hEvent,READ_TIMEOUT);
switch(dwRes)
{
//Readcompleted.
caseWAIT_OBJECT_0:
GetOverlappedResult(hComm,&
osReader,&
dwRead,FALSE))
//Readcompletedsuccessfully.
//Resetflagsothatanotheropertioncanbeissued.
fWaitingOnRead=FALSE;
break;
caseWAIT_TIMEOUT:
//Operationisn'
tcompleteyet.fWaitingOnReadflagisn'
t
//changedsinceI'
llloopbackaround,andIdon'
twant
//toissueanotherreaduntilthefirstonefinishes.
//
//Thisisagoodtimetodosomebackgroundwork.
default:
//ErrorintheWaitForSingleObject;
//ThisindicatesaproblemwiththeOVERLAPPEDstructure'
s
//eventhandle.
2.2.2写入操作
写入操作跟读取操作非常相似。
下面的代码片段展示了如何提交写入操作,并等待操作完成。
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.
WriteFile(hComm,lpBuf,dwToWrite,&
dwWritten,&
osWrite)){
=ERROR_IO_PENDING){
//WriteFilefailed,butisn'
tdelayed.Reporterrorandabort.
fRes=FALSE;
//Writeispending.
dwRes=WaitForSingleObject(osWrite.hEvent,INFINITE);
//OVERLAPPEDstructure'
seventhasbeensignaled.
osWrite,&
dwWritten,FALSE))
//Writeoperationcompletedsuccessfully.
fRes=TRUE;
//AnerrorhasoccurredinWaitForSingleObject.
//Thisusuallyindicatesaproblemwiththe
seventhandle.
//WriteFilecompletedimmediately.
CloseHandle(osWrite.hEvent);
returnfRes;
注意,上面的代码使用WaitForSingleObject时,超时值是INFINITE,这使得函数无限等待直到操作完成。
这可能让调用线程似乎是被挂起了;
而实际上只是写入操作需要较长的时间,或者流控制阻塞了传输操作。
下文将讨论的状态检查可以检测到这种情况,但它也不会让WaitForSingleObject返回。
有三种方法可以克服此问题:
把代码放在单独的线程中。
这样写入线程在等待写操作完成时,其他线程可以进行任何所需的操作。
MTTTY就是这么做的。
使用COMMTIMEOUTS使得写操作在经过一个超时值指定的时间后完成。
本文后面的“通信超时”节将详细讨论它。
MTTTY也可以使用这种方法。
修改WaitForSingleObject调用,使用超时值。
这样会更麻烦:
如果原来的操作仍在进行中,程序提交另一个操作请求,则需要分配新的OVERLAPPED结构和重叠事件。
这种记录跟踪保持是很困难的,尤其是与“工作队列”相比较时。
MTTTY使用了工作队列。
上面代码中的WaitForSingleObject使用了INFINITE作为超时值,其效果等同于使用TRUE作为GetOverlappedResult的fWait参数。
下面是等效的更简洁的代码:
//CreatethiswritesOVERLAPPEDstructurehEvent.
//Errorcreatingoverlappedeventhandle.
//WriteFilefailed,butitisn'
else{
dwWritten,TRUE))
GetOverlappedResult并不总是等待重叠操作完成的最好方法。
比如说,如果应用需要同时等待另一个事件句柄,则第一个代码片段模型比第二个更好,因为可以很容易地用WaitForMultipleObjects替换WaitForSingleObject,来等待更多的句柄。
在前一个重叠操作完成前重用OVERLAPPED结构是重叠I/O编程中常现的一个错误。
如果要在前一个重叠操作完成前提交新的重叠操作请求,则需要分配新的OVERLAPPED结构,其hEvent字段也应该包含新的手动复位事件句柄。
只有在重叠操作完成后,OVERLAPPED结构和其事件句柄才可以被重用。
串口通信中使用OVERLAPPED结构时只需要修改hEvent字段,其他字段只需要初始化为零。
3串口状态
有两种获取通信端口状态的方法。
第一种方法是设置事件掩码,当指定事件发生时应用程序会收到通知。
SetCommMask函数用于设置事件掩码,WaitCommEvent用于等待指定的事件发生。
它们与16位Windows中的SetCommEventMask和EnableCommNotification类似,只是它们不发送WM_COMMNOTIFY消息。
第二种方法是不时地调用另一些状态函数来获取通信端口的状态。
当然,轮询是低效的,不建议使用。
3.1通信事件
通信事件在使用通信端口时可能随时发生。
接收通信事件需要两个步骤:
用SetCommMask设定需要接收通知的事件
用WaitCommEvent提交状态检查请求,请求可以是重叠的或者非重叠的,与读写操作一样。
下面是使用SetCommMask的示例:
DWORDdwStoredFlags;
dwStoredFlags=EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|
EV_RLSD|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY;
SetCommMask(hComm,dwStoredFlags))
//errorsettingcommunicationsmask
下表描述了每种事件类型。
事件标志
描述
EV_BREAK
检测到输入中的break
EV_CTS
CTS(ClearToSend)信号状态改变。
要取得CTS线路状态,应使用GetCommModemStatus函数。
EV_DSR
DSR(DataSetReady)信号状态改变。
要取得DSR线路状态,应使用GetCommModemStatus函数。
EV_ERR
某线路状态错误发生。
线路状态错误包括CE_FRAME、CE_OVERRUN和CE_RXPARITY。
要取得具体错误种类,需调用ClearCommError函数。
EV_RING
检测到振铃指示
EV_RLSD
RLSD(ReceiveLineSignalDetect)信号状态改变。
要取得RLSD线路状态,需调用GetCommModemStatus函数。
注意,RLSD通常被称作CD(carrierdetect)。
EV_RXCHAR
接收到一个字符并且已放入输入缓冲区。
请参考下面的“警告”节对此标志的详细讨论。
EV_RXFLAG
接收到一个事件字符并且已放入输入缓冲区。
事件字符由下文讨论的DCB结构EvtChar字段指定。
下面的“警告”节也讨论了这个标志。
EV_TXEMPTY
输出缓冲区中最后一个字符被发送到串口设备了。
如果使用硬件缓冲区,此标志仅表示所有数据已经发送到硬件了。
如果不与设备驱动交互,是无法确定硬件缓冲区空的。
指定事件掩码后,使用WaitCommEvent函数检测事件发生。
如果以非重叠方式打开端口,则WaitCommEvent不需要OVERLAPPED结构体,函数阻塞调用线程直到某事件发生。
如果没有事件发生,调用线程将无限阻塞。
下面的代码片段展示了如何在以非重叠方式打开的端口上等待EV_RING事件。
DWORDdwCommEvent;
SetCommMask(hComm,EV_RING))
//Errorsettingcommunicationsmask
WaitCommEvent(hComm,&
dwCommEvent,NULL))
//Anerroroccurredwaitingfortheevent.
//Eventhasoccurred.
returnTRUE;
如果没有事件发生,上面的代码将无限阻塞调用线程。
解决方法是以重叠方式打开端口,用下面的方式等待状态事件:
#defineSTATUS_CHECK_TIMEOUT500//Milliseconds
DWORDdwStoredFlags;
BOOLfWaitingOnStat=FALSE;
OVERLAPPEDosStatus={0};
dwStoredFlags=EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|\
SetCommMask(comHandle,dwStoredFlags))
//errorsettingcommunicationsmask;
abort
return0;
osStatus.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(osStatus.hEvent==NULL)
//errorcreatingevent;
for(;
;
){
//Issueastatuseventcheckifonehasn'
tbeenissuedalready.
fWaitingOnStat){
dwCommEvent,&
osStatus)){
if(GetLastError()==ERROR_IO_PENDING)
bWaitingOnStatusHandle=TRUE;
//errorinWaitCommEvent;
//WaitCo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- win32 串口 编程