Windows串口编程技术本讲重点提要本讲对Windows串口编程中涉及到的API函数和系统结构进行了较为详细的介绍,内容包括串口编程相关结构和API函数的介绍,以及串口通信超时处理等技术的介绍。并结合之前学习的Windows异步设备I/O技术等介绍一个串口通信类的实现。串口相关结构详解WindowsSDK为串口设备提供了以下几种相关的数据结构。1.设备属性结构。指示串口设备性能,该结构只允许应用程序获取,而不可以设置。设备属性结构的内容由串口设备驱动决定。结构名称:COMMPROP。2.设备控制块:DeviceControlBlock,DCB3.设备配置结构。用于控制设备的可配置属性,该结构允许设置和获取。结构名称:COMMCONFIG。该结构中也包含了DCB结构。4.设备操作相关结构,结构名称:COMMTIMEOUTS,用于设置串口操作的各种超时。该结构可以配置和获取。5.设备状态结构,用于获取串口运行状态。结构名称:COMMSTAT。串口相关结构详解对于串口相关结构的详细介绍,请参阅MSDN文档,这里我们仅对几个结构的关键域进行说明。串口设备通信中最重要的系统结构就是DCB(DeviceControlBlock,设备控制块)。用户需要根据硬件设备厂商提供的硬件数据信息来填充此结构,该结构中以下几个域一般需要填充,其它域参考硬件设备说明书:typedefstruct_DCB{DWORDDCBlength;//结构长度:36字节DWORDBaudRate;//波特率…BYTEByteSize;//数据位长度BYTEStopBits;//停止位位数…}DCB;串口相关结构详解COMMCONFIG结构:该结构对于不同厂商的设备可能会有所不同,根据设备说明书,如果需要对其进行填充,好的做法是首先调用GetCommConfig函数获取当前的COMMCONFIG结构,然后调用SetCommConfig函数对关心的域进行填充。相关的函数还有GetDefaultCommConfig和SetDefaultCommConfig,以及CommConfigDialog函数。关于结构COMMTIMEOUTS,我们将在后面的Time-Outs一节介绍。串口相关函数详解串口操作除了使用CreateFile,ReadFile,WriteFile等IO操作使用的函数之外,WindowsSDK还提供了一套专门用于设备通信的函数,这些函数可以用于串口通信,也根据设备的类型,也可以用于一些其它设备使用。这里我们仅介绍可以用于串口设备通信的函数。我们可以将其分为几类:操作类属性类配置类辅助类串口相关函数详解操作类对串口的操作除了通常的CreateFile,ReadFile,WriteFile等函数之外,Win32还提供了以下函数:BOOLTransmitCommChar(HANDLEhFile,//设备句柄charcChar//传送的字符);该函数向指定的串口设备传送一个特定的字符,该字符会在任何尚未被传送的数据之前传送。这个函数并非专为串口设备提供,比如你可以调用该函数向控制台传送一个中断符CTRL+C。BOOLPurgeComm(HANDLEhFile,//设备句柄DWORDdwFlags//actiontoperform);该函数清空设备的输入或输出缓存,或者终止一个未完成的读写操作。串口相关函数详解BOOLSetCommBreak(HANDLEhFile//设备句柄);该函数将指定通信设备的数据传输挂起,将数据线置于BREAK状态,直到ClearCommBreak函数被调用。这里BREAK并不是向串口设备发送的特殊的中断字符,而是通信线路的电气状态。BOOLClearCommBreak(HANDLEhFile//设备句柄);该函数恢复指定通信设备的挂起的数据传输过程,并将数据线置于NONBREAK状态,参见SetCommBreak函数说明。BOOLClearCommError(HANDLEhFile,//设备句柄LPDWORDlpErrors,//错误码LPCOMSTATlpStat//通信状态);串口相关函数详解当通信发生错误时,该函数获取通信的错误信息以及报告设备的通信状态。并清除设备的错误标志使得后续操作可以进行。BOOLEscapeCommFunction(HANDLEhFile,//设备句柄DWORDdwFunc//执行函数);该函数允许指定的通信设备执行dwFunc参数指定的一个扩展的功能函数,dwFunc可取值如下:•CLRDTR:清除DTR(data-terminal-ready)信号。•CLRRTS:清除RTS(request-to-send)信号。•SETDTR:发送DTR信号。•SETRTS:发送RTS信号。•SETXOFF:启动数据传输,效仿收到XOFF字符的情况。•SETXON:启动数据传输,效仿收到XON字符的情况。•SETBREAK:等同于调用SetCommBreak函数。•CLRBREAK:等同于调用ClearCommBreak函数。串口相关函数详解BOOLWaitCommEvent(HANDLEhFile,//设备句柄LPDWORDlpEvtMask,//事件类型LPOVERLAPPEDlpOverlapped//overlapped结构指针);根据参数lpOverlapped是否为空,该函数以同步或异步IO的方式等待关心事件的发生,关心事件的类型由参数lpEvtMask指定。lpEvtMask可取值如下:EV_BREAK检测到一个BREAK信号.EV_CTSCTS(clear-to-send,允许发送)信号状态改变.EV_DSRDSR(data-set-ready,数据设置就绪)信号状态改变.EV_ERR错误发生.EV_RING检测到振铃信号.EV_RLSDRLSD(receive-line-signal-detect)信号状态改变.EV_RXCHAR输入缓存接收到数据.EV_RXFLAG输入缓存接收到事件字符.EV_TXEMPTY输出缓存中的最后一个数据被发送.串口相关函数详解配置类BOOLSetCommConfig(HANDLEhCommDev,LPCOMMCONFIGlpCC,DWORDdwSize);BOOLSetCommMask(HANDLEhFile,DWORDdwEvtMask);BOOLSetCommState(HANDLEhFile,LPDCBlpDCB);BOOLSetCommTimeouts(HANDLEhFile,LPCOMMTIMEOUTSlpCommTimeouts);BOOLSetDefaultCommConfig(LPCTSTRlpszName,LPCOMMCONFIGlpCC,DWORDdwSize);BOOLSetupComm(HANDLEhFile,DWORDdwInQueue,DWORDdwOutQueue);串口相关函数详解配置类函数根据输入参数/结构对串口进行配置,每一个Set**函数对应有一个Get**函数用于获取当前配置。详细介绍请参阅MSDN文档,这里我们仅对几个关键函数进行说明。上面介绍的Set**函数中,除了SetCommMask和SetupComm外,入口参数中都有对应的配置结构,参阅相关结构的说明可以知道该函数的作用。SetCommMask函数用来指定关心的事件,其参数dwEvtMask与操作类函数中WaitCommEvent的取值一致。下面说明SetupComm函数。串口相关函数详解BOOLSetupComm(HANDLEhFile,//设备句柄DWORDdwInQueue,//推荐的输入缓存大小DWORDdwOutQueue//推荐的输出缓存大小);这里dwInQueue和dwOutQueue参数用来设置推荐的输入输出缓存大小,按字节计算。如果不设定,将使用缺省值。一种合适的缓存大小值为比设备通信帧的大小略大,比如对于基于以太网的通信设备(如以太网卡),我们知道一个以太帧的大小通常为1514字节,那么推荐值采用1600是比较合适的。串口相关函数详解这里我们将该值称为“推荐的”,其含义为:我们只是通过该函数通知设备的驱动程序,推荐使用这个值,但驱动程序仍然可以根据实际情况采用不同的输入输出缓存机制,只要能够提供合理的性能并且保证当数据长度超出缓冲区长度时数据不被丢失就可以了(极端情况除外)。比如说,驱动程序甚至可以不分配输入输出缓冲区,只要系统的其它模块提供等效的机制保证通信不出错就可以了。串口相关函数详解函数SetCommState函数根据入口参数中的DCB结构指针对串口设置进行调整。一般来讲,我们仅需对DCB结构中的少数几个域进行调整,因此,不应该直接填充该结构,好的做法是首先调用GetCommState函数获取当前的DCB结构,然后对关心的域进行调整。另外GetCommProperties函数可以帮助获取设备的性能参数,比如设备支持的最大波特率值:dwMaxBaud。串口相关函数详解属性类BOOLGetCommProperties(HANDLEhFile,//设备句柄LPCOMMPROPlpCommProp//COMMPROP结构指针);该函数获取指定通信设备的性能参数,并填充lpCommProp参数指向的COMMPROP结构。辅助类BOOLBuildCommDCB(LPCTSTRlpDef,LPDCBlpDCB);BOOLBuildCommDCBAndTimeouts(LPCTSTRlpDef,LPDCBlpDCB,LPCOMMTIMEOUTSlpCommTimeouts);这两个辅助类函数根据入口参数中的lpDef字符串构建相关的串口配置结构。更详细的说明参阅MSDN文档。串口相关函数详解BOOLCommConfigDialog(LPCTSTRlpszName,//设备名称HWNDhWnd,//窗口句柄LPCOMMCONFIGlpCC//配置信息);调用该函数将弹出一个指定的设备驱动程序支持的设备配置对话框,但该函数必须有硬件设备供应商提供的DLL文件支持。该函数的入口参数lpCC为一个指向COMMCONFIG结构的指针,前面我们知道,对于不同的设备提供商,COMMCONFIG结构也可能会有差异。串口相关函数详解其它可用函数使用前面提到的PurgeComm函数可以清空指定通信设备的输入输出缓冲区,并且终止正在进行的读写操作,但输出缓冲区中被删除的数据将不被发送。如果想清空输出缓冲区的同时保证数据被发送,可以使用FlushFileBuffers函数,该函数入口参数为通信设备句柄,调用该函数将清空设备的输出缓冲区并确保数据被发送,对于文件设备的情况,是将缓冲区中的数据写入文件。但要注意的是,该函数的执行不依赖TimeOut值,而是依赖于设备流控制。串行通信超时(Time-Outs)处理串行通信超时(Time-Outs)处理:COMMTIMEOUTS结构用来设置串口超时参数,当应用程序打开一个串口设备时,系统设置设备的超时参数为设备最后一次使用时的数值,如果设备未被打开过,则使用缺省值。合理的做法是:每次打开设备都应该调用SetCommTimeouts函数对超时参数进行设置,查看当前超时参数设置,调用GetCommTimeouts函数。串行通信超时(Time-Outs)处理Windows环境下的串行通信支持两种超时处理:Intervaltime-outs和Totaltime-outs。Intervaltime-outs我们可以称之为字符间隔超时,当接收两个字符的间隔时间超过指定的毫秒数时,超时发生。Intervaltime-outs每当收到一个字符时开始计时。Totaltime-outs我们可以称之为操作总超时,当一个读写操作消耗的时间超过指定的毫秒数时,超时发生。Totaltime-outs当一个读写操作开始时开始计时。写操作仅支持