串口异步读写
来源:互联网 发布:淘宝发布宝贝不同规格 编辑:程序博客网 时间:2024/05/16 14:41
串口异步读写
最近尝试写了一个串口读写的程序,学习并复习了一些知识点。本文首先讲异步读写注意点,然后讲串口的注意点。因为有些问题没有深入研究下去,所以本文也仅仅当做一个笔记。
1. 文件指针
我们使用ReadFile和WriteFile来进行读写,这两个API是用来读写文件的,在同步读写中,有一个文件指针的概念,但是在异步读写中,系统会忽略文件指针。
OVERLAPPED的结构如下:
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
上述结构的5个成员,其中三个成员Offset、OffsetHigh和hEvent必须在调用ReadFile和WriteFile之前初始化,hEvent是一个通知事件,而Offset和OffsetHigh就是用来代替同步读写中文件指针的——用来指定本次读写的开始的位置。
非文件设备会忽略Offset和OffsetHigh——我们必须将这两个成员都初始化为0,否则I/O请求会失败,这时调用GetLastError会返回ERROR_INVALID_PARAMETER。
Internal和InternalHigh原本设计时是内部使用的,但是后来用途变了:Internal用作返回状态值;InternalHigh返回读取字符数。这些在后面有进一步说明。
2. 异步读写中实际读写的字符数
以ReadFile为例,函数定义如下:
BOOL WINAPI ReadFile(
_In_ HANDLE hFile,
_Out_ LPVOID lpBuffer,
_In_ DWORD nNumberOfBytesToRead,
_Out_opt_ LPDWORD lpNumberOfBytesRead,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
在同步读时,lpNumberOfBytesRead返回实际读取的数据长度,但是在异步读时,lpOverlapped不为NULL,此时lpNumberOfBytesRead可以设为NULL值。
ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
返回数据的长度在lpOverlapped->InternalHigh中。
3. 触发事件内核对象
异步读写时,ReadFile和WriteFile中必须设置lpOverlapped值(不能置为NULL)。但是可以将lpOverlapped->hEvent置为NULL。当异步读写完成时,Windows系统会触发文件内核对象hFile,如果lpOverlapped->hEvent不是NULL值,Windows也会触发该事件。
因为hFile可以被多个异步读写同时共享,所以如果在hFile上的等待可能会被误触发(其他读写完成了,系统触发了hFile)。当只有一个读或者写时,可以使用hFile等待。其它情况应该使用lpOverlapped->hEvent事件等待。
但是有一点要明确,Windows会触发hFile和lpOverlapped->hEvent(如果有的话)。
4. lpOverlapped
当这个OVERLAPPED结构的地址通过ReadFile/WriteFile传给系统后,系统会一直使用这个数据块,直到读写完成。
完成时,系统会将读写数据长度放在InternalHigh中,将返回状态值放在Internal中。所以千万不要在读写结束前将这个OVERLAPPED内存块销毁或者挪作他用。
5. GetOverlappedResult
这个函数原型如下:
BOOL WINAPI GetOverlappedResult(
_In_ HANDLE hFile,
_In_ LPOVERLAPPED lpOverlapped,
_Out_ LPDWORD lpNumberOfBytesTransferred,
_In_ BOOL bWait
);
这个函数的源代码可以看我以前的文章。从源码看以看出这个如下几点:
1) hFile不一定是必须的。只有在bWait为TRUE且lpOverlapped->hEvent为0时才会使用hFile.
2) lpOverlapped就是前面调用ReadFile、WriteFile时所使用的地址值,不能是其他的。
3) 这个函数的功能其实很简单:将lpOverlapped->InternalHigh赋给lpNumberOfByesTransferred;将lpOverlapped->Internal赋给返回值。
所以,当读写完成时,这个函数基本没有什么用。也因此,《Windows核心编程》基本就不用这个函数了。
HANDLE hFile = CreateFile(..., FILE_FLAG_OVERLAPPED, ...);
BYTE bBuffer[100];
OVERLAPPED o = { 0 };
o.Offset = 345;
BOOL bReadDone = ReadFile(hFile, bBuffer, 100, NULL, &o);
DWORD dwError = GetLastError();
if (!bReadDone && (dwError == ERROR_IO_PENDING)) {
// 异步I/O,等待返回
WaitForSingleObject(hFile, INFINITE);//有时此处也会发生错误。
bReadDone = TRUE;
}
//返回了,不管是同步也是异步。
if (bReadDone) {
// o.Internal包含错误值
// o.InternalHigh包含传输字节数
// bBuffer包含读取的数据
} else {
// 发生错误,见dwError
}
6. 串口通讯
应用程序实际上是直接和驱动程序打交道的。所以虽然是串口I/O操作,很多函数都是很快返回的。这可能有一定的好处,但是也引发一些不利的地方,比如不能了解PC串口上是否真正连接到设备上。就是说对一个空的COM口,大部分COMM函数都能成功返回的。
7. CreateFile打开串口设备
CreateFile函数原型如下:
HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
lpFileName是指定要打开设备的名称。COM1至COM9口可以直接lpFileName指定为“COM1”..“COM9”。但是超过COM10这种方法就没有用了,必须使用“\\.\COM10”了。所以为了统一可以都使用“\\.\COMx”方式来指定名称。
8. SetCommTimeouts
串口通讯中使用该函数来设置超时(GetCommTimeouts用来获取超时设置)。超时设置会影响并决定何时完成读写(返回ReadFile/WriteFile或者WaitFor…)。
函数设置了一个COMMTIMEOUTS结构:
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS, *LPCOMMTIMEOUTS;
设置了超时后,只要满足条件,ReadFile/WriteFile都会正常返回(hEvent和hFile被触发)。
在WaitForSingleObject中:
DWORD WINAPI WaitForSingleObject(
_In_ HANDLE hEvent,
_In_ DWORD dwMilliseconds
);
这里也有一个dwMilliseconds超时值,这和SetCommTimeouts设置的超时是不同的:
1) CommTimeouts超时,hHandle会被触发。WaitForSingleObject返回WAIT_OBJECT_0,ol.InternalHigh返回读写数据字节数。
2) dwMilliseconds超时,WaitForSingleObject返回WAIT_TIMEOUT。
- 串口异步读写
- 串口异步读写
- Win32串口异步读写函数
- 用API实现串口异步读写
- 用API实现串口异步读写
- 用API实现串口异步读写
- 异步读写的简单串口类
- 用API实现串口异步读写
- 关于异步读写串口的注意事项
- 串口读写
- 读写串口
- 异步串口
- 异步读写
- 异步读写
- EasyComm - 一个完全的串口读写类, 简单易用, 支持同步和异步.
- Android串口通信:串口读写
- Android串口通信:串口读写
- 串口 API串口异步通讯
- poj 2892 Tunnel Warfare
- oracle 10g recyclebin引起的dba_free_space性能问题
- 【android官方文档】android 多国语言支持 国际化
- html 文本输入框效果
- libsvm代码阅读:svm.cpp浅谈和函数指针
- 串口异步读写
- 第三方存管与普通转账的区别
- python-pcap
- linux下查看电脑硬件设备属性
- http://baiy.cn/doc/cpp/inside_exception.htm#栈回退(Stack_Unwind)机制
- 定义点类,并以点类为基类,派生出直线类,从基类中继承的点的信息表示直线的中点。
- 一、Camparable和Comparator的区别
- 关于IAR Embedded Workbench的Licence问题
- java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource