首页 > 范文大全 > 正文

串行通信与重叠I/O

开篇:润墨网以专业的文秘视角,为您筛选了一篇串行通信与重叠I/O范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!

摘要:串行通讯方便易行,在军内外应用广泛。文章结合硬件详细介绍了在Windows环境下使用Win API实现异步串行通讯的方法。

关键词:串行通信;RS232;重叠;I/O;Win API

中图分类号:TN914文献标识码:A文章编号:1007-9599 (2010) 06-0000-02

Serial Communication and Overlapping I/O

Yu Lu,Li Qing

(PLA 91550 Troop,Dalian116023,China)

Abstract:The serial communication to facilitate easy,widely used in both military and civilian.In combination with the hardware described in detail in the Windows environment,use the Win API for asynchronous serial communication method.

Keywords:Serial communication;RS232;Overlap;I/O;Win API

一、前言

串行通讯在通讯领域被广泛应用,标准的RS232接口已成为计算机、计算机外设、交换机和许多通讯设备的标准接口。微机与微机、微机与外设、微机与程控交换机等都可以通过RS232接口进行方便的连接,以实现控制外设和传输数据等目的。

在Windows应用程序的开发中,我们常常需要面临与数据源设备通信的问题。笔者在实际工作中积累了一些经验,现结合硬件、软件,及需要注意的要点作一番探讨。希望对各位需要编写串口通信程序的朋友有一些帮助。

二、RS232串口标准

EIA-RS-232是美国电子工业协会正式公布的串行总线标准,也是目前最常用的串行接口标准。该标准规定:直接连接的最大物理距离为15m,通讯速率低于20kbps。

由于RS232并未定义连接器的物理特性,因此,出现了DB-25、DB-15和DB-9各种类型的连接器,其引脚的定义也各不相同。表1介绍了其中两种连接器(DB-25,DB-9)。

RS232标准接口有25条线,4条数据线、11条控制线、3条定时线、7条备用和未定义线,但常用的只有9根。

目前较为常用9针串口和25针串口,当通信距离较近时,可以用电缆线直接连接,若距离较远,须附加Modem。最为简单且常用的是三线制接法,即地、接收数据和发送数据三脚相连。表2列举了RS232串口通信接线方法。

EIA-RS-232对电气特性、逻辑电平和各种信号线功能都作了规定。

在TxD和RxD上:

逻辑1(MARK)=-3V~-15V。

逻辑0(SPACE)=+3V~+15V。

在RTS、CTS、DSR、DTR和DCD等控制线上:

信号有效:(接通,ON状态,正电压)=+3V~+15V。

信号无效:(断开,OFF状态,负电压)=-3V~-15V。

三、Win32串口应用程序

(一)打开串口

Win32系统把文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的声明为:HANDLE CreateFile(LPCTSTR lpFileName,//文件名DWORD dwDesiredAccess,//访问模式DWORD dwShareMode,//共享模式LPSECURITY_ATTRIBUTES lpSecurityAttributes,//通常为NULL

DWORD dwCreationDistribution,//创建方式

DWORD dwFlagsAndAttributes,//文件属性和标志

HANDLE hTemplateFile // 临时文件的句柄,通常为NULL

);

如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。

(二)串口配置和串口属性

在打开通信设备句柄后,常常需要对串口进行一些初始化工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。

调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。一般在用CreateFile打开串口后,可以调用GetCommState函数来获取串口的初始配置。要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串口。

除了在DCB中的设置外,程序一般还需要设置I/O缓冲区的大小和超时。Windows用I/O缓冲区来暂存串口输入和输出的数据,如果通信的速率较高,则应该设置较大的缓冲区。调用SetupComm函数可以设置串口的输入和输出缓冲区的大小。

(三)串口读写

在用ReadFile和WriteFile读写串口时,既可以同步执行,也可以重叠(异步)执行。在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。“重叠”一词的含义就在于此。

ReadFile函数只要在串口输入缓冲区中读入指定数量的字符,就算完成操作。而WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串口送出去后才算完成操作。

