串口开发,需要用到一些工具,这里我推荐 :
虚拟串口工具(Virtual Serial Port Driver) : http://www.eltima.com/products/vspdxp/,这个是收费的,可以百度个破解版本的.
串口调试工具(AccessPort) :http://www.sudt.com/,这个是免费的,随便下.
虚拟串口工具 : 当我们没有串口,但我们又需要测试我们的串口代码,怎么办?
通过软件虚拟出来!
Virtual Serial Port Driver就是这一类型的工具,软件打开的主界面 :
如上图:Physical ports : 我家里面的电脑的确没有串口连接
Virtual ports : 也没有创建任何的虚拟串口
点击右侧的Add pair,产生了一对的虚拟串口,如下图:
这一对串口的关系是 : COM1发送的数据,COM2接收,COM2发送的数据,COM1接收.
这样,我们在代码里面操作COM1,就可以使用串口调试工具打开COM2完成数据的接收和发送.
下面截个串口工具AccessPort的主界面吧 :
看上图: COM2已经给我打开了,在虚拟串口工具中也可以看见,如下图:
废话不多说,直接上代码.在代码之后写了一些Win32串口API使用的说明.
1.SerialPort.h
#ifndef TRAIN_SERIAL_PORT_H
#define TRAIN_SERIAL_PORT_H
// headers
#include <windows.h>
// end headers
// macros
#ifndef NULL
#define NULL ((void *)0)
#endif // NULL
#ifndef TRUE
#define TRUE 1
#endif // TRUE
#ifndef FALSE
#define FALSE 0
#endif // FALSE
#define PORT_IN_BUF_SIZE 8192 // size of the device's internal input buffer
#define PORT_OUT_BUF_SIZE 8192 // size of the device's internal output buffer
// end macros
// enums
enum PORT_ERROR{
PORT_SUCCESS = 0,
PORT_ERROR_INVALID_PARA = 0-1,
PORT_ERROR_OPEN = 0-2,
PORT_ERROR_SET_BUF = 0-3,
PORT_ERROR_SET_TIMEOUT = 0-4,
PORT_ERROR_GET_DCB = 0-5,
PORT_ERROR_SET_DCB = 0-6,
PORT_ERROR_INVALID_HANDLE = 0-7,
PORT_INVALID_CLR_TYPE = 0-8,
PORT_ERROR_CLEAR_BUFFER = 0-9,
PORT_ERROR_WRITE_FILE = 0-10,
PORT_ERROR_WRITE_TIMEOUT = 0-11,
PORT_ERROR_WRITE_NOT_EQUAL = 0-12,
PORT_ERROR_READ_FILE = 0-13,
PORT_ERROR_READ_TIMEOUT = 0-14,
PORT_ERROR_CREATE_EVENT = 0-15,
PORT_ERROR_GET_RESULT = 0-16,
PORT_ERROR_SET_MASK = 0-17,
PORT_ERROR_END = 0-100
};
enum PORT_CLRBUF_TYPE{
PORT_CLEAR_RX,
PORT_CLEAR_TX,
PORT_CLEAR_RX_TX
};
// end enums
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
void PortSetAsync(const bAsync);
int PortClose(HANDLE *handle);
int PortOpen(HANDLE *handle, const unsigned char *pszPortName, const int iBaudRate);
int PortClearBuffer(HANDLE *handle, enum PORT_CLRBUF_TYPE eClrType);
int PortSends(HANDLE *handle, const char *pszSendData, const unsigned long ulWriteLen, unsigned long *pOutWrittens);
int PortRecvs(HANDLE *handle, char *psInRecvData, const unsigned long ulMaxReadBytes, unsigned long *pulOutRecvBytes);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // TRAIN_SERIAL_PORT_H
// end of file
2.SerialPort.c
#include"SerialPort.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
static int sg_bAsync = FALSE;
// set synchronization or asynchronization
// TRUE-asynchronize, FALSE-synchronization
void PortSetAsync(const bAsync)
{
if(TRUE == bAsync)
{
sg_bAsync = FILE_FLAG_OVERLAPPED;
}
else
{
sg_bAsync =0;
}
}
// open serial port,return the handle to operate serial port
// pszPortName should be like COM1,COM2...COM99,the max is COM99
int PortOpen(HANDLE *handle, const unsigned char *pszPortName, const int iBaudRate)
{
int iRet;
int iPortNum;
unsigned char szPortNum[3+1];
unsigned char szPortName[12+1];
COMMTIMEOUTS stCommTimeouts;
DCB stDcb; // device-control block
if(NULL == pszPortName || MAX_PATH < strlen(pszPortName) ||
(9600 != iBaudRate && 115200 != iBaudRate))
{
return PORT_ERROR_INVALID_PARA;
}
if(strlen(pszPortName)<4 || strlen(pszPortName)>5)
{
return PORT_ERROR_INVALID_PARA;
}
memset(szPortName, 0x00, sizeof(szPortName));
strncpy(szPortName, pszPortName, 3);
strcpy(szPortNum, pszPortName+3);
iPortNum = 0;
iPortNum = atoi(szPortNum);
if(strcmp(szPortName, "COM") !=0 || iPortNum==0)
{
return PORT_ERROR_INVALID_PARA;
}
// if port number is more than 9,it should format like this "\\\\.\\COMxx"
if(iPortNum > 9)
{
sprintf(szPortName, "\\\\.\\%s", pszPortName);
}
else
{
strcpy(szPortName, pszPortName);
}
*handle = CreateFile(szPortName, // COM name
GENERIC_READ | GENERIC_WRITE, // allow write and read
0, // not share with other process
NULL,
OPEN_EXISTING, // serial port must be open,not create
sg_bAsync, // synchronization or asynchronization
NULL
);
if(INVALID_HANDLE_VALUE == *handle)
{
return PORT_ERROR_OPEN;
}
iRet = SetupComm(*handle, PORT_IN_BUF_SIZE, PORT_OUT_BUF_SIZE); // if fails,return 0
if(FALSE == iRet)
{
return PORT_ERROR_SET_BUF;
}
// set timeout for read
stCommTimeouts.ReadIntervalTimeout = 500;
stCommTimeouts.ReadTotalTimeoutMultiplier = 2000;
stCommTimeouts.ReadTotalTimeoutConstant = 5000;
// set timeout for write
stCommTimeouts.WriteTotalTimeoutMultiplier = 100;
stCommTimeouts.WriteTotalTimeoutConstant = 3000;
iRet = SetCommTimeouts(*handle, &stCommTimeouts);
if(FALSE == iRet)
{
return PORT_ERROR_SET_TIMEOUT;
}
// get current configuration,then set only a few members of the DCB structure
iRet = GetCommState(*handle, &stDcb);
if(FALSE == iRet)
{
return PORT_ERROR_GET_DCB;
}
// change only a few members of the DCB structure
stDcb.BaudRate = iBaudRate;
stDcb.ByteSize = 8;
stDcb.Parity = NOPARITY;
stDcb.StopBits = ONESTOPBIT;
iRet = SetCommState(*handle, &stDcb);
if(FALSE == iRet)
{
return PORT_ERROR_SET_DCB;
}
iRet = SetCommMask(*handle, EV_RXCHAR|EV_TXEMPTY);
if(FALSE == iRet)
{
return PORT_ERROR_SET_MASK;
}
return PortClearBuffer(handle, PORT_CLEAR_RX_TX);
}
// clear serial port receive and send buffer
int PortClearBuffer(HANDLE *handle, enum PORT_CLRBUF_TYPE eClrType)
{
int iRet;
if(INVALID_HANDLE_VALUE == *handle)
{
return PORT_ERROR_INVALID_HANDLE;
}
if(PORT_CLEAR_RX > eClrType || PORT_CLEAR_RX_TX < eClrType)
{
return PORT_INVALID_CLR_TYPE;
}
if(PORT_CLEAR_TX == eClrType)
{
iRet = PurgeComm(*handle, PURGE_TXABORT|PURGE_TXCLEAR);
}
else if(PORT_CLEAR_RX == eClrType)
{
iRet = PurgeComm(*handle, PURGE_RXABORT|PURGE_RXCLEAR);
}
else if(PORT_CLEAR_RX_TX == eClrType)
{
iRet = PurgeComm(*handle, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
}
if(FALSE == iRet)
{
return PORT_ERROR_CLEAR_BUFFER;
}
return PORT_SUCCESS;
}
// close handle
int PortClose(HANDLE *handle)
{
int iRet;
if(INVALID_HANDLE_VALUE == *handle)
{
return PORT_ERROR_INVALID_HANDLE;
}
iRet = CloseHandle(*handle);
*handle = INVALID_HANDLE_VALUE;
return !iRet;
}
// send data to serial port
int PortSends(HANDLE *handle, const char *pszSendData, const unsigned long ulWriteLen, unsigned long *pOutWrittens)
{
int iRet;
COMSTAT stComStat;
unsigned long ulWrittens;
unsigned long ulErrFlags;
OVERLAPPED stOverLapped;
if(INVALID_HANDLE_VALUE == *handle)
{
return PORT_ERROR_INVALID_HANDLE;
}
if( ulWriteLen<=0 || NULL==pszSendData )
{
return PORT_ERROR_INVALID_PARA;
}
if(FILE_FLAG_OVERLAPPED == sg_bAsync)
{
stOverLapped.hEvent = CreateEvent( NULL, TRUE,FALSE,NULL);
stOverLapped.Offset = 0;
stOverLapped.OffsetHigh = 0;
if( NULL == stOverLapped.hEvent )
{
return PORT_ERROR_CREATE_EVENT;
}
iRet = WriteFile(*handle, pszSendData, ulWriteLen, &ulWrittens, &stOverLapped);
}
else
{
iRet = WriteFile(*handle, pszSendData, ulWriteLen, &ulWrittens, NULL);
}
if(FALSE == iRet)
{
if(0 == sg_bAsync)
{
return PORT_ERROR_WRITE_FILE;
}
else
{
DWORD dwLastError = GetLastError();
if( dwLastError == ERROR_IO_PENDING )
{
DWORD dwWaitResult = WaitForSingleObject(stOverLapped.hEvent, 5000);
// PortClearBuffer(handle, PORT_CLEAR_TX);
CloseHandle(stOverLapped.hEvent);
if( dwWaitResult == WAIT_FAILED )
{
return PORT_ERROR_WRITE_FILE;
}
else if(dwWaitResult == WAIT_ABANDONED || dwWaitResult == WAIT_OBJECT_0)
{
// do nothing
}
else if(dwWaitResult == WAIT_TIMEOUT)
{
return PORT_ERROR_WRITE_TIMEOUT;
}
}
else
{
return PORT_ERROR_WRITE_FILE;
}
}
}
if(FILE_FLAG_OVERLAPPED == sg_bAsync)
{
iRet = GetOverlappedResult(*handle, &stOverLapped, &ulWrittens, TRUE);
if(0 == iRet)
{
return PORT_ERROR_GET_RESULT;
}
}
if(ulWriteLen != ulWrittens)
{
return PORT_ERROR_WRITE_NOT_EQUAL;
}
// clear transmit error information
ClearCommError(*handle, &ulErrFlags, &stComStat);
*pOutWrittens = ulWrittens;
return PORT_SUCCESS;
}
// read data from serial port
int PortRecvs(HANDLE *handle, char *psInRecvData, const unsigned long ulMaxReadBytes, unsigned long *pulOutRecvBytes)
{
int iRet;
unsigned long ulRecvBytes;
OVERLAPPED stOverLapped;
if(NULL == psInRecvData || 0 >= ulMaxReadBytes)
{
return PORT_ERROR_INVALID_PARA;
}
if(FILE_FLAG_OVERLAPPED == sg_bAsync)
{
stOverLapped.hEvent = CreateEvent( NULL, TRUE,FALSE,NULL);
stOverLapped.Offset = 0;
stOverLapped.OffsetHigh = 0;
if( NULL == stOverLapped.hEvent )
{
return PORT_ERROR_CREATE_EVENT;
}
iRet = ReadFile(*handle, psInRecvData, ulMaxReadBytes, &ulRecvBytes, &stOverLapped);
}
else
{
iRet = ReadFile(*handle, psInRecvData, ulMaxReadBytes, &ulRecvBytes, NULL);
}
if(FALSE == iRet)
{
if(0 == sg_bAsync)
{
return PORT_ERROR_READ_FILE;
}
else
{
DWORD dwLastError = GetLastError();
if( dwLastError == ERROR_IO_PENDING )
{
DWORD dwWaitResult = WaitForSingleObject(stOverLapped.hEvent, 7000);
// PortClearBuffer(handle, PORT_CLEAR_RX);
CloseHandle(stOverLapped.hEvent);
if( dwWaitResult == WAIT_FAILED )
{
return PORT_ERROR_READ_FILE;
}
else if(dwWaitResult == WAIT_ABANDONED || dwWaitResult == WAIT_OBJECT_0)
{
// do nothing
}
else if(dwWaitResult == WAIT_TIMEOUT)
{
return PORT_ERROR_READ_TIMEOUT;
}
}
else
{
return PORT_ERROR_READ_FILE;
}
}
}
if(FILE_FLAG_OVERLAPPED == sg_bAsync)
{
iRet = GetOverlappedResult(*handle, &stOverLapped, &ulRecvBytes, TRUE);
if(0 == iRet)
{
return PORT_ERROR_GET_RESULT;
}
}
*pulOutRecvBytes = ulRecvBytes;
return PORT_SUCCESS;
}
// end of file
使用参考 :
3.main.c
#include "SerialPort.h"
int main(void)
{
HANDLE handle;
int iRet;
unsigned long ulSends;
unsigned long ulRecvs;
unsigned char szRecvBuffer[1024];
PortSetAsync(TRUE);
iRet = PortOpen(&handle, "COM2", 115200);
if(0 == iRet)
{
printf("PortOpen Succeed\n");
}
else
{
printf("PortOpen Fail\n");
return -1;
}
iRet = PortSends(&handle, "123", 3, &ulSends);
if(0 == iRet)
{
printf("PortSends Succeed, SendBytes=%d\n", ulSends);
}
else
{
printf("PortSends Fail\n");
PortClose(&handle);
return -2;
}
memset(szRecvBuffer, 0x00, sizeof(szRecvBuffer));
iRet = PortRecvs(&handle, szRecvBuffer, 1024, &ulRecvs);
if(0 == iRet)
{
printf("PortRecvs Succeed, RecvBytes=%d\n", ulRecvs);
printf("RecvData : %s\n", szRecvBuffer);
}
else
{
printf("PortRecvs Fail\n");
PortClose(&handle);
return -2;
}
iRet = PortClose(&handle);
if(0 == iRet)
{
printf("PortClose Succeed\n");
}
else
{
printf("PortClose Fail\n");
return -2;
}
return 0;
}
// end of file
4.演示例子
先看运行效果,如图:
代码如下 :
main.c
#include "SerialPort.h"
int main(void)
{
HANDLE handle;
int iCnt;
int iRet;
unsigned long ulSends;
unsigned long ulRecvs;
unsigned char szRecvBuffer[1024];
PortSetAsync(TRUE);
iCnt = 0;
while(1)
{
iRet = PortOpen(&handle, "COM3", 115200);
if(0 == iRet)
{
printf("PortOpen Succeed\n");
break;
}
else
{
printf("PortOpen Fail, iCnt=%d, Retrying...\n", ++iCnt);
Sleep(10);
if(iCnt > 10000 * 10)
{
return 0;
}
continue;
}
}
iCnt = 0;
while(1)
{
iRet = PortSends(&handle, "Ready?", 6, &ulSends);
if(0 == iRet)
{
printf("Handshaking..., SendBytes=%d\n", ulSends);
memset(szRecvBuffer, 0x00, sizeof(szRecvBuffer));
iRet = PortRecvs(&handle, szRecvBuffer, 1, &ulRecvs);
if(0 == iRet)
{
if(0 != ulRecvs)
{
printf("PortRecvs Succeed, RecvBytes=%d\n", ulRecvs);
printf("RecvData : %s\n", szRecvBuffer);
}
if(szRecvBuffer[0] == 'Y')
{
printf("Handshake succeeds\n");
break;
}
else
{
if(0 != ulRecvs)
{
printf("Handshaking Fail : Not Matched, iCnt=%d, Retrying...\n", ++iCnt);
}
else
{
printf("Handshaking Fail, iCnt=%d, Retrying...\n", ++iCnt);
}
PortClearBuffer(&handle, PORT_CLEAR_RX_TX);
Sleep(10);
if(iCnt > 10000 * 10)
{
PortClose(&handle);
return 0;
}
continue;
}
}
else
{
printf("Handshaking Fail, iCnt=%d, Retrying...\n", ++iCnt);
Sleep(10);
if(iCnt > 10000 * 10)
{
PortClose(&handle);
return 0;
}
continue;
}
}
else
{
PortClearBuffer(&handle, PORT_CLEAR_RX_TX);
printf("Retry..., iCnt=%d", iCnt);
if(iCnt > 3)
{
printf("PortSends Fail\n");
PortClose(&handle);
return 0;
}
Sleep(10);
continue;
}
}
iCnt = 0;
while(1)
{
memset(szRecvBuffer, 0x00, sizeof(szRecvBuffer));
iRet = PortRecvs(&handle, szRecvBuffer, 4, &ulRecvs);
if(0 == iRet)
{
printf("PortRecvs Succeed, RecvBytes=%d\n", ulRecvs);
printf("RecvData : %s\n", szRecvBuffer);
if(strcmp(szRecvBuffer, "CMD1") == 0)
{
PortSends(&handle, "Welcome!", strlen("Welcome!"), &ulSends);
}
else if(strcmp(szRecvBuffer, "CMD2") == 0)
{
PortSends(&handle, "I miss you!", strlen("I miss you!"), &ulSends);
}
else if(strcmp(szRecvBuffer, "CMD3") == 0)
{
PortSends(&handle, "I waut for you!", strlen("I waut for you!"), &ulSends);
}
else if(strncmp(szRecvBuffer, "END", 3) == 0)
{
break;
}
}
else
{
printf("Waiting CMD, iCnt=%d, Retry...\n", ++iCnt);
continue;
}
PortClearBuffer(&handle, PORT_CLEAR_RX_TX);
}
iRet = PortClose(&handle);
if(0 == iRet)
{
printf("PortClose Succeed\n");
}
else
{
printf("PortClose Fail\n");
return -2;
}
return 0;
}
// end of file
Win32串口API使用说明:
1.CreateFile
原型 :
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
lpFileName : [入参] Com Port名字,如COM1,COM2,...,COM Port名字长度不能超过MAX_PATH
如果COM1到COM9,直接可以使用,但如果是COM10以上,则要格式化成\\\\.\\COMxx
考虑到串口很少情况会使用超过COM100的,所以PortOpen接口对此作了限制。
dwDesiredAccess : [入参] 串口访问权限,包括读(GENERIC_READ)/写(GENERIC_WRITE),
对串口操作来说,一般需要同时具备读写权限,即GENERIC_READ | GENERIC_WRITE
dwShareMode : [入参] 共享类型。即本进程打开串口后,限制其他进程访问串口的权限控制。
包括FILE_SHARE_READ/FILE_SHARE_WRITE/FILE_SHARE_DELETE三种共享权限。
该参数受第二个参数dwDesiredAccess的影响,如你当以GENERIC_READ权限打开串口
本来就天生不具备WRIET权限,现在你要共享FILE_SHARE_WRITE,这在现实生活中也说不通的。
同理以GENERIC_WRITE打开却共享FILE_SHARE_READ,也是这样。
这两种情况在其他进程共享打开时,都会返回错误码ERROR_SHARING_VIOLATION.
在实际项中一般不共享,传0即可。
lpSecurityAttributes : [入参] 该参数决定子进程是否继承CreateFile返回的句柄HANDLE,
参数为NULL,则子进程不继承,相反则继承。在开发中,传NULL的场景更多些。
dwCreationDisposition : [入参] 该参数决定是打开还是新建文件。对串口来说,必须是打开已经存在的。
即参数OPEN_EXISTING.
dwFlagsAndAttributes : [入参] 设置打开的文件的一些标志位和属性。对串口来说,该参数主要用于
表示串口是同步的还是异步的,0表示同步,FILE_FLAG_OVERLAPPED表示异步。
hTemplateFile : [入参] 对串口来说,这个参数没意义。因为串口是OPEN_EXISTING,CreateFile在调用时
会忽略参数hTemplateFile.
2.SetupComm
原型 :
BOOL SetupComm(
HANDLE hFile,
DWORD dwInQueue,
DWORD dwOutQueue
);
该函数主要设置串口通讯的读缓冲区和写缓冲区大小,如果在CreateFile打开串口后,没有调用SetupComm
设置缓冲区大小,则会使用默认的缓冲区大小。
3.COMMTIMEOUTS
原型 :
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS, *LPCOMMTIMEOUTS;
这里主要看看这个结构体各个成员的含义。
每个成员使用的超时单位都是以毫秒来计算的。
a.ReadIntervalTimeout :接收到第一个字节以后该参数才使用,计算每两个接收到的字节的隔间时间,如果
大于ReadIntervalTimeout,则立即返回读缓冲区中的数据。
b.ReadTotalTimeoutMultiplier : 乘数因子,计算方式为TotalTimeout = 乘数因子X 已经接收到的字节数
如果接收的字节数的实际使用时间大于计算出来的TotalTimeout,则超时了。
当然,总的超时时间还要依赖ReadTotalTimeoutConstant
c.ReadTotalTimeoutConstant : 常量因子,此时配合ReadTotalTimeoutMultiplier计算总的超时时间为:
TotalTimeout =乘数因子X 已经接收到的字节数 + 常量因子。
d.如果ReadTotalTimeoutmultiplier和ReadTotalTimeoutConstant同时设置为0,
则在接收字节时忽略总超时时间。
e.WriteTimeoutMultiplier和WriteTotalTimeoutConstant :原理同b,c,d。
4.SetCommTimeouts
原型 :
BOOL SetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
只要理解上面的结构体COMMTIMEOUT各个成员变量的含义,这个函数就比较简单了。
5.DCB
原型 :
typedef struct _DCB {
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary :1;
DWORD fParity :1;
DWORD fOutxCtsFlow :1;
DWORD fOutxDsrFlow :1;
DWORD fDtrControl :2;
DWORD fDsrSensitivity :1;
DWORD fTXContinueOnXoff :1;
DWORD fOutX :1;
DWORD fInX :1;
DWORD fErrorChar :1;
DWORD fNull :1;
DWORD fRtsControl :2;
DWORD fAbortOnError :1;
DWORD fDummy2 :17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
} DCB;
这个结构体成员变量很多,这里只针对BaudRate,ByteSize,Parity,StopBits作说明
BaudRate : 常用的波特率有 CBR_9600/CBR_57600/CBR_115200,当然也可以使用自定义的波特率。
ByteSize : 在接收到的一个字节中,使用多少二进制位表示实际数据。可以去5,6,7,8
Parity : 奇偶校验位,不同的值,校验算法不同。其值可以去EVENPARITY,MARKPARITY,NOPARITY
ODDPARITY,SPACEPARITY。
一般我选择NOPARITY,然后自己在应用层制定自己的传输协议,在协议中增加校验部分,
如LRC,CRC等校验。
StipBits : 停止位,其值为ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS
其中,ByteSize为5时,StopBits不能是TWOSTOPBITS,
ByteSize为6,7,8时,StopBits不能是ONE5STOPBITS
不然在CreateFile会报错,返回的句柄是INVALID_HANDLE_VALUE;
6.SetCommState
原型 :
BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB
);
只要了解DCB结构体,这个函数就简单了。