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串口的示例代码:
HANDLE hCom;
DWORD dwError;
hCom = CreateFile("COM1",//对串口1进行操作
GENERIC_READ|GENERIC_WRITE,//允许读和写
0,//独占方式
NULL,//默认安全属性
OPEN_EXISTING,//串口必须存在
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//重叠方式
NULL
);
if(hCom == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
......
}
......
CloseHandle(hCom);
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位停止位
操作举例:
DCB ComDCB;
GetCommState(hComm,&ComDCB);//取得当前串口状态
ComDCB.BaudRate=9600;//更改为9600bps,该值即为你要修改后的波特率
SetCommState(hComm,&ComDCB;//将更改后的参数写入串口
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:
HANDLE hFile;
char DataBuffer[] = "This is some test data to write to the file.";
DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer);
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
hFile = CreateFile(argv[1], // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("CreateFile"));
_tprintf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), argv[1]);
return;
}
bErrorFlag = WriteFile(
hFile, // open file handle
DataBuffer, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
DisplayError(TEXT("WriteFile"));
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != dwBytesToWrite)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
_tprintf(TEXT("Wrote %d bytes to %s successfully.\n"), dwBytesWritten, argv[1]);
}
}
CloseHandle(hFile);
BOOL WINAPI ReadFile( _In_ HANDLE hFile,//文件句柄 _Out_ LPVOID lpBuffer,//指向一个缓冲区,保存读取的数据 _In_ DWORD nNumberOfBytesToRead,//要读取数据的字节数 _Out_opt_ LPDWORD lpNumberOfBytesRead,//实际读取的字节数 _Inout_opt_ LPOVERLAPPED lpOverlapped//指向一个OVERLAPPED结构 );eg:
#define BUFFERSIZE 5
DWORD g_BytesTransferred = 0;
......
VOID CALLBACK FileIOCompletionRoutine(
__in DWORD dwErrorCode,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped )
{
_tprintf(TEXT("Error code:\t%x\n"), dwErrorCode);
_tprintf(TEXT("Number of bytes:\t%x\n"), dwNumberOfBytesTransfered);
g_BytesTransferred = dwNumberOfBytesTransfered;
}
......
HANDLE hFile;
DWORD dwBytesRead = 0;
char ReadBuffer[BUFFERSIZE] = {0};
OVERLAPPED ol = {0};
hFile = CreateFile(L"test1.dat", // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("Terminal failure: unable to open file for read.\n") );
return FALSE;
}
if( FALSE == ReadFileEx(hFile, ReadBuffer, BUFFERSIZE-1, &ol, FileIOCompletionRoutine) )
{
printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());
CloseHandle(hFile);
return FALSE;
}
SleepEx(5000, TRUE);
dwBytesRead = g_BytesTransferred;
if (dwBytesRead > 0 && dwBytesRead <= BUFFERSIZE-1)
{
ReadBuffer[dwBytesRead]='\0'; // NULL character
_tprintf(TEXT("Data read from test1.dat (%d bytes): \n"), dwBytesRead);
printf("%s\n", ReadBuffer);
}
else if (dwBytesRead == 0)
{
_tprintf(TEXT("No data read from file test1.dat\n"));
}
else
{
printf("\n ** Unexpected value for dwBytesRead ** \n");
}
CloseHandle(hFile);
4、超时处理
以下转自:http://blog.youkuaiyun.com/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结构可以规定读/写操作的超时,该结构的定义为:
typedef struct _COMMTIMEOUTS{
DWORD ReadIntervalTimeout; // 读间隔超时:接收时,两字符间最大的时延。
DWORD ReadTotalTimeoutMultiplier; // 读时间系数:读取每字节的超时。
DWORD ReadTotalTimeoutConstant; // 读时间常量:读串口数据的固定超时。
//读总超时 = ReadTotalTimeoutMultiplier*字节数 + ReadTotalTimeoutConstant
DWORD WriteTotalTimeoutMultiplier;// 写时间系数:写每字节的超时。
DWORD WriteTotalTimeoutConstant; // 写时间常量:写串口数据的固定超时。
//写总超时 = WriteTotalTimeoutMultiplier*字节数 + WriteTotalTimeoutConstant
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
如果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错误码解释如下:
1-CE_BREAK:检测到中断信号。意思是说检测到某个字节数据缺少合法的停止位。
2-CE_FRAME:硬件检测到帧错误。
3-CE_IOE:通信设备发生输入/输出错误。
4-CE_MODE:设置模式错误,或是hFile值错误。
5-CE_OVERRUN:溢出错误,缓冲区容量不足,数据将丢失。
6-CE_RXOVER:溢出错误。
7-CE_RXPARITY:硬件检查到校验位错误。
8-CE_TXFULL:发送缓冲区已满。
lpStat通讯状态结构体如下:
typedef struct _COMSTAT{
...
...
DWORD cbInQue; //输入缓冲区中的字节数
DWORD cbOutQue;//输出缓冲区中的字节数
}COMSTAT,*LPCOMSTAT;
该结构中对我们很重要的只有上面两个参数,其他的我们可以不用管。
举例:
unsigned char ucRxBuff[20];
COMSTAT ComStat;
DWORD dwError=0;
DWORD BytesRead=0;
OVERLAPPED ov_Read;
ov_Read.hEvent=CreateEvent(NULL, true, false, NULL);//必须创建有效事件
ClearCommError(hComm,&dwError,&ComStat);//检查串口接收缓冲区中的数据个数
bResult=ReadFile(hComm,
ucRxBuff,
ComStat.cbInQue,
&BytesRead,
&ov_Read);
//假如当前串口中有5个字节数据的话,那么执行完ClearCommError()函数后,ComStat \
结构中的ComStat.cbInQue将被填充为5,此值在ReadFile函数中可被直接利用。
6、其它串口通信常用API
以下转自:http://blog.youkuaiyun.com/vodomine/article/details/6542089
PurgeComm()
用途:清空串口缓冲区,在读写串口之前,还要用PurgeComm()函数清空缓冲区。
原型:BOOL PurgeComm(HANDLE hFile, DWORD dwFlags );
参数说明:
-hFile:串口句柄
-dwFlags:指定串口执行的动作,由以下参数组成:
-PURGE_TXABORT:停止目前所有的传输工作立即返回不管是否完成传输动作。
-PURGE_RXABORT:停止目前所有的读取工作立即返回不管是否完成读取动作。
-PURGE_TXCLEAR:清除发送缓冲区的所有数据。
-PURGE_RXCLEAR:清除接收缓冲区的所有数据。
操作举例:
PurgeComm(hComm, PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
//清除串口的所有操作。
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:输出缓冲区中的数据已被完全送出。
操作举例:
SetCommMask(hComm,EV_RXCHAR|EV_TXEMPTY);
//上面函数执行完毕后将监视串口中有无数据和发送缓冲区中的数据是否全部发送完毕。
WaitCommEvent()
用途:用来判断用SetCommMask()函数设置的串口通信事件是否已发生。
原型:
BOOL WINAPI WaitCommEvent( _In_ HANDLE hFile, _Out_ LPDWORD lpEvtMask, _In_ LPOVERLAPPED lpOverlapped );参数说明:
-hFile:串口句柄
-lpEvtMask:函数执行完后如果检测到串口通信事件的话就将其写入该参数中。
-lpOverlapped:指向重叠结构,如果串口打开时指定了FILE_FLAG_OVERLAPPED标志 ,则改参数不能为NULL,且重叠结构中 应该包含一个手工重置对象句柄(通过CreateEvent()创建)。
操作举例:
DWORD dwMask,dwTrans,dwError=0,err;
OVERLAPPED os;
memset(&os,0,sizeof(OVERLAPPED));
os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!WaitCommEvent(hComm,&dwMask,&os))
{
//如果异步操作不能立即完成的话,函数返回FALSE,并且调用GetLastError()函
//数分析错误原因后返回ERROR_IO_PENDING,指示异步操作正在后台进行.这种情
//况下,在函数返回之前系统设置OVERLAPPED结构中的事件为无信号状态,该函数
//等待用SetCommMask()函数设置的串口事件发生,共有9种事件可被监视:
//EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,
//EV_RXFLAG,EV_TXEMPTY;当其中一个事件发生或错误发生时,函数将
//OVERLAPPED结构中的事件置为有信号状态,并将事件掩码填充到dwMask参数中
if(GetLastError()==ERROR_IO_PENDING)
{
/**************************************************************/
/*在此等待异步操作结果,直到异步操作结束时才返回.实际上此时 */
/*WaitCommEvent()函数一直在等待串口监控的事件之一发生,当事件发*/
/*生时该函数将OVERLAPPED结构中的事件句柄置为有信号状态,此时 */
/*GetOverlappedResult()函数发现此事件有信号后马上返回,然后下面*/
/*的程序马上分析WaitCommEvent()函数等到的事件是被监视的串口事 */
/*件中的哪一个,然后执行相应的动作并发出相应消息. */
/**************************************************************/
GetOverlappedResult(hComm,&os,&dwTrans,true);
switch(dwMask)
{
case EV_RXCHAR:
PostMessage(Parent,WM_COMM_RXCHAR,0,0);
break;
case EV_TXEMPTY:
PostMessage(Parent,WM_COMM_TXEMPTY,0,0);
break;
case EV_ERR:
switch(dwError)
{
case CE_FRAME:
err=0;
break;
case CE_OVERRUN:
err=1;
break;
case CE_RXPARITY:
err=2;
break;
default:break;
}
PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err);
break;
case EV_BREAK:
PostMessage(Parent,WM_COMM_BREAK,0,0);
break;
case ...://其他用SetCommMask()函数设置的被监视的串口通信事件。
... ...
break;
default:break;
}
}
}
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会保存具体发生的事件。
#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <stdio.h>
void _tmain(
int argc,
TCHAR *argv[]
)
{
HANDLE hCom;
OVERLAPPED o;
BOOL fSuccess;
DWORD dwEvtMask;
hCom = CreateFile( TEXT("\\\\.\\COM1"),
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // default security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return;
}
// Set the event mask.
fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR);
if (!fSuccess)
{
// Handle the error.
printf("SetCommMask failed with error %d.\n", GetLastError());
return;
}
// Create an event object for use by WaitCommEvent.
o.hEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
NULL // no name
);
// Initialize the rest of the OVERLAPPED structure to zero.
o.Internal = 0;
o.InternalHigh = 0;
o.Offset = 0;
o.OffsetHigh = 0;
assert(o.hEvent);
if (WaitCommEvent(hCom, &dwEvtMask, &o))
{
if (dwEvtMask & EV_DSR)
{
// To do.
}
if (dwEvtMask & EV_CTS)
{
// To do.
}
}
else
{
DWORD dwRet = GetLastError();
if( ERROR_IO_PENDING == dwRet)
{
printf("I/O is pending...\n");
// To do.
}
else
printf("Wait failed with error %d.\n", GetLastError());
}
}
7、串口通信API流程
无论那种操作方式,一般都通过四个步骤来完成:
(1) 打开串口
(2) 设置和配置串口
(3) 读写串口
(4) 关闭串口
打开串口:
HANDLE hCom;
DWORD dwError;
hCom = CreateFile("COM1",//对串口1进行操作
GENERIC_READ|GENERIC_WRITE,//允许读和写
0,//独占方式
NULL,//默认安全属性
OPEN_EXISTING,//串口必须存在
0,//同步方式,重叠方式:FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED
NULL
);
if(hCom == INVALID_HANDLE_VALUE)
{
AfxMessageBox(L"打开串口失败");
return FALSE;
}
设置和配置串口:
<span style="font-size:14px;">//设置缓冲区大小
SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024
//设置超时
COMMTIMEOUTS TimeOuts;
TimeOuts.ReadIntervalTimeout=1000; //设定读超时
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000;
TimeOuts.WriteTotalTimeoutMultiplier=500; //设定写超时
TimeOuts.WriteTotalTimeoutConstant=5000;
SetCommTimeouts(hCom,&TimeOuts); //设置超时
//配置串口
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率为9600
dcb.ByteSize=8; //每个字节有8位
dcb.Parity=NOPARITY; //无校验
dcb.StopBits=TWOSTOPBITS; //两个停止位
SetCommState(hCom,&dcb);
//清空缓冲区
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);//清空接收和发送缓冲区 </span>
读写串口(异步):
//重叠I/O非常灵活,它也可以实现阻塞(例如我们可以设置一定要读取到一个数据才能进行到下一步操作)。有两种方法可以等待操作完成:一种方法是用
//像WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员;另一种方法是调用GetOverlappedResult函数等待,后面将演示说明。
/*******使用WaitForSingleObject函数来等待OVERLAPPED结构的hEvent成员*******/
//在使用ReadFile和WriteFile重叠操作时,线程需要创建OVERLAPPED结构以供这两个函数使用。
//线程通过OVERLAPPED结构获得当前的操作状态,该结构最重要的成员是hEvent。hEvent是读写事件。
//当串口使用异步通讯时,函数返回时操作可能还没有完成,程序可以通过检查该事件得知是否读写完毕。
//当调用ReadFile, WriteFile 函数的时候,该成员会自动被置为无信号状态;当重叠操作完成后,
//该成员变量会自动被置为有信号状态。
char lpInBuffer[1024];
DWORD dwBytesRead=1024;
COMSTAT ComStat;
DWORD dwErrorFlags;
OVERLAPPED m_osRead;
memset(&m_osRead,0,sizeof(OVERLAPPED));
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
ClearCommError(hCom,&dwErrorFlags,&ComStat);//清除错误,获得输入缓冲区中字节数
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
if(!dwBytesRead)
return FALSE;
BOOL bReadStatus;
bReadStatus=ReadFile(hCom,lpInBuffer, dwBytesRead,&dwBytesRead,&m_osRead);//读串口
if(!bReadStatus) //如果ReadFile函数返回FALSE
{
if(GetLastError()==ERROR_IO_PENDING) //GetLastError()函数返回ERROR_IO_PENDING,表明串口正在进行读操作
{
WaitForSingleObject(m_osRead.hEvent,2000); //使用WaitForSingleObject函数等待,直到读操作完成或延时已达到2秒钟
//当串口读操作进行完毕后,m_osRead的hEvent事件会变为有信号
PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);//情况缓冲区
return dwBytesRead;
}
return 0;
}
PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
return dwBytesRead;
/************使用GetOverlappedResult()函数*************/
//函数返回重叠操作的结果,用来判断异步操作是否完成,它是通过判断OVERLAPPED结构中的
//hEvent是否被置位来实现的。
char lpInBuffer[1024];
DWORD dwBytesRead=1024;
BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;
OVERLAPPED m_osRead;
ClearCommError(hCom,&dwErrorFlags,&ComStat);//清除错误,获得输入缓冲区中字节数
if(!ComStat.cbInQue)
return 0;
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
bReadStatus=ReadFile(hCom, lpInBuffer,dwBytesRead, &dwBytesRead,&m_osRead);//读串口
if(!bReadStatus) //如果ReadFile函数返回FALSE
{
if(GetLastError()==ERROR_IO_PENDING)
{
GetOverlappedResult(hCom, &m_osRead,&dwBytesRead,TRUE); // GetOverlappedResult函数的最后一个参数设为TRUE, //函数会一直等待,直到读操作完成或由于错误而返回。
return dwBytesRead;
}
return 0;
}
return dwBytesRead;
关闭串口:
CloseHandle(hComm);