?? serialcomm.asp
字號:
<pre>threadFn...
WaitCommEvent(m_hCommPort,&dwEventMask, &ov) ;
if ( WaitForSingleObject(ov.hEvent,INFINITE) == WAIT_OBJECT_0)
{
char szBuf[100];
memset(szBuf,0,sizeof(szBuf));
do
{
ReadFile( hPort,szBuf,sizeof(szBuf),&dwBytesRead,&ov);
}while (dwBytesRead > 0 );
} </pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana">ReadFile API has following signature:<br>
</font></p>
<table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
<tr>
<td width="100%">
<pre><font face="Verdana" size="2">BOOL ReadFile( HANDLE<i> <a class="synParam" onclick="showTip(this)" href>hFile</a></i>, // handle to file
</font> <font face="Verdana" size="2">LPVOID<i> <a class="synParam" onclick="showTip(this)" href>lpBuffer</a></i>, // data buffer
DWORD<i> <a class="synParam" onclick="showTip(this)" href>nNumberOfBytesToRead</a></i>, // number of bytes to read
LPDWORD<i> <a class="synParam" onclick="showTip(this)" href>lpNumberOfBytesRead</a></i>, // number of bytes read
LPOVERLAPPED<i> <a class="synParam" onclick="showTip(this)" href>lpOverlapped</a></i> // overlapped buffer );</font></pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana"><br>
<br>
The first parameter is as usual the com port, the last parameter is the
overlapped structure. Again we need to create a manual reset event and pass the
overlapped structure to the ReadFile function. Again if you issue a read for say
10 bytes and there is no data available , ReadFile will return FALSE and
GetLastError() will return ERROR_IO_PENDING and the system will set the
overlapped event when the overlapped operation(read ) completes. <br>
As you can see ReadFile returns dwBytesRead which as is clear returns the number
of bytes read. If there are no bytes remaining, the dwBytesRead will return 0.
Lets say there are 11 bytes that have arrived and you read 10 characters
in the first go in while loop. In the first go 10 characters will be returned in
dwBytesRead. In the second go with while loop, the dwBytesRead will return 1.
Now in the third go the dwBytesRead will return 0 and you will break out of the
while loop. This allows you to read all the data. In this approach ,if you
noticed,we never really took advantage of the overlapped structure that we
passed to the ReadFile function but we still need to pass it because we opened
the COM port in Overlapped manner.<br>
<br>
<br>
And finally when you want to send data to other device, you need to call
WriteFile. WriteFile is not even worth discussing.<br>
<br>
<br>
There is one more thing that needs to be taken into account before we move on
and that is communication <b>timeouts</b>. Its important to set the timeout to
proper values for things to work. The API to do so is:
</font></p>
<p><font size="2" face="Verdana">SetCommTimeouts ( HANDLE hCommPort,
LPCOMMTIMEOUTS lpCommTimeOuts)<br>
<br>
<br>
COMTIMEOUTS is a structure with following members:<br>
</font></p>
<table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
<tr>
<td width="100%">
<pre class="syntax"><font face="Verdana" size="2">typedef struct _COMMTIMEOUTS {
DWORD <a class="synParam" onclick="showTip(this)" href>ReadIntervalTimeout</a>;
DWORD <a class="synParam" onclick="showTip(this)" href>ReadTotalTimeoutMultiplier</a>;
DWORD <a class="synParam" onclick="showTip(this)" href>ReadTotalTimeoutConstant</a>;
DWORD <a class="synParam" onclick="showTip(this)" href>WriteTotalTimeoutMultiplier</a>;
DWORD <a class="synParam" onclick="showTip(this)" href>WriteTotalTimeoutConstant</a>;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS; </font></pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana">
<br>
For a description of all these fields consult MSDN documentation. But one thing
I want to point out is this: <br>
"...A value of MAXDWORD, combined with zero values for both the <b>ReadTotalTimeoutConstant</b>
and <b>ReadTotalTimeoutMultiplier</b> 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..."<br>
This is exactly what we want . We do NOT want the ReadFile to get stuck if there
is no data available as we will know with WaitCommEvent() API.<br>
and also "...A value of zero for both the <b>WriteTotalTimeoutMultiplier</b>
and <b>WriteTotalTimeoutConstant</b> members indicates that total time-outs are
not used for write operations..." is what we want. In short we need to do
this:</font></p>
<table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
<tr>
<td width="100%">
<pre> COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(m_hCommPort, &timeouts))
{
ASSERT(0);
TRACE ( "CSerialCommHelper : Error setting time-outs. %d",GetLastError());
return E_FAIL;
}
</pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana"><br>
Now we have discussed almost everything that needs to be discussed for the sake
of this article.<br>
<b><br>
Putting it all together <br>
</b>All this I have put together in a form of two classes: <br>
The main class is <b>CSerialCommHelper</b> - the main class that does performs
all the communication .<br>
The helper class called <b>CSerialBuffer</b> that is an internal buffer used by
the CSerialCommHelper.<br>
<br>
Here is the main API of the <b>CSerialCommHelper:<br>
</b></font></p>
<table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
<tr>
<td width="100%">
<pre><font face="Verdana" size="2">inline bool IsInputAvailable()
inline bool IsConnection() {return m_abIsConnected ;}
inline void SetDataReadEvent() { SetEvent ( m_hDataRx ); }
HRESULT Read_N (std::string& data,long alCount,long alTimeOut);
HRESULT Read_Upto (std::string& data,char chTerminator ,long* alCount,long alTimeOut);
HRESULT ReadAvailable(std::string& data);
HRESULT Write (const char* data,DWORD dwSize);
HRESULT Init(std::string szPortName, DWORD dwBaudRate,BYTE byParity,BYTE byStopBits,BYTE byByteSize);
HRESULT Start();
HRESULT Stop();
HRESULT UnInit();</font></pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana">and the interface for CSerialBuffer is :<br>
</font></p>
<table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
<tr>
<td width="100%">
<pre><font size="2" face="Verdana">inline void LockBuffer();
inline void UnLockBuffer();
void AddData( char ch ) ;
void AddData( std::string& szData ) ;
void AddData( std::string& szData,int iLen ) ;
void AddData( char *strData,int iLen ) ;
std::string GetData() ;
void Flush();
long Read_N( std::string &szData,long alCount,HANDLE& hEventToReset);
bool Read_Upto( std::string &szData,char chTerm,long &alBytesRead ,HANDLE& hEventToReset);
bool Read_Available( std::string &szData,HANDLE & hEventToReset);
inline long GetSize() ;
inline bool IsEmpty() ;</font></pre>
</td>
</tr>
</table>
<p><font size="2" face="Verdana"> </font></p>
<p><font size="2" face="Verdana">Here is the logic and working behind the
classes:</font></p>
<p><font size="2" face="Verdana">First of let me show you how to use the class.
In your application create an object of CSerialCommHelper like this:</font></p>
<p><font size="2" face="Verdana">CSerialCommHelper m_theCommPort;<br>
<br>
Call m_theCommPort.Init() passing in the necessary information. If you want you
can use default values.<br>
Next call m_theCommPort.Start() </font></p>
<p><font size="2" face="Verdana">If you want to get notification about when the
some data is available you can get the kernel event object to wait on by
calling m_theCommPort.GetWaitForEvent().</font></p>
<p><font size="2" face="Verdana">What CSerialCommHelper does is that on call to
Init(), it opens the specified COM port and also starts a thread. The thread
starts "listening" for any incoming data and once the data has been
received it reads all the data into a local buffer which is of type
CSerialBuffer . Once its done reading all the data it sets the event in case you
want to get the notification. Now you have three options </font></p>
<ul>
<li><font size="2" face="Verdana">read all the data by calling ReadAvailable()
which reads all the data . </font></li>
<li><font size="2" face="Verdana">read up to some character by calling
Read_Upto and passing character upto which you want to read.</font></li>
<li><font size="2" face="Verdana">read N character calling Read_N passing the
numbers to be read. </font></li>
</ul>
<p><font size="2" face="Verdana">There is one more thing that needs to be paid
attention. If you want to read 10 characters but there are only 5
characters in the local buffer, the read_N makes a blocking call and waits for
the timeout passed as the last parameter . Same is true for Read_Upto.</font></p>
<p><font size="2" face="Verdana">One more thing. If there are 10 characters in
the local buffer but you made a call to Read_N() for 5 characters you will be
returned first 5 characters. If you made a next call Read_N() for 5 characters
again, it would returned next 5 characters.<br>
<br>
Thats all there is to it. </font></p>
<p><font size="2" face="Verdana">If you think I have left something please feel
free to email me at <a href="mailto:ashishdhar@hotmail.com">ashishdhar@hotmail.com</a>
<br>
<br>
</font></p>
</td>
</tr>
</tbody>
</table>
<hr width="70%" color="#000000">
<p align="center"><font face="Verdana" size="1">copyright
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -