1、打开和关闭串口
在Win32中,串口是作为文件处理的,使用CreateFile()函数可以打开串口,进行读写访问操作。CreateFile()返回串口句柄,可以在以后的端口操作中使用。关闭端口使用CloseHandle()函数来完成。
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:要打开或创建的文件名。打开串口设备可以直接写串口号,如"COM4",需要注意的是COM10及以上的串口格式应为: "\\\\.\\COM10"。
dwDesiredAccess:访问类型,0:设备查询访问权限(程序可以不访问该设备就能查询到设备属性)
GENERIC_READ:读访问权限
GENERIC_WRITE:写访问权限
dwShareMode:共享方式,0:文件不能被共享,其它打开操作会失败。
FILE_SHARE_DELETE:其它删除操作会成功。
FILE_SHARE_READ:其它读操作会成功。
FILE_SHARE_WRITE:其它写操作会成功。
lpSecurityAttributes:安全属性,一个指向SECURITY_ATTRIBUTES结构的指针。
dwCreationDisposition:指定要打开的文件以存在或不存在的动作,
CREATE_NEW :创建文件;如文件存在则会失败
CREATE_ALWAYS:创建文件,如果文件已存在则清空
OPEN_EXISTING:打开文件,文件必须已经存在,否则会失败
OPEN_ALWAYS:打开文件,如果文件不存在则创建它
TRUNCATE_EXISTING 打开文件,且将现有文件缩短为零长度(需要GENERIC_WRITE权限),如果文件不存在则失败
dwFlagsAndAttributes:文件属性和标志,
如果CreateFile()打开的是命名管道客户端,那么dwFlagsAndAttributes参数也可以包含服务信息的安全特性,
当程序指定了SECURITY_SQOS_PRESENT标志,dwFlagsAndAttributes可以包含下表中一个或多个值。
hTemplateFile:一个指向模板文件的句柄,且该模板必须是以GENERIC_READ访问方式打开的。如果此参数不是NULL,则会使用hTemplateFile关联的文件的属性和标志来创建文件。另外,如果是打开一个现有文件,则该参数被忽略。
以下为打开COM1串口的示例代码:
2、串口配置和属性
串口打开后就可以设置接收缓冲区和发送缓冲区,这可以通过SetupComm()函数实现,如果通信的速率较高,则应该设置较大的缓冲区:
BOOL WINAPI SetupComm(
__in HANDLE hFile,//串口句柄
__in DWORD dwInQueue,//输入缓冲区大小
__in DWORD dwOutQueue//输出缓冲区大小
);
可以通过函数GetCommState()和SetCommState()来获得和设置串口的配置:
BOOL WINAPI GetCommState(
__in HANDLE hFile,//串口句柄
__out LPDCB lpDCB//保存串口配置信息
);
BOOL WINAPI SetCommState(
__in HANDLE hFile,//串口句柄
__in LPDCB lpDCB//设置串口配置信息
);
DCB结构:
DCB结构中常用成员:
DWORD BaudRate:串口波特率,常用的有: CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000, CBR_14400
DWORD fParity:指定奇偶校验,1为激活奇偶校验检查
DWORD Parity:校验方式,值0~4分别对应无校验、奇校验、偶校验、校验
置位(标记校验)、校验清零
DWORD ByteSize:一个字节的数据位个数,范围是5~8
DWORD StopBits:停止位个数,0~2分别对应1位停止位、1.5位停止位、2位停止位
操作举例:
GetCommProperties()可以获得串口的属性:
BOOL WINAPI GetCommProperties(
__in HANDLE hFile,//串口句柄
__out LPCOMMPROP lpCommProp//保存串口属性
);
CommConfigDialog()用来对通信设备进行配置,从而改变数据传输速率、数据位、奇偶校方法、停止位和流控制方法,当函数返回时,选定的设置在COMMCONFIG结构的DCB参数中返回:
BOOL WINAPI CommConfigDialog(
__in LPCWSTR lpszName,//端口名
__in_opt HWND hWnd,//拥有对话框的窗口句柄
__inout LPCOMMCONFIG lpCC//指向一个COMMCONFIG结构
);
对于已经打开的串口,对端口设置进行更改应通过SetCommState()来进行。
3、读写串口
一般在程序中使用WriteFile()向串口中写数据,调用ReadFile()从串口读数据。
BOOL WINAPI WriteFile( _In_ HANDLE hFile,//文件句柄 _In_ LPCVOID lpBuffer,//指向一个缓冲区,包含要写入的数据 _In_ DWORD nNumberOfBytesToWrite,//要写入数据的字节数 _Out_opt_ LPDWORD lpNumberOfBytesWritten,//实际写入的字节数 _Inout_opt_ LPOVERLAPPED lpOverlapped//指向一个OVERLAPPEN结构体 );
如果想要异步读写操作,则lpOverlappen参数不能为NULL,且在CreateFile()打开文件时应指定FILE_FLAG_OVERLAPPEN标记。
需要注意的是,当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。这说明重叠操作还未完成。
WriteFileEx()与ReadFileEx()只能用于异步读写操作,且可以设置一个读写完成后自动调用的回调函数。
eg:
BOOL WINAPI ReadFile( _In_ HANDLE hFile,//文件句柄 _Out_ LPVOID lpBuffer,//指向一个缓冲区,保存读取的数据 _In_ DWORD nNumberOfBytesToRead,//要读取数据的字节数 _Out_opt_ LPDWORD lpNumberOfBytesRead,//实际读取的字节数 _Inout_opt_ LPOVERLAPPED lpOverlapped//指向一个OVERLAPPED结构 );eg:
4、超时处理
以下转自:http://blog.csdn.net/augusdi/article/details/10220911
在用ReadFile和WriteFile读写串行口时,需要考虑超时问题,如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。
BOOL GetCommTimeouts( _In_ HANDLE hFile, _Out_ LPCOMMTIMEOUTS lpCommTimeouts );
BOOL SetCommTimeouts( _In_ HANDLE hFile, _In_ LPCOMMTIMEOUTS lpCommTimeouts );
有两种超时:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共花费的最大时间。写操作只支持总超时,而读操作两种超时均支持。
用COMMTIMEOUTS结构可以规定读/写操作的超时,该结构的定义为:
如果ReadIntervalTimeout为MAXDWORD, 并且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier都为0, 则指定读操作携带已经收到的字符立即返回,即使没有收到任何字符;如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则在读操作时忽略总超时数;如果WriteTotalTimeoutMultiplier和WriteTotalTimeoutConstant都为0,则在写操作时忽略总超时数。
COMMTIMEOUTS结构的成员都以毫秒为单位,用户设置通讯超时后,如没有出错,串口已经被打开。
可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且两个读总超时为0,那么在读一次输入缓冲区中的内容后读操作就立即完成,而不管是否读入了要求的字符。 在用重叠方式读写串行口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。
5、通信状态和通信错误
如果在串口通信中发生错误,如终端错误、奇偶错误等,I/O操作将会终止。如果程序要进一步执行I/O操作,必须调用ClearCommError()函数。ClearCommError()可以清除(获得)通信错误和获得串口的当前通信状态。
BOOL WINAPI ClearCommError( _In_ HANDLE hFile,//串口句柄 _Out_opt_ LPDWORD lpErrors,//错误码 _Out_opt_ LPCOMSTAT lpStat//通讯状态 );lpErrors错误码解释如下:
lpStat通讯状态结构体如下:
该结构中对我们很重要的只有上面两个参数,其他的我们可以不用管。
举例:
6、其它串口通信常用API
以下转自:http://blog.csdn.net/vodomine/article/details/6542089
PurgeComm()
用途:清空串口缓冲区,在读写串口之前,还要用PurgeComm()函数清空缓冲区。
原型:BOOL PurgeComm(HANDLE hFile, DWORD dwFlags );
参数说明:
-hFile:串口句柄
-dwFlags:指定串口执行的动作,由以下参数组成:
-PURGE_TXABORT:停止目前所有的传输工作立即返回不管是否完成传输动作。
-PURGE_RXABORT:停止目前所有的读取工作立即返回不管是否完成读取动作。
-PURGE_TXCLEAR:清除发送缓冲区的所有数据。
-PURGE_RXCLEAR:清除接收缓冲区的所有数据。
操作举例:
SetCommMask()
用途:设置串口通信事件。
原型:BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask);
参数说明:
-hFile:串口句柄
-dwEvtMask:准备监视的串口事件掩码
注:在用api函数撰写串口通信函数时大体上有两种方法,一种是查寻法,另外一种是事件通知法。 这两种方法的区别在于收串口数据时,前一种方法是主动的周期性的查询串口中当前有没有 数据;后一种方法是事先设置好需要监视的串口通信事件,然后依靠单独开设的辅助线程进行 监视该事件是否已发生,如果没有发生的话该线程就一直不停的等待直到该事件发生后,将该串口事件以消息的方式通知主窗体,然后主窗体收到该消息后依据不同的事件性质进行处理。 比如说当主窗体收到监视线程发来的RX_CHAR(串口中有数据)的消息后,就可以用ReadFile() 函数去读串口。
dwEvtMask参数有如下信息掩码位值:
EV_BREAK:收到BREAK信号
EV_CTS:CTS(dear to send)线路发生变化
EV_DSR:DST(Data Set Ready)线路发生变化
EV_ERR:线路状态错误,包括了CE_FRAME/CE_OVERRUN/CE_RXPARITY 3钟错误。
EV_RING:检测到振铃信号。
EV_RLSD:CD(Carrier Detect)线路信号发生变化。
EV_RXCHAR:输入缓冲区中已收到数据。
EV_RXFLAG:使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。
EV_TXEMPTY:输出缓冲区中的数据已被完全送出。
操作举例:
WaitCommEvent ()
用途:用来判断用SetCommMask()函数设置的串口通信事件是否已发生。
原型:
BOOL WINAPI WaitCommEvent( _In_ HANDLE hFile, _Out_ LPDWORD lpEvtMask, _In_ LPOVERLAPPED lpOverlapped );参数说明:
GetOverlappedResult()可以判断一个重叠操作当前的状态,用来判断异步操作是否完成。
BOOL WINAPI GetOverlappedResult( _In_ HANDLE hFile,//文件句柄 _In_ LPOVERLAPPED lpOverlapped,//指向欲检查的重叠结构 _Out_ LPDWORD lpNumberOfBytesTransferred,//读或写操作的字节数 _In_ BOOL bWait );如果参数bWait为TRUE则函数会一直等待直到重叠机构中的hEvent变成有信号;FALSE为如果检测到pending状态则立即返回,此时函数返回FALSE,GetLastError()返回值为ERROR_IO_INCOMPLETE。
MSDN上关于WaitCommEvent的说明及例子:
WaitCommEvent()用来检测指定通信设备上一组事件的发生,可以通过SetCommMask()函数来设置通信设备上的事件掩码,GetCommMask()函数获得通信设备上的事件掩码。
如果重叠操作不能立即完成,则WaitCommEvent()返回FALSE,GetLastError()会返回ERROR_IO_PENDING,表示操作正在后台进行。在WaitCommEvent返回之前,重叠结构中的hEvent成员会被设置为无信号状态,如果当事件发生或错误发生时,其被设置为有信号状态,应用程序可以调用wait functions(WaitForSingleObject、WaitForSingleObjectEx等)来判断事件对象的状态,然后调用GetOverlappedResult()来判断WaitCommEvent()操作的结果,GetOverlappedResult会报告操作成功或失败,而参数lpEvtMask会保存具体发生的事件。
7、串口通信API流程
无论那种操作方式,一般都通过四个步骤来完成:
(1) 打开串口
(2) 设置和配置串口
(3) 读写串口
(4) 关闭串口
打开串口:
设置和配置串口:
读写串口(异步):
关闭串口: