原创 C++串口API应用

2009-3-18 20:43 8132 6 8 分类: MCU/ 嵌入式

/***********************************************************************************************
          initialSerial
功能:串口初始化


参数:无


返回:无


***********************************************************************************************/


void initialSerial()   //串口初始化
{
 char szComParams[50];
 DCB dcb;
 char *m_com; char *m_baud; char *m_jiaoyan;
    m_com="Com1";
 m_baud="1200";
 m_jiaoyan="E";


 COMMTIMEOUTS CommTimeOuts;
 m_hIDComDev = NULL;
 m_hIDComDev = CreateFile(m_com, GENERIC_READ|GENERIC_WRITE, 0, NULL, 
         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  //打开串口


 if(m_hIDComDev==INVALID_HANDLE_VALUE)
 {
  AfxMessageBox("打开串口错误0,请检查!");
  goto endd;
 }
 if(m_hIDComDev ==(HANDLE) -1)
 {
  AfxMessageBox("打开串口错误,请检查!");
  goto endd;
 }


 SetCommTimeouts(m_hIDComDev, &CommTimeOuts);  //串口超时配置
 CommTimeOuts. ReadIntervalTimeout="0xFFFFFFFF";
 CommTimeOuts. ReadTotalTimeoutMultiplier = 0;
 CommTimeOuts. ReadTotalTimeoutConstant =5000;
 CommTimeOuts. WriteTotalTimeoutMultiplier = 0;
 CommTimeOuts. WriteTotalTimeoutConstant = 5000;
 PurgeComm(m_hIDComDev, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR ) ;
 m_com="Com1:38400,E,8,1";
 wsprintf(szComParams, m_com);   //设置串口参数


 dcb. DCBlength = sizeof(DCB);
 GetCommState(m_hIDComDev, &dcb);//


 int baud;
 baud = atoi(m_baud);
 dcb. BaudRate = baud;   //设置波特率 
 dcb. ByteSize= 8; //设置校验字节


 if ((!SetCommState(m_hIDComDev, &dcb))||(!SetupComm(m_hIDComDev,10000,10000)))//设置串口和收发缓冲器的大小
    {
  DWORD dwError = GetLastError();
  CloseHandle(m_hIDComDev);
 }
 PurgeComm(m_hIDComDev,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_TXABORT|PURGE_RXABORT);//清收发缓冲器
endd:
;
}
/************************************************************************************************
           SendData
功能:发送数据给串口


参数: buff发送的数据
  send_length   长度


返回:  成功 1  失败  0



************************************************************************************************/
DWORD SendData( unsigned char buff[],int send_length)    //发送数据
{  
 int t;
 DWORD dwBytesWritten;
 if(!WriteFile(m_hIDComDev,buff,send_length,&dwBytesWritten,NULL))
 {
  return 0;
 }
 for (t=0;t  fprintf(stdout," %2X ",buff[t]);
    printf("\n");
 PurgeComm(m_hIDComDev,PURGE_TXCLEAR); //清发送缓冲区
    return 1;
}
/***********************************************************************************************
          ReadData
功能:从串口读取数据


参数:rebuff  收到的数据  dwBytesRead   要收到的长度    delay   延时



***********************************************************************************************/
DWORD ReadData(unsigned char rebuff [],DWORD dwBytesRead )  //读取数据
{
 DWORD dwErrorFlags;
 COMSTAT stat;
 if(delay>0)                                                                                   
  ClearCommError(m_hIDComDev,&dwErrorFlags,&stat); 
 if(stat.cbInQue <= 0)  //"stat.cbInQue" is  bytes in input buffer
  return 0;
 dwBytesRead = min(dwBytesRead,(DWORD)stat.cbInQue); //获取字符个数
 
 if(!ReadFile(m_hIDComDev,rebuff,dwBytesRead,&dwBytesRead,NULL))  //整体读入
 {
  
  return 0;
 }
 return dwBytesRead;
}
下面三个函数中所用到结构本OR函数


COMMTIMEOUTS


The COMMTIMEOUTS structure is used in the SetCommTimeouts and GetCommTimeouts functions to set and query the time-out parameters for a communications device. The parameters determine the behavior of ReadFile, WriteFile, ReadFileEx, and WriteFileEx operations on the device.

typedef struct _COMMTIMEOUTS {  
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

Members


ReadIntervalTimeout
Specifies the maximum time, in milliseconds, allowed to elapse between the arrival of two characters on the communications line. During a ReadFile operation, the time period begins when the first character is received. If the interval between the arrival of any two characters exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.

A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the characters that have already been received, even if no characters have been received.


ReadTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for read operations. For each read operation, this value is multiplied by the requested number of bytes to be read.
ReadTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for read operations. For each read operation, this value is added to the product of the ReadTotalTimeoutMultiplier member and the requested number of bytes.

A value of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant members indicates that total time-outs are not used for read operations.


WriteTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for write operations. For each write operation, this value is multiplied by the number of bytes to be written.
WriteTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for write operations. For each write operation, this value is added to the product of the WriteTotalTimeoutMultiplier member and the number of bytes to be written.

A value of zero for both the WriteTotalTimeoutMultiplier and WriteTotalTimeoutConstant members indicates that total time-outs are not used for write operations.


PurgeComm


The PurgeComm function can discard all characters from the output or input buffer of a specified communications resource. It can also terminate pending read or write operations on the resource.

BOOL PurgeComm(
HANDLE
hFile, // handle to communications resource
DWORD dwFlags // action to perform
);

Parameters


hFile
Handle to the communications resource. The CreateFile function returns this handle.
dwFlags
Specifies the action to take. This parameter can be a combination of the following values:
ValueMeaning
PURGE_TXABORTTerminates all outstanding overlapped write operations and returns immediately, even if the write operations have not been completed.
PURGE_RXABORTTerminates all outstanding overlapped read operations and returns immediately, even if the read operations have not been completed.
PURGE_TXCLEARClears the output buffer (if the device driver has one).
PURGE_RXCLEARClears the input buffer (if the device driver has one).


Return Values


If the function succeeds, the return value is nonzero.


If the function fails, the return value is zero. To get extended error information, call GetLastError.


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


GetCommState


The GetCommState function fills in a device-control block (a DCB structure) with the current control settings for a specified communications device.

BOOL GetCommState(
HANDLE
hFile, // handle to communications device
LPDCB lpDCB // pointer to device-control block structure
);

Parameters


hFile
Handle to the communications device. The CreateFile function returns this handle.
lpDCB
Pointer to the DCB structure in which the control settings information is returned.

Return Values


If the function succeeds, the return value is nonzero.


If the function fails, the return value is zero. To get extended error information, call GetLastError.


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


SetCommState


The SetCommState function configures a communications device according to the specifications in a device-control block (a DCB structure). The function reinitializes all hardware and control settings, but it does not empty output or input queues.

BOOL SetCommState(
HANDLE
hFile, // handle to communications device
LPDCB lpDCB // pointer to device-control block structure
);

Parameters


hFile
Handle to the communications device. The CreateFile function returns this handle.
lpDCB
Pointer to a DCB structure containing the configuration information for the specified communications device.

Return Values


If the function succeeds, the return value is nonzero.


If the function fails, the return value is zero. To get extended error information, call GetLastError.


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


SetupComm


The SetupComm function initializes the communications parameters for a specified communications device.

BOOL SetupComm(
HANDLE
hFile, // handle to communications device
DWORD dwInQueue, // size of input buffer
DWORD dwOutQueue // size of output buffer
);

Parameters


hFile
Handle to the communications device. The CreateFile function returns this handle.
dwInQueue
Specifies the recommended size, in bytes, of the device's internal input buffer.
dwOutQueue
Specifies the recommended size, in bytes, of the device's internal output buffer.

Return Values


If the function succeeds, the return value is nonzero.


If the function fails, the return value is zero. To get extended error information, call GetLastError.


 


 


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


PurgeComm


The PurgeComm function can discard all characters from the output or input buffer of a specified communications resource. It can also terminate pending read or write operations on the resource.

BOOL PurgeComm(
HANDLE
hFile, // handle to communications resource
DWORD dwFlags // action to perform
);

Parameters


hFile
Handle to the communications resource. The CreateFile function returns this handle.
dwFlags
Specifies the action to take. This parameter can be a combination of the following values:
ValueMeaning
PURGE_TXABORTTerminates all outstanding overlapped write operations and returns immediately, even if the write operations have not been completed.
PURGE_RXABORTTerminates all outstanding overlapped read operations and returns immediately, even if the read operations have not been completed.
PURGE_TXCLEARClears the output buffer (if the device driver has one).
PURGE_RXCLEARClears the input buffer (if the device driver has one).


Return Values


If the function succeeds, the return value is nonzero.


If the function fails, the return value is zero. To get extended error information, call GetLastError.


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


COMSTAT


The COMSTAT structure contains information about a communications device. This structure is filled by the ClearCommError function.

typedef struct _COMSTAT { 
DWORD fCtsHold : 1; // Tx waiting for CTS signal
DWORD fDsrHold : 1; // Tx waiting for DSR signal
DWORD fRlsdHold : 1; // Tx waiting for RLSD signal
DWORD fXoffHold : 1; // Tx waiting, XOFF char received
DWORD fXoffSent : 1; // Tx waiting, XOFF char sent
DWORD fEof : 1; // EOF character sent
DWORD fTxim : 1; // character waiting for Tx
DWORD fReserved : 25; // reserved
DWORD cbInQue; // bytes in input buffer
DWORD cbOutQue; // bytes in output buffer
} COMSTAT, *LPCOMSTAT;

Members


fCtsHold
Specifies whether transmission is waiting for the CTS (clear-to-send) signal to be sent. If this member is TRUE, transmission is waiting.
fDsrHold
Specifies whether transmission is waiting for the DSR (data-set-ready) signal to be sent. If this member is TRUE, transmission is waiting.
fRlsdHold
Specifies whether transmission is waiting for the RLSD (receive-line-signal-detect) signal to be sent. If this member is TRUE, transmission is waiting.
fXoffHold
Specifies whether transmission is waiting because the XOFF character was received. If this member is TRUE, transmission is waiting.
fXoffSent
Specifies whether transmission is waiting because the XOFF character was transmitted. If this member is TRUE, transmission is waiting. Transmission halts when the XOFF character is transmitted to a system that takes the next character as XON, regardless of the actual character.
fEof
Specifies whether the end-of-file (EOF) character has been received. If this member is TRUE, the EOF character has been received.
fTxim
If this member is TRUE, there is a character queued for transmission that has come to the communications device by way of the TransmitCommChar function. The communications device transmits such a character ahead of other characters in the device's output buffer.
fReserved
Reserved; do not use.
cbInQue
Specifies the number of bytes received by the serial provider but not yet read by a ReadFile operation.
cbOutQue
Specifies the number of bytes of user data remaining to be transmitted for all write operations. This value will be zero for a nonoverlapped write.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


一,异步非阻塞串口通讯的优点


读写串行口时,既可以同步执行,也可以重叠(异步)执行。
在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。
在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。
例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。"重叠"一词的含义就在于此。


二,异步非阻塞串口通讯的基本原理
首先,确定要打开的串口名、波特率、奇偶校验方式、数据位、停止位,传递给CreateFile()函数打开特定串口;
其次,为了保护系统对串口的初始设置,调用 GetCommTimeouts()得到串口的原始超时设置;
然后,初始化DCB对象,调用SetCommState() 设置DCB,调用SetCommTimeouts()设置串口超时控制;
再次,调用SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置就基本完成,之后就可以启动读写线程了。


三,异步非阻塞串口通讯的基础知识
下面来介绍并举例说明一下编写异步非阻塞串口通讯的程序中将会使用到的几个关键函数


CreateFile()
功能:打开串口设备
函数原型
HANDLE CreateFile(
LPCTSTR lpFileName, // 串口名称字符串;如: "COM1" 或 "COM2"
DWORD dwDesiredAccess, // 设置读写属性(访问模式 );一般为 GENERIC_READ|GENERIC_WRITE,
DWORD dwShareMode, // 共享模式;"必须"为 0, 即不能共享
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性;一般为NULL
DWORD dwCreationDistribution, // 创建方式,串口设置必须设置此值; 在这里"必须"为 OPEN_EXISTING
DWORD dwFlagsAndAttributes, // 文件属性和标志;在这里我们设置成FILE_FLAG_OVERLAPPED ,实现异步I/O
HANDLE hTemplateFile // 临时文件的句柄,通常为NULL 
);
说明:
如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。
Forexample:


Handle m_hComm = CreateFile(com1,GENERIC_READ||GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);


CloseHandle();
功能:关闭串口
BOOL CloseHandle(
HANDLE hObject // handle to object to close

这个,我想就不多说了吧!


GetCommState()
功能:获得串口状态
BOOL GetCommState(
HANDLE hFile, // handle of communications device
LPDCB lpDCB // address of device-control block structure
);


SetCommState()
功能:设置串口状态 
BOOL SetCommState(
HANDLE hFile, // handle of communications device
LPDCB lpDCB // address of device-control block structure
);
说明:
在打开通信设备句柄后,常常需要对串行口进行一些初始化工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置置串行口的属性时,都要用DCB结构来作为缓冲区。
调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。一般在用CreateFile打开串行口后,可以调用GetCommState函数来获取串行口的初始配置。要修改串行口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口
Forexample:
DCB dcb;
memset(&dec,0,dizeof(dcb));
if(!GetCommState(HComm,&dcb))//获取当前DCB配置
 return FALSE; 
dcb.BaudRate = CBR_9600;//修改数据传输率
............
if(SetCommState(hComm,&dcb))//设置新参数
......    //错误处理


BuildCommDCB()
功能:初始化DCB结构
BOOL BuildCommDCB(
LPCTSTR lpDef, // pointer to device-control string
LPDCB lpDCB // pointer to device-control block
);
Forexample:
DCB dcb;
memset(&dcb,0,sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
if(!BuildCommDCb("9600,n,8,1",&dcb))//"baud=9600 parity="N" data="8" stop="1""
{
 ......  //参数修改错误
 return FALSE;
}
else
{
......  //己准备就绪
}
说明:功能同上面的例子。


SetupComm()
功能:设置I/O缓冲区的大小
函数原型:
BOOL SetupComm(
  HANDLE hFile,     // handle to communications device
  DWORD dwInQueue,  // size of input buffer
  DWORD dwOutQueue  // size of output buffer
);
说明:
除了在DCB中的设置外,程序一般还需要设置I/O缓冲区的大小和超时。Windows用I/O缓冲区来暂存串行口输入和输出的数据,如果通信的速率较高,则应该设置较大的缓冲区。调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。



先介绍一个结构:COMMTIMEOUTS


typedef struct _COMMTIMEOUTS { 
DWORD ReadIntervalTimeout; // 读间隔超时
DWORD ReadTotalTimeoutMultiplier; // 读时间系数
DWORD ReadTotalTimeoutConstant; // 读时间常量
DWORD WriteTotalTimeoutMultiplier; // 写时间系数
DWORD WriteTotalTimeoutConstant; // 写时间常量
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;


再介绍两个函数
GetCommTimeouts
功能:读取TimeOut的值
函数原型:
BOOL GetCommTimeouts(
HANDLE hFile, // handle of communications device
LPCOMMTIMEOUTS lpCommTimeouts // address of comm. time-outs structure
);
SetCommTimeouts
功能:设置TimeOUt的值
函数原型:
BOOL SetCommTimeouts(
HANDLE hFile, // handle of communications device
LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure
);


这里顺便介绍一下TimeOut机制的两个性质:


超时函数
说明:
在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。


  有两种超时:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共花费的最大时间。写操作只支持总超时,而读操作两种超时均支持。用COMMTIMEOUTS结构可以规定读/写操作的超时,该结构的定义为: COMMTIMEOUTS结构的成员都以毫秒为单位。总超时的计算公式是:


总超时=时间系数×要求读/写的字符数 + 时间常量


  例如,如果要读入10个字符,那么读操作的总超时的计算公式为:


读总超时=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant


  可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。


  如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且两个读总超时为0,那么在读一次输入缓冲区中的内容后读操作就立即完成,而不管是否读入了要求的字符。


  在用重叠方式读写串行口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。


Forexample:
COMMTIMEOUTS timeOver;
memset(&&timeOver.0.sizeof(timeOver));
DWORDtimeMultiplier,timeConstant;
timeOver.ReadTotalTimeoutMultiplier=timeMultiplier;
timeOver.ReadTotalTimeoutConstant=timeConstant;
SetCommTimeouts(hComport,&&timeOver);


ReadFile()
功能:读取数据
函数原型:
BOOL ReadFile(
HANDLE hFile, // 串口名称字符串(文件句柄 )
LPVOID lpBuffer, // 读缓冲区
DWORD nNumberOfBytesToRead, // 要求读入的字节数
LPDWORD lpNumberOfBytesRead, // 实际读入的字节数
LPOVERLAPPED lpOverlapped // 指向一个OVERLAPPED结构
); //若返回TRUE则表明操作成功


Forexample:
char *pReciveBuf;
DWORD nWantRead = 100,
 nReadRead;
LPOVERLAPPED m_OverlappedRead;
BOOL bReadStatus = ReadFile( m_hComm, preciveBuf,nWantRead, &&nReadRead, &&m_OverlappedRead );


WriteFile()
功能:来将资料写入Serial port.
函数原型:
BOOL WriteFile(
HANDLE hFile, // handle to file to write to
LPCVOID lpBuffer, // pointer to data to write to file
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
LPOVERLAPPED lpOverlapped // pointer to structure needed for overlapped I/O
);


说明:
 ReadFile函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作。
而WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串行口送出去后才算完成操作。


当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。


如果GetLastError函数返回ERROR_IO_PENDING,则说明重叠操作还为完成,线程可以等待操作完成。
有两种等待办法:一种办法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员,
可以规定等待的时间,在等待函数返回后,调用GetOverlappedResult。
另一种办法是调用GetOverlappedResult函数等待,如果指定该函数的bWait参数为TRUE,
那么该函数将等待OVERLAPPED结构的hEvent 事件。
GetOverlappedResult可以返回一个OVERLAPPED结构来报告包括实际传输字节在内的重叠操作结果。


如果规定了读/写操作的超时,那么当超过规定时间后,hEvent成员会变成有信号的。因此,在超时发生后,WaitForSingleObject和GetOverlappedResult都会结束等待。WaitForSingleObject的dwMilliseconds参数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值。注意GetOverlappedResult不能设置等待的时限,因此如果hEvent成员无信号,则该函数将一直等待下去


ClearCommError()
功能: 从字面上的意思看来, 它是用来清除错误情况用的, 但是实际上它还可以拿来取得目前通讯设备的一些信息.
函数原型:
BOOL ClearCommError(
HANDLE hFile, // handle to communications device
LPDWORD lpErrors, // pointer to variable to receive error codes
LPCOMSTAT lpStat // pointer to buffer for communications status
);
说明:
 在调用ReadFile和WriteFile之前,线程应该调用ClearCommError函数清除错误标志。
该函数负责报告指定的错误和设备的当前状态。



PurgeComm()
功能:终止目前正在进行的读或写的动作
函数原型:
BOOL PurgeComm(
HANDLE hFile, // handle of communications resource
DWORD dwFlags // action to perform
);
参数说明:
HANDLE hFile,//串口名称字符串
dwFlags 共有四种 flags:


  PURGE_TXABORT: 终止目前正在进行的(背景)写入动作
  PURGE_RXABORT: 终正目前正在进行的(背景)读取动作
  PURGE_TXCLEAR: flush 写入的 buffer
  PURGE_TXCLEAR: flush 读取的 buffer
调用PurgeComm函数可以终止正在进行的读写操作,该函数还会清除输入或输出缓冲区中的内容。


GetCommMask()
功能:得到设置的通信事件的掩码
函数原型:
BOOL GetCommMask(
HANDLE hFile, // handle of communications device
LPDWORD lpEvtMask // address of variable to get event mask
);


SetCommMask()
功能:设置想要得到的通信事件的掩码
函数原型:
BOOL SetCommMask(
HANDLE hFile, // handle of communications device
DWORD dwEvtMask // mask that identifies enabled events
);
说明:
可设置的通信事件标志(即SetCommMask()函数所设置的掩码)
可以有EV_BREAK、EV_CTS、EV_DSR、 EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY。


注:若对端口数据的响应时间要求较严格,可采用事件驱动I/O读写,Windows定义了9种串口通信事件,较常用的有:


  EV_RXCHAR: 接收到一个字节,并放入输入缓冲区。


  EV_TXEMPTY: 输出缓冲区中的最后一个字符发送出去。


  EV_RXFLAG: 接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区。


下面是MSDN上的解释:
EV_BREAK A break was detected on input.
EV_CTS The CTS (clear-to-send) signal changed state.
EV_DSR The DSR (data-set-ready) signal changed state.
EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING A ring indicator was detected.
EV_RLSD The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR A character was received and placed in the input buffer.
EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY The last character in the output buffer was sent.


WaitCommEvent()
功能:等待设定的通讯事件的发生
函数原型:
BOL WaitCommEvent(
HANDLE hFile, // handle of communications device
LPDWORD lpEvtMask, // address of variable for event that occurred
LPOVERLAPPED lpOverlapped, // address of overlapped structure
);
说明:
WaitCommEvent() 会一直 block(阻塞) 到你所设定的通讯事件发生为止.
所以当 WaitCommEvent() 返回时, 你可以由 lpEvtMask 取得究竟是那一事件发生, 再来决定要如何处理.


WaitForSingleObject()
功能:保证线程同步的等待函数
函数原型:
DWORD WaitForSingleObject(HANDLE hHandle,//同步对象的句柄
     DWORD dwMilliseconds//以毫秒为单位的超时间隔,如果设为INFINITE,则超时间隔是无限的
    );
说明:
返回值    含义 
WAIT_FAILED   函数失败
WAIT_OBJECT_0  指定的同步对象处于有信号的状态
WAIT_ABANDONED  拥有一个mutex的线程已经中断了,但未释放该MUTEX
WAIT_TIMEOUT   超时返回,并且同步对象无信号


WaitForMultipleObjects()
功能:可以同时监测多个同步对象
函数原型:
DWORD WaitForMultipleObjects(DWORD nCount,//句柄数组中句柄的数目
     CONST HANDLE *lpHandles,//代表一个句柄数组
     BOOL bWaitAll, //说明了等待类型(),如果为TRUE,那么函数在所有对象都有信号后才返回,
      //如果为FALSE,则只要有一个对象变成有信号的,函数就返回
    DWORD dwMilliseconds//以毫秒为单位的超时间隔
     );
说明:
 返回值        含义
WAIT_OBJECT_0到WAIT_ OBJECT_0+nCount-1    若bWaitAll为TRUE,则返回值表明所有对象都是有信号的。
       如果bWaitAll为FALSE,则返回值减去WAIT_OBJECT_0就是数组中有信号对       象的最小索引。
 
WAIT_ABANDONED_0到WAIT_ ABANDONED_ 0+nCount-1  若bWaitAll为TRUE,则返回值表明所有对象都有信号,但有一个mutex被       放弃了。若bWaitAll为FALSE,则返回值减去WAIT_ABANDONED_0就是被放弃       mutex在对象数组中的索引。
 WAIT_TIMEOUT      超时返回


 


实现重叠模型的步骤
下面就结合俺写的一个Console程序简单示例进行说明:


【第一步】打开串口
 HANDLE m_hCom = CreateFile("com1",GENERIC_READ | GENERIC_WRITE, 0, NULL,
    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 if (m_hCom == INVALID_HANDLE_VALUE)
 {
  cout<<"CreateFile fail!"<  return -1;
 }
 cout<<"CreateFile OK!"<


【第二步】设置缓冲区大小
 if(!SetupComm(m_hCom,2048,2048))
 {
  cout<<"SetupComm fail! Close Comm!"<  CloseHandle(m_hCom);
  return -1;
 }
 cout<<"SetupComm OK!"<


【第三步】设置超时
COMMTIMEOUTS TimeOuts;
memset(&TimeOuts,0,sizeof(TimeOuts));
TimeOuts.ReadIntervalTimeout = MAXDWORD;
TimeOuts.ReadTotalTimeoutConstant = 0;
TimeOuts.ReadTotalTimeoutMultiplier = 0;
TimeOuts.WriteTotalTimeoutConstant = 2000;
TimeOuts.WriteTotalTimeoutMultiplier = 50;
SetCommTimeouts(m_hCom,&TimeOuts);


【第四步】设置串口参数
       DCB dcb;
 if (!GetCommState(m_hCom,&dcb))
 {
  cout<<"GetCommState fail! Comm close"<  CloseHandle(m_hCom);
  return -1;
 }
 cout<<"GetCommState OK!"< 
 dcb.DCBlength = sizeof(dcb);
 if (!BuildCommDCB("9600,n,8,1",&dcb))//填充DCB的数据传输率、奇偶校验类型、数据位、停止位
 {
  //参数修改错误,进行错误处理
  cout<<"BuileCOmmDCB fail,Comm close!"<  CloseHandle(m_hCom);
  return -1;
  }
 if(SetCommState(m_hCom,&dcb))
 {
  cout<<"SetCommState OK!"< }


【第五步】建立并初始化重叠结构
 OVERLAPPED wrOverlapped;
 ZeroMemory(&wrOverlapped,sizeof(wrOverlapped));
 if (wrOverlapped.hEvent != NULL)
 {
  ResetEvent(wrOverlapped.hEvent);
  wrOverlapped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
 }


【第六步】封装数据(按照自己的格式封装需要发送的数据,此步可以省略)
typedef enum
{
 HEAT_BEAT, //心跳数据
 NET_STATE,//网络状态数据
 PACKET  //正常数据包
 //支持可扩展性......
}ProtocolType;


typedef enum
{
 Train_No,//无线车次信息
 Attemper_Command,//调度命令信息
 Revert_Command,//调度命令回执信息
 Replay_Command,//重发的调度命令信息
     KGL_SING    //开关量数据
 //支持可扩展性......
}PacketDataType;


//串口数据结构
typedef struct SerialNetProto
{
 unsigned long  PacketSize; //包总长度,不包括本身字段
 ProtocolType  NetState; //协议包类型
 PacketDataType DataType; //数据类型
 unsigned long SourcedAddr; //数据包源地址
 unsigned long DestinationAddr; //数据包目的地址
 unsigned long DataLength;    //包的数据段长度
 unsigned long Offset;    // 数据在整个包中的偏移地址
}PacketHead;


 int DataLen = 100;
 char *pBuf = new char[DataLen];
 strcpy(pBuf,"Hello World!");
 DataLen   = strlen(pBuf);
 PacketHead Myhead;
 Myhead.DestinationAddr = 11;
 Myhead.SourcedAddr = 10;
 Myhead.DataType = Attemper_Command;
 Myhead.DataLength = DataLen;
 Myhead.NetState = PACKET;
 Myhead.PacketSize = sizeof(PacketHead) - sizeof(unsigned long);
 Myhead.Offset = sizeof(Myhead.DestinationAddr) +sizeof(Myhead.SourcedAddr) + sizeof(Myhead.DataType) +sizeof(Myhead.DataLength) + sizeof(Myhead.NetState) + sizeof(Myhead.PacketSize);
 
 char *pSendBuffer = new char[sizeof(Myhead)+DataLen+ 4];//发送的数据
 memcpy(pSendBuffer,"##",2);//包头标志
 memcpy(pSendBuffer+2,(char*)&Myhead,sizeof(Myhead));//包头
 memcpy(pSendBuffer+2+sizeof(Myhead),pBuf,DataLen);//数据
 memcpy(pSendBuffer+2+sizeof(Myhead)+DataLen,"@@",2);//包尾标志


【第七步】发送数据
 DWORD dwError;
 //DWORD dwWantSend = 100;
 DWORD dwRealSend = 0;
 char* pReadBuf = NULL;
 if (ClearCommError(m_hCom,&dwError,NULL))
 {
  PurgeComm(m_hCom,PURGE_TXABORT | PURGE_TXCLEAR);
  cout<<"PurgeComm OK!"< }
 if (!WriteFile(m_hCom,pSendBuffer,sizeof(Myhead)+DataLen+ 4,&dwRealSend,&wrOverlapped))
 {
  if (GetLastError() == ERROR_IO_PENDING)
  {
   while (!GetOverlappedResult(m_hCom,&wrOverlapped,&dwRealSend,FALSE))
   {
    if (GetLastError() == ERROR_IO_INCOMPLETE)
    {
     //cout<<"写未完成,继续!"<     continue;
    }
    else
    {
     cout<<"发生错误,尝试恢复!"<     ClearCommError(m_hCom,&dwError,NULL);
     break;
    }
   }
  }
 }


【第八步】数据接收
 DWORD dwError;
 DWORD dwWantRead = 100;
 DWORD dwRealRead = 0;
 char* pReadBuf = new char[100];
 if (ClearCommError(m_hCom,&dwError,NULL))
 {
  PurgeComm(m_hCom,PURGE_TXABORT | PURGE_TXCLEAR);
  cout<<"PurgeComm OK!"< }


 if(!ReadFile(m_hComm,pReadBuf,dwWantRead,&RealRead,&wrOverlapped))
 {
  if(dwError = GetLastError()==ERROR_IO_PENDING)
  {
   While(GetOverlappedResult(m_hComm,&wrOverlapped,&dwRealRead,FALSE))
   {
    //对接收到的数据进行数据解析,处理
    //【第九步】............
    cout<<"dwRealRead = "<   }
  }
 }
【第九步】数据解析(数据解包处理)


#define MAX_SERIAL_BUFFER 4096
BOOL CanGetFullFrame(char* pReadBuf,int& dwRealRead)
{
 static char Buf[MAX_SERIAL_BUFFER*2];//自定义一个数据缓冲区
 static unsigned long nFrameStart = 0;//数据祯的开始位置
 static unsigned long nFrameEnd = 0;//数据祯的结束位置
 static unsigned long nCurrectPos = 0;//指针当前位置
 char *pdest = NULL;
 
 if (pReadBuf && (dwRealRead!= 0))
 {
  memcpy(&Buf[nCurrectPos],pReadBuf,dwRealRead);
  nCurrectPos = nCurrectPos + dwRealRead;//更新当前位置
 }
 //查找数据祯的开始标志
 pdest = (char*)Find(Buf,"##",MAX_SERIAL_BUFFER*2,2);
 if (pdest)
 {
  nFrameStart = unsigned long(pdest - Buf);//找到数据祯的开始位置
 }
 else//没有找到开始祯标志"##"
 {
  Buf[0] = Buf[nCurrectPos];//丢弃数据
  nFrameStart = 0;
  return FALSE;
 }
 
 //查找数据祯的结尾标志
 pdest =  (char*)Find(Buf,"@@",MAX_SERIAL_BUFFER*2,2);
 if (pdest)
 {
  nFrameEnd = unsigned long (pdest - Buf+2);
  dwRealRead= nFrameEnd - nFrameStart;
  memcpy(pReadBuf,&Buf[nFrameStart],dwRealRead);


  nFrameStart = nFrameEnd;//指向下一帧的开始位置
  nCurrectPos = nCurrectPos - dwRealRead;//修正nCurrentPos值
  memcpy(Buf,&Buf[nFrameEnd],nCurrectPos);//向前移动数据
  return TRUE;
 }
 else
 {
  return FALSE;
 }
}


//一个在内存块中查找指定字符串的函数
void* Find(const char *pSour,const char *pDest,int SourLen,int DestLen)
{
 int i = 0, j = 0;
 while(i < SourLen && j < DestLen)
 {
  if(*(pSour + i) == *(pDest + j))
  {
   i++;
   j++;
  }
  else
  {
   i =i - j + 1;
   j = 0;
  }
 }
 if(j == DestLen)
 {
  return (void*)(pSour + (i - DestLen));
 }
 else
 {
  return NULL;
 }


}

 

文章评论2条评论)

登录后参与讨论

用户44669 2012-3-20 10:25

很详细的一篇文章,很有启发,有些问题,领会的更深刻了。

用户1409644 2011-4-1 09:18

好文章,谢谢了!

用户604151 2010-9-14 09:29

是15.7的,16.3的操作应该也差不太多吧…… 我倒是感觉这个功能不是太常用,至少我很少用。

用户324983 2010-9-13 22:03

博主你说的这个版本是15.7的吗? 要是是16.3的就好咯 呵呵

用户324983 2010-9-13 22:02

博主,你说的这些都是15.7的吗? 要是16.3的就更好了 呵呵
相关推荐阅读
用户815303 2013-04-09 23:47
C和C++混合编程
  C和C++混合编程 extern "C"表示编译生成的内部符号名使用C约定。C++支持函数重载,而C不支持,两者的编译规则也不一样。函数被C++编译后在符号库中的名字与C语言的不同。例...
用户815303 2013-03-02 22:07
IDT高精度计量芯片IDT90E32AS
  1、 完全符合IEC62052-11,IEC62053-22和IEC62053-23,ANSI c12 1和ANSI c12 20 2、5000:1动态范围内,有功能量精度为±0.1%...
用户815303 2013-03-02 21:58
IDT90E32AS
1、 完全符合IEC62052-11,IEC62053-22和IEC62053-23,ANSI c12 1和ANSI c12 20 2、5000:1动态范围内,有功能量精度为±0.1%,无功能...
用户815303 2012-12-15 21:37
DLMS IEC标准
  第61部分:对象标识系统 第62部分:接口类 第53部分:COSEM应用层 第46部分:使用HDLC(High Level Data Link Control)协议的数据链路层...
用户815303 2012-11-11 22:55
IDT 高精度宽量程计量芯片
计量功能 1、完全符合要求IEC62052 - 11,IEC62053 - 22和IEC62053 - 23,ANSI C12.1和 ANSI C12.20;适用于0.5S级或1级多相电度表要...
用户815303 2012-11-11 22:55
IDT 90E36 参数计算
为供大家调试方便 做一个参数计算工具。 ...
我要评论
2
6
关闭 站长推荐上一条 /2 下一条