基于VC++环境的串口通信

2014-04-16 11:52
科技视界 2014年13期
关键词:句柄缓冲区线程

李 林

(中国电子科技集团公司 第二十研究所,陕西 西安 710068)

0 引言

在导航设备及测试仪器中,显控计算机(一般都基于Windows平台)经常需要与下层DSP设备模块通过串口进行通信。串口通信方便易行,应用广泛。一般情况下,显控计算机通过RS232总线进行通信.RS232的通信方式是双工的。每次通信都是由显控计算机通过串口向DSP设备模块命令,DSP设备模块在接收到正确的命令后作出应答.在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序简单,但欠灵活.其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。本文使用API串口通信。串口的操作可以有两种操作方式:同步操作方式和重叠操作方式(又称为异步操作方式)。同步操作时,API函数会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而重叠操作方式,API函数会立即返回,操作在后台进行,避免线程的阻塞.无论哪种操作方式,一般都通过四个步骤来完成:(1)打开串口;(2)配置串口;(3)读写串口;(4)关闭串口。

1 打开串口

Win32系统把文件的概念进行了扩展.无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的原型为:

2 配置串口

在打开串口设备句柄后,常需要对串口进行一些初始化配置工作.这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。一般用CreateFile打开串口后,可以调用GetCommState函数来获取串口的初始配置.要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数设置串口。DCB结构包含了串口的各项参数设置,下面仅介绍几个该结构常用的变量。

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

在用ReadFile和WriteFile读写串行口时,需要考虑超时问题.超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile或WriteFile的操作仍然会结束。要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构,调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。读写串口的超时有两种:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共花费的最大时间,写操作只支持总超时,而读操作两种超时均支持。用COMMTIMEOUTS结构可以规定读写操作的超时,COMMTIMEOUTS结构的定义为:

COMMTIMEOUTS结构的成员都以毫秒为单位,总超时的计算公式是:

总超时=时间系数×要求读/写的字符数+时间常量

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

读总超时=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant

可以看出:间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时。如果 ReadTotalTimeoutMultiplier和 ReadTotalTimeoutConstant都为 0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且读时间系数和读时间常量都为0,那么在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。在用重叠方式读写串口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。

配置串口的示例:

3 读写串口

使用ReadFile和WriteFile读写串口,下面是两个函数的声明:BOOL ReadFile(HANDLE hFile,//串口的句柄

在用ReadFile和WriteFile读写串口时,既可以同步执行,也可以重叠执行。在同步执行时,函数直到操作完成后才返回。这意味着同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,这两个函数也会立即返回,费时的I/O操作在后台进行。ReadFile和WriteFile函数是同步还是异步由CreateFile函数决定,如果在调用CreateFile创建句柄时指定FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的操作就应该是重叠的;如果未指定重叠标志,则读写操作应该是同步的。ReadFile和WriteFile函数的同步或者异步应该和CreateFile函数相一致。ReadFile函数只要在串口输入缓冲区中读入指定数量的字符,就算完成操作。而WriteFile函数不但要把指定数量的字符拷入到输出缓冲区,而且要等这些字符从串行口送出去后才算完成操作。如果操作成功,这两个函数都返回TRUE。需要注意的是,当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING,这说明重叠操作还未完成。而较多使用异步重叠操作方式。有两种方法可以等待操作完成:一种方法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员;另一种方法是调用GetOverlappedResult函数等待,后面将演示说明。下面先简单介绍一下OVERLAPPED结构和GetOverlappedResult函数:OVERLAPPED结构OVERLAPPED结构包含了重叠I/O的一些信息,定义如下:

在使用ReadFile和WriteFile重叠操作时,线程需要创建OVERLAPPED结构以供这两个函数使用。线程通过OVERLAPPED结构获得当前的操作状态,该结构最重要的成员是hEvent.hEvent是读写事件。当串口使用异步通讯时,函数返回时操作可能还没有完成,程序可以通过检查该事件得知是否读写完毕。当调用ReadFile,WriteFile函数的时候,该成员会自动被置为无信号状态;当重叠操作完成后,该成员变量会自动被置为有信号状态。

该函数返回重叠操作的结果,用来判断异步操作是否完成,它是通过判断OVERLAPPED结构中的hEvent是否被置位来实现的。

异步读串口的示例:

该函数获得通信错误并报告串口的当前状态,同时,该函数清除串口的错误标志以便继续输入、输出操作。参数lpStat指向一个COMSTAT结构,该结构返回串口状态信息。COMSTAT结构COMSTAT结构包含串口的信息,结构定义如下:

这里只用到了cbInQue成员变量,该成员变量的值代表输入缓冲区的字节数。最后用PurgeComm函数清空串口的输入输出缓冲区。

异步写串口的示例:

4 关闭串口

利用API函数关闭串口非常简单,只需使用CreateFile函数返回的句柄作为参数调用CloseHandle即可:

5 结语

在具体开发过程中要用到多线程方式,让接收模块用一个守护线程来把串口实时的监控起来,当有数据收到时候就告知其他应用线程,这时还要用到事件类来维护应用线程和守护线程之间的同步通信方式。还有一个问题就是接收过程中时序不确定性,可能是由于对串口硬件本身通信机制没有彻底把握清楚,所以在接收函数中需要加适当的延时,这个延时的大小就需要建立一个辅助文件来打印下所接受的数据,进而随着时延变化观察分析所接收的数据是否符合预期后,确定相应时延。这些是应用串口通信过程中所得到的体会。

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

[2]张筠莉,刘书智.Visual C++实践与提高:串口应用与工程应用篇[M].北京:中国铁道出版社,2011.

[3]周韧研.Visual C++串口通信开发入门与编程实践[M].北京:电子工业出版社,2006.

[4]刘书智.Visual C++串口通信与工程应用实践[M].北京:中国铁道出版社,2007.

[5]谭思亮,邹超群.Visual C++串口通信工程开发实例导航[M].北京:人民邮电出版社,2009.

猜你喜欢
句柄缓冲区线程
嫩江重要省界缓冲区水质单因子评价法研究
浅谈linux多线程协作
关键链技术缓冲区的确定方法研究
基于上下文定界的Fork/Join并行性的并发程序可达性分析*
Linux线程实现技术研究
地理信息系统绘图缓冲区技术设计与实现
AVS标准中的视频码流缓冲区校验模型分析
么移动中间件线程池并发机制优化改进