ReadFile和WriteFile函数是否为执行重叠操作是由CreateFile函数决定的。如果在调用CreateFile创建句柄时指定了FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的读写操作就是重叠的,如果未指定重叠标志,则读写操作是同步的。

函数ReadFile和WriteFile的参数和返回值很相似。这里仅列出ReadFile函数的声明:

BOOL ReadFile(

HANDLE hFile,//文件句柄

LPVOID lpBuffer,//读缓冲区

DWORD nNumberOfBytesToRead,//要求读入的字节数

LPDWORD lpNumberOfBytesRead,//实际读入的字节数

LPOVERLAPPED lpOverlapped//指向一个OVERLAPPED结构

);//若返回TRUE则表明操作成功

需要注意的是如果该函数因为超时而返回,那么返回值是TRUE。参数lpOverlapped在重叠操作时应该指向一个OVERLAPPED结构,如果该参数为NULL,那么函数将进行同步操作,而不管句柄是否是由FILE_FLAG_OVERLAPPED标志建立的。

当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。

在使用重叠I/O时,线程需要创建OVERLAPPED结构以供读写函数使用。OVERLAPPED结构最重要的成员是hEvent,hEvent是一个事件对象句柄,线程应该用CreateEvent函数为hEvent成员创建一个手工重置事件,hEvent成员将作为线程的同步对象使用。如果读写函数未完成操作就返回,就那么把hEvent成员设置成无信号的。操作完成后(包括超时),hEvent会变成有信号的。

如果GetLastError函数返回ERROR_IO_PENDING,则说明重叠操作还为完成,线程可以等待操作完成。有两种等待办法:一种办法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员,可以规定等待的时间,在等待函数返回后,调用GetOverlappedResult。另一种办法是调用GetOverlappedResult函数等待,如果指定该函数的bWait参数为TRUE,那么该函数将等待OVERLAPPED结构的hEvent事件。GetOverlappedResult可以返回一个OVERLAPPED结构来报告包括实际传输字节在内的重叠操作结果。

如果规定了读/写操作的超时,那么当超过规定时间后,hEvent成员会变成有信号的。因此,在超时发生后,WaitForSingleObject和GetOverlappedResult都会结束等待。WaitForSingleObject的dwMilliseconds参数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值。注意GetOverlappedResult不能设置等待的时限,因此如果hEvent成员无信号,则该函数将一直等待下去。

在调用ReadFile和WriteFile之前,线程应该调用ClearCommError函数清除错误标志。该函数负责报告指定的错误和设备的当前状态。

调用PurgeComm函数可以终止正在进行的读写操作,该函数还会清除输入或输出缓冲区中的内容。

(四)超时设置

在用ReadFile和WriteFile读写串口时,需要考虑超时问题。如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。

有两种超时:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共花费的最大时间。写操作只支持总超时,而读操作两种超时均支持。用COMMTIMEOUTS结构可以规定读/写操作的超时,该结构的定义为:

typedef struct_COMMTIMEOUTS {

DWORD ReadIntervalTimeout;//读间隔超时

DWORD ReadTotalTimeoutMultiplier;//读时间系数

DWORD ReadTotalTimeoutConstant;//读时间常量

DWORD WriteTotalTimeoutMultiplier;//写时间系数

DWORD WriteTotalTimeoutConstant;//写时间常量

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

COMMTIMEOUTS结构的成员都以毫秒为单位。总超时的计算公式是:总超时=时间系数×要求读/写的字符数+时间常量

例如,如果要读入10个字符,那么读操作的总超时的计算公式为:读总超时=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant

可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。

如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且两个读总超时为0,那么在读一次输入缓冲区中的内容后读操作就立即完成,而不管是否读入了要求的字符。

在用重叠方式读写串口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。

四、结束语

以上给出了用Win32 API设计串行通信的基本思路,这个重叠(异步)I/O操作的串行通信程序,曾多次应用于大型任务,表现出良好的性能。在实际应用中,可以以此为模型稍加改造,设计出满足需要的各种串行通信程序。

参考文献:

[1]李现勇.Visual C++串口通信技术与工程实践[M].人民邮电出版社,2004,7

[2]MSDN Library,Microsoft corp,1999