Win32操作系统中,在设置串口是有许多函数还会用到如设备控制DCB以及超市控制等结构,还有通信错误、通信状态以及通信事件等信息。
1、DCB(Device Control Block)结构
在打开通信设备句柄后,常常需要对串口进行一些初始化工作,这需要通过一个DCB结构来进行,DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。
调用GetCommState函数可以获得串口的设置,该函数把当前配置填充到一个DCB结构。一般在用CreateFile打开串口后,可以调用GetCommState函数来获取串行口的初始配置,若要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口。
typedef struct _DCB {
DWORDDCBlength;//DCB快大小
DWORDBaudRate;//当前波特率
DWORD fBinary:1;//二进制模式,不检测EOF
DWORDfParity:1;//允许奇偶校验
DWORDfOutxCtsFlow:1;//CTS输出流控制
DWORDfOutxDsrFlow:1;//DSR输出流控制
DWORDfDtrControl:2;//DTR流控制类型
DWORDfDsrSensitivity:1;//对DSR信号线是否敏感
DWORDfTXContinueOnXoff:1;//XOFF continues TX
DWORDfOutX:1;
DWORDfInX:1;
DWORDfErrorChar:1;
DWORDfNull:1;
DWORDfRtsControl:2;
DWORDfAbortOnError:1;
DWORDfDummy2:17;
WORDwReserved;
WORDXonLim;
WORDXoffLim;
BYTEByteSize;
BYTEParity;
BYTEStopBits;
charXonChar;
charXoffChar;
charErrorChar;
charEofChar;
charEvtChar;
WORDwReserved1;
} DCB, *LPDCB;
具体的配置看说明
2、超时设置COMMTIMEOUTS结构
typedef struct _COMMTIMEOUTS
{
DWORD ReadIntervalTimeout; //读间隔超时
DWORD ReadTotalTimeoutMultiplier; //读事件系数
DWORD ReadTotalTimeoutConstant; //读时间常量
DWORD WriteTotalTimeoutMultiplier; //读时间系数
DWORD WriteTotalTimeoutConstant; //读时间常量
} COMMTIMEOUTS, *LPCOMMTIMEOUTS
在用ReadFile和WriteFile读写串行口时,需要考虑超时问题,如果在指定的时间内没有读出或写入指定数量的字节,那么ReadFile和WriteFile的操作就会结束,要查询当前的超市设置应调用GetCommTimeouts函数,该函数会填充COMMTIMEOUTS结构,调用SetCommTimeouts函数可以用某一个COMMTIMEOUTS结构的内容来设置超时。
1、ReadIntervalTimeout读时间间隔超时
以毫秒为单位设置通信线路上两个字符到达之间最大时间间隔,在ReadFile操作期间,从接收到第一个字符开始计时,如果任意两个字符达到之间的时间间隔超过这个最大值,则ReadFile操作完成,返回缓冲数据,如果改制被置位0,则不实用时间间隔超时。
2、ReadTotalTimeoutMultiplier读时间系数
以毫秒为单位设置一个用来计算读操作总超时时间的时间系数,改时间系数乘要求读出的字节数。
3、ReadTotalTimeoutConstant读时间常量
以毫秒为单位设置一个用来计算读操作总超时时间的时间常量。
4、WriteTotalTimeoutMultiplier写时间系数
以毫秒为单位设置一个用来计算写操作总超时时间的时间系数,该时间系数乘要求写入的字节数。
5、WriteTotalTimeConstatnt写时间常量
以毫秒为单位设置一个用来计算读操作总超时的时间常量。
总超时时间=读/写时间系数*要求度/写的字节数+读/写时间常量。
超时有间隔超时和总超时两种类型,间隔超时是指在接受时两个字符之间的最大时延,用于从串口读取数据,当接受一个字节时,通信驱动程序启动一个内部定时器开始计时,在下一个字节到达之前,如果定时时间超过了间隔时间,读操作就会被放弃,总超时是指读写操作总共话费的最大时间,写操作只支持总超时,而读操作对两种超时均支持。
二、OVERLAPPED异步I/O重叠结构
在用ReadFile和WriteFile读写串口数据时,既可以同步执行,也可以重叠执行,在同步执行时,韩式直到操作完成后才返回,这意味着在同步执行时线程会被阻塞,从而导致效率下降,在重叠执行时,即使操作还未完成,调用的函数也会立即返回,费时的IO操作在后台运行,这样线程可以干别的事情,在异步设置时,会用到OVERLAPPED结构。
typedef struct _OVERLAPPED
{
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
在串口通信编程过程中,与重叠IO操作关系最密切的是串口的读写操作,涉及的两个函数是ReadFile和WriteFile,当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。
在使用重叠IO时,线程需要创建OVERLAPPED结构以供读写函数使用,OVERLAPPED节诶狗最重要的成员是hEvent,hEvent是一个事件对象句柄,线程应该用CreateEvent函数为和Event成员创建一个手工重置事件,hEvent成员将作为线程的同步对象使用,如果读写函数未完成擦偶走就返回,那么就把和Event成员设置成无信号,操作完成后(包括超时),和Event会变成有信号的。
如果GetLastError函数返回ERROR_IO_PENDING,则说明重叠操作还未完成,线程等待操作完成,有两种等待办法:一种办法就是用像WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的和Event成员,可以规定等待的时间,在等待函数返回后,调用GetOverlappedResult,另一种办法是调用GetOverLappedResult函数等待,如果指定该函数bWait参数为TRUE,那么该函数将等待OVERLAPPED节诶狗的hEvent事件,GetOverlappedResult可以返回一个OVERLAPPED结构来报告包括实际传输字节在内的重叠操作的结果。
如果规定了读写操作的超时,那么当超过规定时间后,hEvent成员会变成有信号的,因此,在超时发生后,WaitForSingleObject和GetOverlappedResult都会结束等待,WaitForSingleObject的dwMilliseconds参数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值,注意GetOverlappedResult不能设置等待的时限,因此,如果hEvent成员无信号,则该函数将一直等待下去。
以以上可以看出,异步IO操作有两种方法来获取结果
l 利用GetOverlappedResult函数
l 利用WaitForSingleObject等事件处理函数
l 以上两种结合起来使用