VC 串口通讯基本原理,讲的很是详细
阅读原文时间:2023年07月08日阅读:2

目 录
打开串口………………………………………………………………………………………………………… 2
关闭串口………………………………………………………………………………………………………… 3
串口配置与串口属性………………………………………………………………………………………. 4
串口配置………………………………………………………………………………………………….. 4
串口属性………………………………………………………………………………………………….. 6
缓冲区控制………………………………………………………………………………………………. 7
读写串口………………………………………………………………………………………………………… 8
读串口操作………………………………………………………………………………………………. 8
写串口操作………………………………………………………………………………………………. 9
异步 I/O 操作 …………………………………………………………………………………………. 10
超时设置………………………………………………………………………………………………… 11
通信状态和通信错误………………………………………………………………………………. 13
打开串口代码示例:…………………………………………………………………………………….. 14
串口的读写代码示例:…………………………………………………………………………………. 15
PDF created with pdfFactory Pro trial version www.pdffactory.com
在“创意天地中级”开发过程中,关于串口通信的开发采用的是 Windows 串口通信相
关 API 函数。
在 32 位 windows 系统中,串口和其他通信设备都是作为文件处理的。串口的打开、关
闭、读取和写入所有的函数与操作文件的函数完全一致。
打开串口
通信会话以调用 CreateFile()开始。CreateFile()为读访问、写访问或读写访问“打开”串
口。按照 windows 的通常做法,CreateFile()返回一个句柄,随后在打开端口的操作中使用。
CreateFile()函数非常复杂,复杂性的原因之一是他是通用的。可以使用 CreateFile 打开已经
存在的文件,创建新文件和打开根本就不是文件的设备,例如串口、并口、和调制解调器。
CreateFile()函数声明如下:
HANDLE CreateFile(
LPCTSTR lpszName, //lpszName:指定要打开的串口逻辑名,用字符串表示,
例如”COM1”,表示串口 1
DWORD fdwAccess, //fdwAccess:用来指定串口访问的类型。与文件一样,
串口也是可以被打开一并读取写入或者两者兼有。
GENERIC_READ 为读取访问,GENERIC_WRITE 为写访问
const GENERIC_READ = 0x80000000h;
const GENERIC_WRITE = 0x40000000h;
因为大部分串口通信都是双向的, 所以在设置中通常将
两个标识连接起来使用,
即 fdwAccess = GENERIC_READ|GENERIC_WRITE
DWORD fdwShareMode, //fdwShareMode:指定该端口的共享属性。该参数是为
那些有许多应用程序共享的文件提供的。 对于不能共享
的串口,它必须设为 0。这就是文件与通信设备之间的
主要差异之一。 如果在当前的应用程序调用 CreateFile()
时, 另一个应用程序已经打开了串口, 该函数就会返回
错误代码, 原因是两个应用程序不能共享一个端口。 然
而, 同一个应用程序的多个线程可以共享由 CreateFile()
返回的端口句柄, 并且根据安全性属性设置, 该句柄可
以被打开端口的应用程序的子程序所继承。
LPSECURITY_ATTRIBUTES lpsa , //lpsa: 引 用 安 全 属 性 结 构
(SECURITY_ARRTIBUTES) ,该结构定义了一些属
性, 例如通信句柄如何被打开端口的应用程序的子
程序所继承。将该参数设置为 NULL 将为该端口分
配默认的安全性属性。 子应用程序所继承的默认属
性是该端口不能被继承的。
安全属性结构 SECURITY_ATTRIBUTES 声明如下:
typedef struct _SECURITY_ATTRIBUTES{
DWORD nLength;//指明该结构的长度
DWORD lpSecurityDescriptor;//指向一个安全描
述字符
PDF created with pdfFactory Pro trial version www.pdffactory.com
BOOL bInheritHandle;//表明句柄是否能被继承
}SECURITY_ATTRIBUTES;
DWORD fdwCreate, //fdwCreate:指定如果 CreateFile()正在被已有的文件调
用时应采取的动作。因为串口总是存在,fdwCreate 必
须设置成 OPEN_EXISTING。该标志告诉 Windows 不要
企 图 创 建 新 端 口 , 而 是 打 开 已 存 在 的 端 口 。
OPEN_EXISTING 常数定义为:const OPEN_EXISTING = 3;
DWORD fdwAttrsAndFlags, //fdwAttrsAndFlags:描述了端口的各种属性。对于
文件来说, 对于文件来说可能具有很多属性, 但对于串
口,唯一有意义的设置是 FILE_FLAG_OVERLAPPED。当
创建时指定该设置, 端口 I/O 可以在后台进行 (后台 I/O
也叫异步 I/O) 。 FILE_FLAG_OVERLAPPED 常数定义如下:
const FILE_FLAG_OVERLAPPED = 0x40000000h;
HANDLE hTemplateFile //hTemplateFile:指向模版文件的句柄, 当端口处于打开
状态时,不使用该参数,因而必须设置成 0。
)
当使用 CreateFile()函数打开串口时,为实现调制解调器的排他性访问,共享标识
(fdwShareMode)必须设为零;创建标识(fdwCreate)必须设为 OPEN_EXISTING;模板句
柄(hTemplateFile)必须设置为空。
一旦端口处于打开状态,就可以分配一个发送缓冲区和接收缓冲区,并且通过调用
SetupComm()实现其他初始化工作。也可以不调用 SetupComm()函数,Windows 系统也会分
配默认的发送和接收缓冲区, 并且初始化端口。 但是为了保证缓冲区的大小与实际需要一致,
最好还是调用该函数。SetupComm()函数声明如下:
BOOL SetupComm(
HANDLE hFile, //通信设备句柄。由 CreateFile()返回的只想一打开端口的句柄
DWORD dwInQueue, //接受/输入缓冲区大小。
DWORD dwOutQueue //发送/输出缓冲区大小
);
接收缓冲区和发送缓冲区, 这两个定义并非是实际的缓冲区大小, 指定的大小仅仅是 “推
荐的”大小,而 Windows 可以随意分配任意大小的缓冲区。Windows 设备驱动程序可以获
得这两个数据,并不直接分配大小,而是用来优化性能和避免缓冲区超限。
关闭串口
调用 CloseHandle()函数关闭由 CreateFile()函数返回的句柄即可。
CloseHandle 函数声明如下:
BOOL CloseHandle(
HANDLE hObject //需要关闭的设备句柄
);
使用串口时一般需要关闭它,如果忘记关闭串口,串口就会始终处于打开状态,其他应
用程序就不能打开并使用串口了。
PDF created with pdfFactory Pro trial version www.pdffactory.com
串口配置与串口属性
在 CreateFile 函数中打开串口之后, 系统将根据上次打开串口时设置的值来初始化串口,
可 以 集 成 上 次 打 开 操 作 后 的 数 值 , 包 括 设 备 控 制 块 ( DCB ) 和 超 时 控 制 结 构
(COMMTIMEOUTS) 。如果是首次打开串口,Windows 操作系统将会使用默认的配置。
串口配置
使用 GetCommState()函数获取串口的当前配置, 使用 SetCommState()重新分配串口资源
的各个参数。
GetCommState()函数声明如下:
BOOL GetCommState(
HANDLE hFile, //通信设备句柄。 由 CreateFile()函数返回的指向已打开串口的句柄
LPDCB lpDCB //指向 device-control block structure 的指针。一个非常重要的结构
——设备控制块 DCB(Device Control Block)
);
DCB 结构的声明如下:
typedef structure _DCB{
DWORD DCBlength; //DCB 块大小,以字节为单位指定 DCB 结构的大小;
DWORD BaudRate; //现在的数据传输率。 用以指定串口设备通信的数据传输速
率。它可以是实际的数据传输率,也可以是下列数据之一:
110, 19200, 300, 38400, 600、 56000、 1200、 57600、 2400、
115200、4800、128000、9600、256000、14400。
DWORD fBinary: 1; //二进制模式, 不检验 EOF。 指定是否允许二进制。 Win32 API
不支持非二进制传输,因此此参数必须设置为 TRUE,否则
不能正常工作。
DWORD fParity: 1; //允许奇偶校验。指定是否允许奇偶校验,如果设置为
TRUE,则执行奇偶校验并报告错误信息。
DWORD fOutxCtsFlow: 1; //CTS 输出流控制。 指定 CTS 是否用于检测发送流控制。
当设置为 TRUE 时,CTS 为 OFF,此时发送被挂起,直
到 CTS 置 ON。
DWORD fOutxDsrFlow: 1; //DSR输出流控制。 指定DSR是否用于检测发送流控制。
当设置为 TRUE 时,DSR 为 OFF,此时发送被挂起,直
到 DSR 置 ON。
DWORD fDtrControl: 2; //指定 DTR 流控制类型, 可以是以下三个值中的任意一
值:DTR_CONTROL_DISABLE(禁止 DTR 线,并保持禁止
状态) 、DTR_CONTROL_ENABLE(允许 DTR 线,并保持
允许状态) 、DTR_CONTROL_HANDSHAKE(允许 DTR 握
手 , 如 果 允 许 握 手 , 则 不 允 许 应 用 程 序 使 用
EscapeCommFunction 函数调整线路) 。
PDF created with pdfFactory Pro trial version www.pdffactory.com
DWORD fDsrSensitivity: 1 //对 DTR 信号线是否敏感。指定通信设备驱动程序对
DTR 线是否敏感,如果该位置设为 TRUE,DSR 信号为
OFF,接受的任何字节将被忽略。
DWORD fTXContinueOnOff: 1; //XOFF continues Tx。指定当接收缓冲区已满,并
且驱动程序已经发送出 XoffChar 字符时发送是否
停止。TRUE:在接收缓冲区接收到代表缓冲区已
满的字节 XoffLim,并且驱动程序已经发送出
xoffChar 字符终止接收字节之后,发送继续进行。
FALSE:在接收缓冲区接收到代表缓冲区已空的字
节 XonLim,并且驱动程序已经发送出恢复发送的
xonChar 字符之后,发送可以继续进行。
DWORD fOutX: 1; //XON/XOFF输出流控制。 该值为TRUE时, 代表接收到XoffChar
之后停止发送,接收到 XonChar 之后发送将重新开始。
DWORD fInX: 1; //XON/XOFF 输入流控制。 该值为 TRUE 时,接收缓冲区接收
到代表缓冲区满的 XoffLim 之后,XoffChar 发送出去,接收
缓冲区接收到代表缓冲区已空的字符 XonLim 之后, XonChar
发送出去。
DWORD fErrorChar: 1; //错误替换。当该值为 TRUE,并且 fParity 为 TRUE 时,
就会用 ErrorChar 成员指定的字符来代替奇偶校验错误
的接收字符。
DWORD fNull: 1; //是否丢弃接收到的 NULL(ASCII 0)字符。该值为 TRUE 时,
接收时去掉空(零之)字节;反之不丢弃。
DWORD fRtsControl: 2; //RTS 流控制。指定 RTS 流控制,可取值为以下四种:
RTS_CONTROL_DISABLE(打开设备时禁止 RTS 线,并保
持禁止状态) 、 RTS_CONTROL_ENABLE (打开设备时允许
RTS 线,并保持允许状态) 、RTS_CONTROL_HANDSHAKE
(允许握手。 在接收缓冲区小于半满时将 TRS 置为 ON,
在接收缓冲区超过 3/4 时将 RTS 置为 OFF。如果允许握
手,则不允许应用程序使用 EscapeCommFunction 函数
调整线路) 、RTS_CONTROL_TOGGLE(当发送的字节有
效,将 RTS 置为 ON,发送完缓冲区的所有字节后,RTS
置为 OFF)
DWORD fAbortOnError: 1; //发送错误,指定是否终止读、写操作。TRUE:当
发生错误时, 驱动程序以出错状态中止所有的读写
操作。只有当应用程序调用 ClearCommError()函数
处理后,串口才能接收随后的通信操作
DWORD fDummy2: 17; //保留的位,没有使用
WORD wReserved; //现在不用,必须为零
WORD XonLim; //XOFF 字符发送之前接收到缓冲区中可允许的最小字节数
WORD XoffLim; //XOFF 字符发送之前缓冲区中可允许的最小可用字节数
BYTE ByteSize; //指定端口当前使用的数据位数
BYTE Parity; //指定当前使用的奇偶校验方法。EVENPARITY(偶校验) 、
MARKPARITY(符号校验) 、NOPARITY(无校验) 、ODDPARITY(奇
校验) 、SPACEPARITY(空格校验)
PDF created with pdfFactory Pro trial version www.pdffactory.com
BYTE StopBits; //当前使用的停止位数。ONESTOPBIT(1 位停止位) 、
ONE5STOPBIT(1.5 位停止位) 、TWOSTOPBIT(2 位停止位)
char XonChar; //指明发送和接收的 XON 字符,它表明允许继续传输
char XoffChar; //指明发送和接收的 XOFF 字符,它表明暂停数据传输
char ErrorChar; //用来代替接收到的奇偶校验发生错误的字符
char EofChar; //表示数据的结束
char EvtChar; //事件字符。当接收到此字符的时候,会产生一个事件。
WORD wReserved1; //保留的位,没有使用
}
如果 GetCommState()函数调用成功,则返回值不为 0。若函数调用失败,返回值为 0,
如果想得到进一步的错误信息,可以调用 GetLastError()函数来获取。GetLastError()函数也是
Win32 API 函数,它的声明如下: DWORD GetLastError(void);
如果应用程序只修改一部分配置时,可以通过 GetCommState()函数获得当前的 DCB 结
构,然后更新 DCB 结构中的参数,调用 SetCommState()函数配置修改过的 DCB 来配置端口。
SetCommState()函数声明如下:
BOOL SetCommState(
HANDLE hFile, //已打开的串口句柄
LPDCB lpDCB //指向 DCB 结构的指针
);
如果函数调用成功,则返回值不为 0,否则返回值为 0.出错时可以调用 GetLastError()
函数来获取进一步的出错信息。 如果 SetCommState()函数调用的 DCB 结构中的 XonChar 等价
于 XoffChar 成员,则 SetCommState()函数调用失败。
串口属性
配置串口属性时应先了解串口设备的属性。串口的属性可以通过 GetCommProperties()
函数获得。下面是 GetCommProperties()函数的声明:
GetCommProperties(
HANDLE hFile, //已打开的串口句柄
LPCOMMPROP lpCommProp //指向一个 COMMPROP 的结构,串口的性能从
COMMPROP 中返回,COMMPROP 的结构内容是静
态的, 不允许任何应用程序改变该结构中返回的信
息。没有任何相应的函数可将 COMMPROP 结构中
的 设 置 写 向 串 口 。 COMMPROP 结 构 和
GetCommProperties 并不仅仅用于串口,也可以用
来返回并口的相关信息。
);
COMMPROP 结构的类型定义如下:
typedef struct _COMMPROP{
WORD wPacketLenght; //不管申请的数据大小,参数指明整个数据薄的大小。
WORD wPacketVersion; //指定 COMMPROP 结构的版本,不同版本的 Windows
操作系统使用不同版本的 COMMPROP,成员的解释不
同,所以,首先检查该成员,以确保能够正确的解释该
结构。
PDF created with pdfFactory Pro trial version www.pdffactory.com
DWORD dwServiceMask; //指定一位掩码。它指出由设备实现的服务,广泛的定
义 了 端 口 的 功 能 , 对 串 口 , 该 成 员 设 置 为
SP_SERHLCOMM,Modem 服务也可以指定为次位。
DWORD dwReserved1; //保留的,未使用
DWORD dwMaxTxQueue; //指定驱动程序内部发送缓冲区的最大允许长度(字
节) 。若该成员返回 0,表示端口对缓冲区长度没限制
DWORD dwMaxRxQueue; //指定驱动程序内部接收缓冲区的最大允许长度(字
节) 。若该成员返回 0,表示端口对缓冲区长度没限制
DWORD dwMaxBaud; //以 bit/s 为单位指定可用的最大数据传输速率。
DWORD dwProvSubType; //指定通信设备的类型
DWORD dwProvCapabilities; //指定一个位掩码,标识设备能够实现的功能
DWORD dwSettableParams; //指定一个位掩码,标识可修改的通信参数
DWORD dwSettableBaud; //指定一个位掩码,标识可以设置的数据传输速率
WORD wSettableData; //指定一个位掩码,标识可以设置的数据位数
WORD wSettableStopParity; //指定一个位掩码, 表示可以设置的停止位数和奇偶校
验位
DWORD dwCurrentTxQueue; //以字节为单位指定当前发送缓冲区大小
DWORD dwCurrentRxQueue; //以字节为单位指定当前接收缓冲区大小
DWORD dwProvSpec1; //指定设备的特定数据。在调用 GetCommProperties 函
数之前必须设置该成员为 COMMPROP_INITIALAIZED,
表明 wPacketLength 成员有效
DWORD dwProvSpec2; //指定设备的特定数据。 如果没有关于设备要求的数据
格式信息,则应用程序忽略该成员。
DCHAR wcProvChar[1]; //指定设备的特定数据。 如果没有关于设备要求的数据
格式信息,则应用程序忽略该成员。
//如果 dwProvSubType 属性参数为 PST_Modem,则
dwProvSpec1、dwProvSpec2、wcProvChar[1]参数值分别
设置如下:dwProvSpec1:该参数未用;dwProvSpec2:
该参数未用; wcProvChar[1]: 包含一个 ModemDEVCAPS
结构
}COMMPROP;
缓冲区控制
Win32 通信 API 除了提供 SetupComm 函数控制初始化的缓冲区操作外,还提供了
PurgeComm()函数和 FlushFileBuffers()函数来进行缓冲区操作。
PurgeComm()函数声明如下:
BOOL PurgeComm(
HANDLE hFile, //返回的句柄
DWORD dwFlags //表示执行的操作,该参数可以是:PURGE_TXABORT(即使发送
操作没有完成,也终止所有的重叠发送操作,立即返回) 、
PURGE_RXABORT(即使接受操作没有完成,也终止所有的重叠
接收操作,立即返回) 、PURGE_TXCLEAR(清除发送缓冲区) 、
PDF created with pdfFactory Pro trial version www.pdffactory.com
PURGE_RXCLEAR(清楚接收缓冲区) 。
);
函数调用成功,则返回值不为 0,如果函数调用失败,则返回值为 0,可以调用
GetLastError()函数来获取进一步的错误信息。
可以看出,PurgeComm()函数可以在读写操作的同时,清空缓冲区。当应用程序在读写
操作时调用该函数, 不能保证缓冲区内的所有字符都被发送。 如果要保证缓冲区的所有字符
都被发送,应该调用 FlushFileBuffers()函数。该函数只收流量控制的支配,不受超时控制的
支配,它在所有的写操作完成后才返回。
FlushFileBuffers()函数声明如下:
BOOL FlushFileBuffers(
HANDLE hFile //函数打开的句柄
);
函数调用成功,则返回值不为 0,如果函数调用失败,则返回值为 0,可以调用
GetLastError()函数来获取进一步的错误信息。
读写串口
利用 Win32 通信 API 读写串口时,既可以同步执行,也可以重叠(异步)执行。在同
步执行时,函数直到操作完成才返回。这意味着在同步执行时线程会被阻塞,从而导致效率
低下。在异步执行时,即使操作还没完成,调用的函数也会立即返回。费时的 I/O 操作在后
台进行,这样线程就可以做其他工作。
读串口操作
程序可以使用 Win31 API ReadFile()函数或者 ReadFileEx()函数从串口中读取数据。
ReadFile()函数对同步或异步操作都支持,而 ReadFileEX()函数只支持异步操作。这两个函数
都受到函数是否异步操作、超时操作等有关参数的影响和限定。
ReadFile()函数声明如下:
BOOL ReadFile(
HANDLE hFile, //指向标识的句柄,该句柄必须拥有 GENERIC_READ 的权限
LPVOID lpBuffer, //指向一个缓冲区,主要用来存放从串口设备中读取的数据
DWORD nNumberOfBytesToRead, //指定从串口设备中读取的字节数
LPDWORD lpNumberOfBytesRead, //指向调用该函数读出的字节数,ReadFile()
在读操作前,首先将其设置为 0。
LPOVERLAPPED lpOverlapped // 一 个 OVERLAPPED 的 结 构 。 如 果 hFile 以
FILE_FLAG_OVERLAPPED 方式创建, 则需要此结构,
否则不需要。
);
如果函数因为超时而返回,返回值为 TRUE。参数 lpOverlapped 在异步操作时应该指向
一个 OVERLAPPED 结构,如果该参数为 NULL,那么参数将进行同步操作,而不管句柄是否
是由 FILE_FLAG_OVERLAPPED 标志创建的。 当 ReadFile 返回 FALSE 时, 不一定就是操作失败,
PDF created with pdfFactory Pro trial version www.pdffactory.com
线程应该调用 GetLastError()函数分析返回结果。
ReadFileEX()和 ReadFile()函数的不同之处在于,它只能异步执行。该函数是对 ReadFile()
的扩充,它允许该应用程序继续其他的操作,异步的报告读操作的完成状态。ReadFileEx()
函数的声明如下:
BOOL ReadFileEx(
HANDLE hFile, //指向标示的句柄
LPVOID lpBuffer, //指向一个缓冲区,主要用来存放从串口设备中读取的数据
DWORD nNumberOfBytesToRead, //指定从串口设备中读取的字节数
LPOVERLAPPED lpOverlapped, //一个 OVERLAPPED 的结构。此参数是必须的
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //指明一个 I/O 操作完
成后的常用例子的地址
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
写串口操作
程序可以使用 Win31 API WriteFile()函数或者 WriteFileEx()函数从串口中读取数据。
WriteFile()函数对同步或异步操作都支持,而 WriteFileEX()函数只支持异步操作。这两个函数
都受到函数是否异步操作、超时操作等有关参数的影响和限定。
WriteFile()函数声明如下:
WriteFile(
HANDLE hFile, //指向标识句柄,该句柄必须拥有 GENERAL_WRITE 的权限
LPCVOID lpBuffer, //指向一个缓冲区, 主要用来存放待写入串口设备中的数据
DWORD nNumberOfBytesToWrite, //指定要向串口设备写入的字节数
LPDWORD lpNumberOfBytesWritten, //指向调用该函数要写入的字节数
LPOVERLAPPED lpOverlapped // 一 个 OVERLAPPED 的 结 构 。 如 果 hFile 以
FILE_FLAG_OVERLAPPED 方式创建, 则需要此结构,
否则不需要。
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
WriteFileEX()和 WriteFile()函数的不同之处在于, 它只能异步执行。 该函数是对 WriteFile()
的扩充,它允许该应用程序继续其他的操作,异步的报告读操作的完成状态。WriteFileEx()
函数的声明如下:
BOOL WriteFileEx(
HANDLE hFile, //指向标示的句柄
LPCVOID lpBuffer, //指向一个缓冲区,主要用来存放待写入串口设备中的数据
DWORD nNumberOfBytesToRead, //指定向串口设备中写入的字节数
LPOVERLAPPED lpOverlapped, //一个 OVERLAPPED 的结构。如果 hFile 以
FILE_FLAG_OVERLAPPED 方式创建,则需要此
结构,否则不需要。
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //指明一个 I/O 操作完
PDF created with pdfFactory Pro trial version www.pdffactory.com
成后的常用例子的地址
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
Windows 系统还提供 TransmitCommChar()函数, 该函数可以在发送时指定一字符的传递
权限级别最高,使用该字符可以在缓冲区的其他字符发送之前发送。
TransmitCommChar()函数声明如下:
BOOL TransmitCommChar(
HANDLE hFile, //标识通信的端口句柄
Char cChar //要发送的字符
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
该函数是同步函数,也受流量控制和写超时支配,因此,当端口没有传输时,不能重复
调用 TransmitCommChar()函数。当 TransmitCommChar()函数把一个字符放到输出缓冲区中,
在下次调用这个函数前,这个字符必须被输出,否则 TransmitCommChar()函数会出错。
步 异步 I/O 操作
异步 I/O 操作是指应用程序可以在后台读或者写数据,而在前台做其他事情。例如,应
用程序在开始是对 1000 个数据进行读或者写操作,然后返回执行其他的操作,在读写完成
之后,Windows 就会产生一个信号,应用程序得到这个信号,便可以进行其他的读写操作
了。
要使用 OVERLAPPED 结构,CreateFile()函数的 dwFlagsAndAttributes 参数必须设为
FILE_FLAG_OVERLAPPED 标识,读写串口函数必须指定 OVERLAPPED 结构。异步 I/O 操作在
Windows 中使用广泛。
OVERLAPPED 结构类型声明如下:
typedef struct _OVERLAPPED{
DWORD Internal; //操作系统保留,指出一个和系统相关的状态。当
GetOverlappedResult()函数返回时,如果将扩展信息设置为
ERROR_IO_PENDING,该参数有效。
DWORD InternalHigh; //操作系统保留,指出发送或接收的数据长度。当当
GetOverlappedResult()函数返回值不为 0 时,该参数有效。
DWORD Offset;
DWORD OffsetHigh; //以上两个参数指明文件传送的开始位置和字节偏移量的
高位字
HANDLE hEvent; //指定一个 I/O 操作完成后触发的事件(信号) 。在调用读
写函数进行 I/O 操作之前,必须设置该参数
} OVERLAPPED;
在设置了异步 I/O 操作之后,I/O 操作和函数返回有以下两种情况。
① 函数返回时 I/O 操作已经完成
② 函数返回时 I/O 操作还没完成:此时一方面,函数返回值为零,并且 GetLastError()
函数返回 ERROR_IO_PENDING;另一方面,系统把 OVERLAPPED 中的信号事件设为
无信号状态。当 I/O 操作完成时,系统要把它设置为信号状态。
PDF created with pdfFactory Pro trial version www.pdffactory.com
异步 I/O 操作可以由 GetOverLappedResult()函数来获取结果, 也可以使用 Windows 信号
函数来处理。GetOverLappedResult()函数声明如下:
BOOL GetOverLappedResult(
HANDLE hFile, //标识通信句柄,它应该和开始调用重叠结构的 ReadFile、
WriteFile、WaitCommEvent 函数的参数一致
LPOVERLAPPED lpOverlapped, //在启动异步操作时指定的OVERLAPPED结构
LPDWORD lpNumberOfBytesTransferred, //指向一个长整型变量, 该变量接受
一个读或写操作实际传递的字节数
BOOL bWait //指定函数是否等待挂起的异步操作完成。如果该参数设为 1,
则该函数直到 I/O 操作完成后才返回。如果该参数被设为 0,同
时处于被挂起的状态,则该函数返回为 0,并且 GetLastError()
函数返回 ERROR_IO_INCOMPLETE。
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
Windows 也使用等待函数来检查事件对象的当前状态或者等待 Windows 状态信号,在
WaitForSingleObject()函数、WaitForSingleObjectEx()函数,以及 WaitForMultipleObjects()、
WaitForMultipleObjectsEx()函数中指定的 OVERLAPPED 结构中的 hEvent,即可获取函数返回
事件。
超时设置
当事先设定的超时间隔消逝时,ReadFile()、ReadFileEx()、WriteFile()和 WriteFileEx()操作
仍未结束, 那么超时设置将无条件结束读写操作, 而不管是否已经读出或者写入指定数量的
字符。
在读或写操作期间发生的超时将不按错误处理, 即读或写操作返回指定成功的值。 对于
同步读/写操作,实际传输的字节数由 ReadFile()和 WriteFile()函数报告。对于异步操作,则
有 OVERLAPPED 结构来获取。
超时结构定义如下:
typedef struct _COMMTIMEOUTS{
DWORD ReadIntervalTimeout; //以 MS 为单位指定通信线路上两个字符
到达之间的最大时间间隔。在 ReadFile()
操作期间, 从接收到第一个字符时开始计
时。 如果任意两个字符到达之间的时间间
隔超过这个最大值,则 ReadFile()操作完
成,并返回缓冲数据。如果被置为零,则
表示不适用间隔超时。
DWORD ReadTotalTimeoutMultiplier; //以 ms 为单位指定一个系数,该系数用
来计算读操作的总超时时间。
DWORD ReadTotalTimeoutConstant; //以 ms 为单位指定一个常数,该常数用
来计算读操作的总超时时间。
DWORD WriteTotalTimeoutMultiplier; //以 ms 为单位指定一个系数,该系数用
来计算写操作的总超时时间。
PDF created with pdfFactory Pro trial version www.pdffactory.com
DWORD WriteTotalTimeoutConstant; //以 ms 为单位指定一个常数,该常数用
来计算写操作的总超时时间。
} COMMTIMEOUT, *LPCOMMTIMEOUTS;
超时有两种类型。第一种类型叫做区间超时(interval timeout),它仅适应于从端口读取
数据。它指定在读取两个字符之间要经历多长时间。接收一个字符时,Windows 就启动一
个内部定时器。在下一个字符到达之前,如果定时器超过了区间超时设定时间,读函数就会
放弃。第二种类型的超时叫做总超时(total timeout) ,它适于读和写端口。当读或写特定字
节数需要的总时间超过某一阈值时,该超时即被触发。
Windows 使用下面的是自己算总超时时间:
ReadTotalTimeout = ( ReadTotalTimeoutMultiplier * bytes_to_read ) +
ReadTotalTimeoutConstant;
WriteTotalTimeout = ( WriteTotalTimeoutMultiplier * bytes_to_write ) +
WriteTotalTimeoutConstant;
每个读间隔超时参数 ReadIntervalTimeout 被设置为 MAXDWORD,而且两个读总超时参
数都被设置为 0,那么标识只要一读完接收缓冲区而不管得到什么字符就完成读操作,即使
它是空的。当接收中有间隔时,间隔超时将迫使读操作返回。因此使用间隔超时的进程可以
设置一个非常短的超时参数, 这样它可以实现对一个或者一些字符的小的、 孤立的数据作出
反应。
在传输某种流量控制被阻塞时和调用 SetCommBreak()函数把字符挂起来,写操作的超
时可能有用。如果所有的读超时参数都为零,既没有使用读超时,那么读操作直到读完要求
的字节数或发生错位时为止。同样,如果所有的写超时参数都为 0,那么写操作直到写完要
求的字节数或发生错误时为止。 当打开通信资源时, 超时参数将根据上次设备被打开时所设
置的值设置。 如果资源从未打开或调用 SetupComm 函数, 那么所有的超时参数都设置为零。
如果与获得当前的超时参数,应用程序可以调用 GetCommTimeouts()函数。该函数声明
如下:
BOOL GetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts //指向一个 COMMTIMEOUT 结构, 返回超
时信息
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
如果要设置或改变原来的超时参数,应用程序可以调用 SetCommTimeouts()函数,声明
如下:
BOOL SetCommTimeOuts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts //指向一个 COMMTIMEOUT 结构, 返回超
时信息
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。
PDF created with pdfFactory Pro trial version www.pdffactory.com
通信状态和通信错误
如果在串口通信中发生错误,如发生终端、奇偶错误等,I/O 操作将会终止。如果程序
要进一步执行 I/O 操作, 必须调用 ClearCommError()函数。 ClearCommError()函数有两个作用:
第一个作用是清楚错误条件;第二个作用是确定串口通信状态。ClearCommError()函数的声
明如下:
BOOL ClearCommError(
HANDLE hFile,
LPDWORD lpErrors, //指向用一个指明错误类型的掩码填充的 32 位变量。该参
数可以是以下各值的组合:CE_BREAK(硬件检测的一个终
端条件) 、CE_FRAME (硬件检测到一个帧错误) 、CE_IOE(发
生 I/O 错误) 、CE_MODE(模式出错,或者句柄无效) 、
CE_OVERRUN(超速错误) 、CE_RXOVER(接收缓冲区超限,
或者是输入缓冲区中没有空间, 或者是在文件结束符 (EOF)
接收后收到一个字符) 、CE_RXPARITY(奇偶校验错误) 、
CE_TXFULL(发送缓冲区满) 、CE_DNS(没有检测到并行设
备) 、CE_OOP(并行设备缺纸) 、CE_PTO(并行设备发生超
时错误)
LPCOMSTAT lpStat //指向一个 COMSTAT 结构,该结构接收设备的状态信息。
如果 lpStat 参数不设置,则没有设备状态信息被返回。
);
如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
GetLastError()函数来获取进一步的错误信息。在同步操作时,可以调用 ClearCommError()函
数来确定串口的接收缓冲区处于等待状态的字节数, 而后可以使用 ReadFile()或者 WriteFile()
一次读写完。
COMSTAT 结构存放有关通信设备的当前信息。该结构内容由 ClearCommError()填写,
COMSTAT 结构声明如下:
typedef struct _COMSTAT{
DWORD fCtsHold : 1; //是否等待 CTS 信号,如果为 1,则发送等待
DWORD fDsrHold : 1; //是否等待 DSR 信号,如果为 1,则发送等待
DWORD fRlsdHold : 1; //是否等待 RLSD 信号,如果为 1,则发送等待
DWORD fXoffHold : 1; //收到 XOFF 字符后发送是否等待,如果为 1,则发送等待
DWORD fXoffSent : 1; //发完 XOFF 字符后发送是否等待,如果为 1,则发送等待。
如果 XOFF 字符发送给一系统时,该系统就把下一个字符当
成 XON,而不管实际字符是什么,此时发送将停止。
DWORD fEof : 1; //EOF 字符送出
DWORD fTxim : 1; //指明字符是否正等待被发送,如果为 1,则字符正等待被
发送
DWORD fReserved : 25; //系统保留
DWORD cbInQue; //指明串行设备接收到的字节数。 并不是 ReadFile 操作要求
读的字节数
DWORD cbOutQue; //指明发送缓冲区中尚未发送的字节数, 如果进行不重叠写
操作时置为 0。
PDF created with pdfFactory Pro trial version www.pdffactory.com
} COMSTAT, *LPCOMSTAT;
打开串口代码示例:
HANDLE CContorl:: Connect(CString BTComNum, int BTBaudRate)
{
HANDLE hCom = CreateFile(BTComNum, //指定 Com 口
GENERIC_READ|GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式
NULL);
if(hCom ==INVALID_HANDLE_VALUE)
{
AfxMessageBox("打开 COM 失败!");
//return FALSE;
}
SetupComm(hCom,100,100); //输入缓冲区和输出缓冲区的大小都是 100
COMMTIMEOUTS TimeOuts;
//设定读超时
TimeOuts.ReadIntervalTimeout=MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier=0;
TimeOuts.ReadTotalTimeoutConstant=0;
//在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier=300;
TimeOuts.WriteTotalTimeoutConstant=500;
SetCommTimeouts(hCom,&TimeOuts); //设置超时
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate= BTBaudRate; //波特率(一般为 9600)
dcb.ByteSize=8; //每个字节有 8 位
dcb.Parity=NOPARITY; //无奇偶校验位
dcb.StopBits=ONESTOPBIT; //两个停止位
SetCommState(hCom,&dcb);
PDF created with pdfFactory Pro trial version www.pdffactory.com
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
return hCom;
}
串口的读写代码示例:
int CContorl:: GetLevel(HANDLE hCom)
{
OVERLAPPED m_osWrite;
memset(&m_osWrite,0,sizeof(OVERLAPPED)); //将结构体 m_osWrite 清零
m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
// hEvent:指定一个 I/O 操作完成后触发的事件(信号) 。在调用读写函数进行 I/O 操作
之前,必须设置该参数
//创建或打开一个命名的或无名的事件对象
BYTE lpOutBuffer[4];//里面存放要发送的数据,可以根据自己的需求改变
/*给 lpOutBuffer 赋值*/
DWORD dwBytesWrite=4;//要写入的数据的长度
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,&dwBytesWrite,&m_osWrite);
if(!bWriteStat)
{
if(GetLastError()==ERROR_IO_PENDING)
{
WaitForSingleObject(m_osWrite.hEvent,1000);
}
}
/*下面的部分是读取返回的信息*/
OVERLAPPED m_osRead;
memset(&m_osRead,0,sizeof(OVERLAPPED));
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
byte str[7]={0};//设定初值,读取的数据会放在这个数组中
PDF created with pdfFactory Pro trial version www.pdffactory.com
DWORD dwBytesRead=7;//读取的字节数
BOOL bReadStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
bReadStat=ReadFile(hCom,str,dwBytesRead,&dwBytesRead,&m_osRead);
if(!bReadStat)
{
if(GetLastError()==ERROR_IO_PENDING)
//GetLastError()函数返回 ERROR_IO_PENDING,表明串口正在进行读操作
{
WaitForSingleObject(m_osRead.hEvent,2000);
//使用 WaitForSingleObject 函数等待,直到读操作完成或延时已达到 2 秒钟
//当串口读操作进行完毕后,m_osRead 的 hEvent 事件会变为有信号
}
}
/*此处可以加入处理 str[] 的程序*/
PurgeComm(hCom,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
return ;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章