SerialComm.asp
上传用户:chineseart
上传日期:2022-06-24
资源大小:52k
文件大小:31k
源码类别:

串口编程

开发平台:

Visual C++

  1. <%@ Language=VBScript %>
  2. <!-- #include file="../../ArticleAccess.asp" -->
  3. <!-- #include file="../../GetAccessCount.asp" -->
  4. <%
  5.  PrintArticleAccessCount 45
  6.  AccessArticle  45
  7.  %>
  8.  <html>
  9. <head>
  10. <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
  11. <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
  12. <meta name="ProgId" content="FrontPage.Editor.Document">
  13. <title> Serial Communication in Windows </title>
  14. </head>
  15. <body>
  16. <p align="center">&nbsp;</p>
  17. <p align="center"><font face="Verdana" size="2"><b>[<u> </u></b></font><font size="2" face="Verdana"><b><u>Serial Communication in Windows</u></b></font><font face="Verdana" size="2"><b><u>
  18. </u>]<br>
  19. </b><a href="mailto:ashishdhar@hotmail.com"><i>ashish dhar</i></a></font></p>
  20. <table borderColor="#ffffff" height="4318" cellSpacing="0" borderColorDark="#ffffff" width="99%" borderColorLight="#ffffff" border="1">
  21.   <tbody>
  22.     <tr>
  23.       <td vAlign="top" width="11%" bgColor="#538ab3" height="4314">
  24.         <div align="justify">
  25.           <table borderColor="#538ab3" height="4654" cellSpacing="4" borderColorDark="#000000" width="100%" borderColorLight="#538ab3" border="4">
  26.             <tbody>
  27.               <tr>
  28.                 <td vAlign="top" borderColorLight="#538ab3" width="100%" bgColor="#538ab3" borderColorDark="#538ab3" height="4650"><br>
  29.                   <br>
  30.                   <br>
  31.                   <br>
  32.                   <br>
  33.                   <br>
  34.                   <br>
  35.                   <br>
  36.                   <br>
  37.                   <br>
  38.                   <br>
  39.                   <br>
  40.                   <br>
  41.                   <br>
  42.                   <br>
  43.                   <br>
  44.                 </td>
  45.               </tr>
  46.             </tbody>
  47.           </table>
  48.         </div>
  49.       </td>
  50.       <td vAlign="top" borderColorLight="#e8e9e2" width="69%" bgColor="#ffffff" borderColorDark="#f0f0f0" height="4314">&nbsp;
  51. <p><font size="2" face="Verdana"><a href="../Source.zip">Source code</a><b><br>
  52. <br>
  53. </b>This article is meant to give you a jump start on doing serial communication
  54. in Windows (NT family). The article will provide a class called
  55. CSerialCommHelper which you can use directly to do serial communication in your
  56. application. The class that is provided here with this article does uses
  57. overlapped IO. You donot need to know much about serial communication or
  58. overlapped IO for this article. However, you need to know some about the
  59. synchronization objects like Events and some Windows APIs like
  60. WaitForSingleObject and WaitForMultipleObject etc. Also some basic understanding
  61. of windows threads is required - like thread creation and <i>termination</i>.</font></p>
  62. <p><font size="2" face="Verdana"><b>Introduction<br>
  63. </b>In order for your computer to be able to do serial communication, computer
  64. has to have a serial port. Most of the computers have at least one serial port
  65. also known as COM port ( communication port ) and are generally called COM1 COM2
  66. etc. Then there are the device drivers for the serial ports. If you think it
  67. over, all you that you need to do in serial communication is either send data or
  68. receive data. In other words, you are doing input/output (IO) to the serial
  69. port. The same IO is done with disk based files. Hence there is no surprise that
  70. the APIs for reading and writing to a file apply to serial ports as well. When
  71. you send data to the serial port its in terms of bytes but when it leaves the
  72. serial port it is in the form of bits. Similarly, when the data arrives at the
  73. serial port, its in bit format and when you get data you get it in bytes.&nbsp;<br>
  74. Without any further discussion lets get started.<br>
  75. <br>
  76. <b>Opening the COM port<br>
  77. </b>The first and the foremost step in doing a serial communication is to open
  78. the desired port. Lets say you have your device hooked to COM1 you can open the
  79. COM port using following API:</font></p>
  80. <table border="0" cellspacing="1" width="100%" bgcolor="#EEE8EE">
  81.   <tr>
  82.     <td width="100%">
  83.       <pre><font size="2">HANDLE m_hCommPort = ::<font color="#0000FF">CreateFile</font>( szPortName,
  84. GENERIC_READ|GENERIC_WRITE,//access ( read and write)
  85. 0, //(share) 0:cannot share the COM port
  86. 0, //security  (None)
  87. OPEN_EXISTING,// creation : open_existing
  88. FILE_FLAG_OVERLAPPED,// we want overlapped operation
  89. 0// no templates file for COM port...
  90. );</font></pre>
  91.     </td>
  92.   </tr>
  93. </table>
  94. <p><font size="2" face="Verdana">The third fifth and seventh parameters have to be what they
  95. are in the above example by law. We want to open the file ( the COM port ) in an
  96. overlapped fashion - that's why the sixth param is FILE_FLAG_OVERLAPPED. We will
  97. get into the details of overlapped IO a bit later. As you must have guessed from
  98. the name , CreateFile() API can be used to create a file (disk based) and also
  99. it can be used to open an existing file.&nbsp;<br>
  100. To Windows a serial port or a disk based file both are IO devices . So, in order
  101. to open an existing file ( serial port ) all we need to know the name of the
  102. device ( COM1) and pass the creation flags as OPEN_EXISTING.<br>
  103. If a COM port is opened successfully, the API returns handle to the com port
  104. just like a handle to a file. However, if the system could not open the COM
  105. port, it would return INVALID_HANDLE_VALUE . And you can get the reason by
  106. calling GetLastError(). One of the common errors while opening a COM port is
  107. that the COM port is already opened by some other application and in that case
  108. you would get ERROR_ACCESS_DENIED (5). Similarly if you by mistake opened a COM
  109. port that doesnot exist , you would get ERROR_FILE_NOT_FOUND&nbsp; as the last
  110. error.<br>
  111. <u>Note: Remember not to do make any function calls (like ASSERT) before calling
  112. GetLastError() or you would get 0</u>.<u><br>
  113. </u>Once you have opened the com port all you need to do now is to start using
  114. it.<br>
  115. <br>
  116. <b>Reading and Writing<br>
  117. </b>Now, once you have a com port open, you may want to send&nbsp; some data to
  118. the connected device. For example, lets say you want to send &quot;Hello&quot;
  119. to the device(e.g., another PC). When you want to send the data across the
  120. serial port, you need to write to the serial port just like you would write to a
  121. file. You would use following API:<br>
  122. </font></p>
  123. <table border="0" cellspacing="1" width="100%">
  124.   <tr>
  125.     <td width="100%" bgcolor="#C0C0C0"><font size="2" face="Verdana">iRet = WriteFile (m_hCommPort,data,dwSize,&amp;dwBytesWritten  ,&amp;ov);&nbsp;&nbsp;&nbsp;&nbsp;</font></td>
  126.   </tr>
  127. </table>
  128. <p><font size="2" face="Verdana">where data contains &quot;Hello&quot; .&nbsp;<br>
  129. Lets say in response to your &quot;Hello&quot; , the device sends you
  130. &quot;Hi&quot; . So you need to read the data. Again ,you would&nbsp; use
  131. following API:</font></p>
  132. <table border="0" cellspacing="1" width="100%">
  133.   <tr>
  134.     <td width="100%" bgcolor="#C0C0C0"><font size="2" face="Verdana">abRet = ::ReadFile(m_hCommPort,szTmp
  135.       ,sizeof(szTmp ),&amp;dwBytesRead,&amp;ovRead) ;</font></td>
  136.   </tr>
  137. </table>
  138. <p><font size="2" face="Verdana"><i>For now do not try to understand everything.We will get to
  139. all this later.<br>
  140. </i>All this sounds very simple. Right? <br>
  141. Now lets start digging into issues. </font></p>
  142. <p><font size="2" face="Verdana"><b>Issues with serial communication</b><br>
  143. Just now I said, in response to your &quot;Hello&quot;, the device may send you
  144. &quot;Hi&quot; back and you would like to read that. But the problem here is
  145. that you don't know when the device is going to respond? Or will it ever
  146. respond? When should you start to read from the port. One option is that as soon
  147. as you made call to WriteFile, you make call to ReadFile . If no data is there
  148. you need to make read again later on. This leads to what is called polling. You
  149. keep polling the port for data. This model does not really&nbsp; seem to be a
  150. good one. It would be nice if somehow you were notified by the system when data
  151. has arrived and only then would you make call to ReadFile. This is event driven
  152. approach and fits well into Windows programming. And good news is that such a
  153. model is possible . <br>
  154. <br>
  155. <br>
  156. <br>
  157. Another issue with the serial communication is that since it always occurs
  158. between two devices, the two devices need to agree on how they talk to each
  159. other. Each side needs to follow certain protocols to conduct business. Since
  160. its the serial port that actually carries out the communication, we need to
  161. configure the serial port. There is an API available for exact same purpose.
  162. Following is the API:</font></p>
  163. <table border="0" cellspacing="1" width="100%" bgcolor="#C0C0C0">
  164.   <tr>
  165.     <td width="100%"><font size="2" face="Verdana">SetCommState ( HANDLE hFile, LPDCB lpDCB)</font></td>
  166.   </tr>
  167. </table>
  168. <p><font size="2" face="Verdana">The first parameter is the handle to COM port and the second
  169. paramter is what is called device control block (DCB) . The DCB is a struct
  170. defined in winbase.h and has 28 data members. For example, we need to specify
  171. baud rate at which the COM port operates, you need to set the <b>BaudRate </b>member
  172. of the struct . Baud rate is usual 9600 (bps) . But the two devices have to use
  173. the same baud rate to conduct business. Similarly if you want to use parity you
  174. need to set <b>Parity</b> member of the struct. Again the two devices have to
  175. use same parity. Some of the data members are reserved and have to be 0. I have
  176. found it easier to get the current DCB struct and then set those members which
  177. we are interested in changing. Following code gets the current dcb and sets some
  178. of the fields:<br>
  179. <br>
  180. </font></p>
  181. <table border="0" cellspacing="1" width="100%" bgcolor="#EEE8EE">
  182.   <tr>
  183.     <td width="100%">
  184.       <pre><font size="2" face="Verdana">DCB dcb = {0};
  185. dcb.DCBlength = sizeof(DCB);
  186. if (!::<font color="#0000FF">GetCommState </font>(m_hCommPort,&amp;dcb))
  187. {
  188. TRACE ( &quot;CSerialCommHelper : Failed to Get Comm State Reason: %d&quot;,GetLastError());
  189. return E_FAIL;
  190. }
  191. dcb.BaudRate = dwBaudRate;
  192. dcb.ByteSize = byByteSize;
  193. dcb.Parity = byParity;
  194. if ( byStopBits == 1 )
  195. dcb.StopBits = ONESTOPBIT;
  196. else if (byStopBits == 2 ) 
  197. dcb.StopBits = TWOSTOPBITS;
  198. else 
  199. dcb.StopBits = ONE5STOPBITS;
  200. if (!::<font color="#0000FF">SetCommState</font> (m_hCommPort,&amp;dcb))
  201. {
  202. ASSERT(0);
  203. TRACE ( &quot;CSerialCommHelper : Failed to Set Comm State Reason: %d&quot;,GetLastError());
  204. return E_FAIL;
  205. }
  206. TRACE ( &quot;CSerialCommHelper : Current Settings, (Baud Rate %d; Parity %d; Byte Size %d; Stop Bits %d&quot;, dcb.BaudRate, 
  207. </font></pre>
  208.     </td>
  209.   </tr>
  210. </table>
  211. <p><font size="2" face="Verdana">Most of the time you won't need to change the
  212. other fields of this structure. But if you need to change the structure you need
  213. to be very careful about the fields as changing the fields will affect the
  214. behavior of the serial communication and hence you should be very sure what you
  215. want to change.&nbsp;<br>
  216. <b><br>
  217. Event Driven Approach<br>
  218. </b>Coming back to our earlier problem with the reading of data. If we do not
  219. want to&nbsp; keep polling the COM port for any data then we need to have some
  220. kind of event mechanism available. Fortunately there is a way that you can ask
  221. the system to notify you when certain events happen. The API to use is
  222. </font></p>
  223. <table border="0" cellspacing="1" width="100%" bgcolor="#C0C0C0">
  224.   <tr>
  225.     <td width="100%"><font size="2" face="Verdana">SetCommMask( HANDLE
  226.       hHandle,DWORD dwEvtMask)</font></td>
  227.   </tr>
  228. </table>
  229. <p><font size="2" face="Verdana">The first parameter is the handle to the open
  230. COM port. The second parameter is used to specify a list of events which we are
  231. interested in. <br>
  232. The events that need to be specified in the mask depends upon the application
  233. needs. For simplicity, lets say we are interested in getting&nbsp; notified
  234. whenever a character arrives at the serial port, we would need to specify
  235. EV_RXCHAR as the event mask. Similarly if we are interested to know when all the
  236. data has been sent, we need to specify EV_TXEMPTY flag also. So out call would
  237. look like this:
  238. </font></p>
  239. <table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
  240.   <tr>
  241.     <td width="100%"><font size="2" face="Verdana">SetCommMask(
  242.       m_hCommPort,EV_TXTEMPTY|EV_RXCHAR);</font></td>
  243.   </tr>
  244. </table>
  245. <p><font size="2" face="Verdana">&nbsp; The interesting thing here is that
  246. although we told system about the events of our interest, we did not however
  247. told system what to do when these events occur. Like how would system let us
  248. know that a particular event occurred. An obvious thing seems to be a callback
  249. mechanism. But there is not such mechanism available. Here is when things get a
  250. little tricky. In order for system to let us know about the communication event
  251. occurrence, we need to call <b>WaitCommEvent</b> This function waits for the
  252. events specified in SetCommMask. But if your think a little more, it sounds like
  253. we are turning a notification mechanism back to polling mechanism. Actually its
  254. even worse that than . WaitCommEvent blocks till an event occurs. So whats the
  255. use of WaitCommEvent ? Well , the answer lies in overlapped IO.<br>
  256. If you look at the WaitCommEvent signature it looks like this:
  257. </font></p>
  258. <table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
  259.   <tr>
  260.     <td width="100%"><font face="Verdana" size="2">BOOL</font><font face="Verdana" size="2" color="#0000FF">
  261.       WaitCommEvent</font><font face="Verdana" size="2">(HANDLE hCommPort,
  262.       LPDWORD dwEvtMask,LPOVERLAPPED lpOverlapped);</font></td>
  263.   </tr>
  264. </table>
  265. <p><font size="2" face="Verdana">&nbsp;&nbsp;&nbsp; The third parameter is the
  266. key here.&nbsp;<br>
  267. Think of overlapped IO as asynchronous IO. Whenever a function makes a call and
  268. specifies the overlapped IO structure, it means that try to do the current
  269. operation but if you are not able to complete it immediately let me know when
  270. you are done with this IO. The way system lets you know about the completion is
  271. by setting an kernel event object that is part of the lpOverlapped structure.
  272. So, all you do is spawn a thread and make the thread wait for that event object
  273. using one of the WaitForSingleObject() APIs.<br>
  274. Lets look at the overlapped structure:<br>
  275. </font></p>
  276. <table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
  277.   <tr>
  278.     <td width="100%">
  279.       <pre><font size="2" face="Verdana"><font color="#0000FF">typedef struct </font>_OVERLAPPED {
  280. DWORD Internal;
  281. DWORD InternalHigh;
  282. DWORD Offset;
  283. DWORD OffsetHigh;
  284. <b>HANDLE hEvent</b>;
  285. } OVERLAPPED, *LPOVERLAPPED;</font></pre>
  286.     </td>
  287.   </tr>
  288. </table>
  289. <p><font size="2" face="Verdana">The last parameter is the event handle that you
  290. need to create . This event is generally a manual reset event. When you make a
  291. call like WaitCommEvent () passing overlapped structure as the last parameter,
  292. and the system could not complete call meaning it did not see any characters at
  293. the port, it would return immediately but would return FALSE. If you now make a
  294. call to GetLastError() you would get ERROR_IO_PENDING which means that the call
  295. has been accepted but no characters have yet arrived at the COM port. Also it
  296. means whenever the characters will arrive, the system will set the hEvent of the
  297. overlapped structure that you passed in. So if your thread would wait for single
  298. object on hEvent and you pass INFINITE, then whenever your Wait fn. returns
  299. WAIT_OBJECT_0 it means some character has arrived&nbsp; or all the data in the
  300. output buffer has been sent.<br>
  301. In our current case since we are interested in more than one events we would
  302. need to check what event did we get by making call to GetCommMask and checking
  303. the dword against each event.Following&nbsp; pseudo code will explain it:</font></p>
  304. <p><font size="2" face="Verdana">&nbsp;You can read the data from the com port
  305. and reset the event and make the call to WaitCommEvent again and so on.
  306. </font></p>
  307. <table border="0" cellspacing="1" width="100%" bgcolor="#EEE8EE">
  308.   <tr>
  309.     <td width="100%">
  310.       <pre><font face="Verdana" size="2" color="#417194">unsigned __stdcall CSerialCommHelper::ThreadFn(void*pvParam)
  311. {
  312. OVERLAPPED ov;
  313. memset(&amp;ov,0,sizeof(ov));
  314. ov.hEvent = CreateEvent( 0,true,0,0);
  315. HANDLE arHandles[2];
  316. arHandles[0] = apThis-&gt;m_hThreadTerm;
  317. DWORD dwWait;
  318. SetEvent(apThis-&gt;m_hThreadStarted);
  319. while (  abContinue )
  320. {
  321. </font><font face="Verdana" size="2"> BOOL abRet = ::WaitCommEvent(apThis-&gt;m_hCommPort,&amp;dwEventMask, &amp;ov) ;
  322. </font><font face="Verdana" size="2" color="#417194"> if ( !abRet )
  323. {
  324. ASSERT( GetLastError () == ERROR_IO_PENDING);
  325. }
  326. arHandles[1] = ov.hEvent ;
  327. dwWait = WaitForMultipleObjects (2,arHandles,FALSE,INFINITE);
  328. switch ( dwWait )
  329. {
  330. case WAIT_OBJECT_0:
  331. {
  332. _endthreadex(1);
  333. }
  334. break;
  335. case WAIT_OBJECT_0 + 1:
  336. {
  337. </font><font face="Verdana" size="2">DWORD dwMask;
  338. if (GetCommMask(apThis-&gt;m_hCommPort,&amp;dwMask) )
  339. {
  340.    if ( dwMask &amp; EV_TXEMPTY )
  341.    TRACE(&quot;Data sent&quot;);
  342.    ResetEvent ( ov.hEvent );
  343.    continue;
  344. }<font color="#417194">
  345. </font>else  
  346. {
  347.    //read data here and reset ov.hEvent
  348. }
  349. </font><font color="#417194"><font face="Verdana" size="2"> }
  350. }//switch
  351. }//while
  352. return 0;
  353. }</font></font></pre>
  354.     </td>
  355.   </tr>
  356. </table>
  357. <p><font size="2" face="Verdana">&nbsp;If you understood the above code , you
  358. will understand the whole of this article and the source code provided.<br>
  359. &nbsp;The above piece of code is simple using the overlapped IO method to do its
  360. job.<br>
  361. Once we have received the indication that the data has arrived we need to read
  362. the data. Important thing to note here is that the when data arrives at the
  363. serial port, it is copied over to system buffer.&nbsp; The data is removed from
  364. the system buffer only when you have read the data using API such as ReadFile.&nbsp;
  365. Like any buffer, system buffer has a limited size. So if you do not read the
  366. data from the buffers quick enough the system buffers can be become full if more
  367. data is arriving. What happens to further data depends upon the configuration
  368. that you have set in the device configuration block (in call to SetCommState ).
  369. Usually the applications do some kind of handshaking at the application level
  370. but you can also make configurations such that the com port does not accept any
  371. further data upon buffer-full events. But all that is beyond the scope of this
  372. discussion. If possible its always better to have applications themselves
  373. implementing some kind of handshaking&nbsp; - like do not send next block of
  374. data until you get okay for the first block. Generally this kind of handshaking
  375. is implemented using some sort of ACK / NAK&nbsp; and ENQ protocol.<br>
  376. <br>
  377. In order for us to read data we need to use ReadFile() API. ReadFile API has to
  378. specify how much data to read. Lets say we are monitoring character arrivals and
  379. 10 characters arrive at the port. As soon as first character arrives at the port
  380. the system will set the overlapped structure's event object and out
  381. WaitSingleObject will return. Next we would need to read the data. So how much
  382. data should we read? Should we read 1 byte or&nbsp; 10 bytes? That is a good
  383. question.&nbsp;&nbsp; The way it works is as follows (Note:this
  384. is not documented anywhere but this is what I have found by research on
  385. Win2K,NT4.0)&nbsp; :<br>
  386. When one (or more) characters arrive at the port, the event object associated with the
  387. overlapped structure set once. Now lets say that you made a call to read and you
  388. read 1 character. After reading 1 character , you would finally Reset the
  389. overlapped structure's event object. Now you would go back to the WaitCommEvent
  390. but it would return false since no &quot;new&quot; character has arrived. So you
  391. will not be able to read any more characters.&nbsp; Now when another character
  392. arrives, system will set the overlapped event and you would read one more
  393. character but this time it will be the character that had arrived earlier and
  394. you never read. This clearly is a&nbsp; problem.<br>
  395. So what is the solution? The easiest solution is that as soon as you got the
  396. event object indicating the arrival of a character, you should read all the
  397. characters that are present in the port. (<i>If you are familiar with win API
  398. MsgWaitForMultipleObjects you can draw a analogy here.</i>) </font></p>
  399. <p><font size="2" face="Verdana">So again the question remains how many
  400. characters to read. The answer is read all the characters in a loop using
  401. ReadFile().
  402. <br>
  403. Here is the pseudo code</font></p>
  404. <table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
  405.   <tr>
  406.     <td width="100%">
  407.       <pre>threadFn...
  408. WaitCommEvent(m_hCommPort,&amp;dwEventMask, &amp;ov) ;
  409. if ( WaitForSingleObject(ov.hEvent,INFINITE) == WAIT_OBJECT_0)
  410. {
  411. char szBuf[100];
  412. memset(szBuf,0,sizeof(szBuf));
  413. do
  414. {
  415. ReadFile( hPort,szBuf,sizeof(szBuf),&amp;dwBytesRead,&amp;ov);
  416. }while (dwBytesRead &gt; 0 );
  417. } </pre>
  418.     </td>
  419.   </tr>
  420. </table>
  421. <p><font size="2" face="Verdana">ReadFile API has following signature:<br>
  422. </font></p>
  423. <table border="0" cellspacing="1" width="100%" bgcolor="#DADADA">
  424.   <tr>
  425.     <td width="100%">
  426.       <pre><font face="Verdana" size="2">BOOL ReadFile( HANDLE<i> <a class="synParam" onclick="showTip(this)" href>hFile</a></i>, // handle to file 
  427. </font> <font face="Verdana" size="2">LPVOID<i> <a class="synParam" onclick="showTip(this)" href>lpBuffer</a></i>, // data buffer 
  428. DWORD<i> <a class="synParam" onclick="showTip(this)" href>nNumberOfBytesToRead</a></i>, // number of bytes to read 
  429. LPDWORD<i> <a class="synParam" onclick="showTip(this)" href>lpNumberOfBytesRead</a></i>, // number of bytes read 
  430. LPOVERLAPPED<i> <a class="synParam" onclick="showTip(this)" href>lpOverlapped</a></i> // overlapped buffer );</font></pre>
  431.     </td>
  432.   </tr>
  433. </table>
  434. <p><font size="2" face="Verdana"><br>
  435. <br>
  436. The first parameter is as usual the com port, the last parameter is the
  437. overlapped structure. Again we need to create a manual reset event and pass the
  438. overlapped structure to the ReadFile function. Again if you issue a read for say
  439. 10 bytes and there is no data available , ReadFile will return FALSE and
  440. GetLastError() will return ERROR_IO_PENDING and the system will set the
  441. overlapped event when the overlapped operation(read ) completes.&nbsp;<br>
  442. As you can see ReadFile returns dwBytesRead which as is clear returns the number
  443. of bytes read. If there are no bytes remaining, the dwBytesRead will return 0.
  444. Lets say there are 11 bytes that have arrived and you read 10&nbsp; characters
  445. in the first go in while loop. In the first go 10 characters will be returned in
  446. dwBytesRead. In the second go with while loop, the dwBytesRead will return 1.
  447. Now in the third go the dwBytesRead will return 0 and you will break out of the
  448. while loop. This allows you to read all the data. In this approach ,if you
  449. noticed,we never really took advantage of the overlapped structure that we
  450. passed to the ReadFile function but we still need to pass it because we opened
  451. the COM port in Overlapped manner.<br>
  452. <br>
  453. <br>
  454. And finally when you want to send data to other device, you need to call
  455. WriteFile. WriteFile is not even worth discussing.<br>
  456. <br>
  457. <br>
  458. There is one more thing that needs to be taken into account before we move on
  459. and that is communication <b>timeouts</b>. Its important to set the timeout to
  460. proper values for things to work. The API to do so is:&nbsp;
  461. </font></p>
  462. <p><font size="2" face="Verdana">SetCommTimeouts ( HANDLE hCommPort,
  463. LPCOMMTIMEOUTS lpCommTimeOuts)<br>
  464. <br>
  465. <br>
  466. &nbsp;COMTIMEOUTS is a structure with following members:<br>
  467. </font></p>
  468. <table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
  469.   <tr>
  470.     <td width="100%">
  471.       <pre class="syntax"><font face="Verdana" size="2">typedef struct _COMMTIMEOUTS {  
  472.   DWORD <a class="synParam" onclick="showTip(this)" href>ReadIntervalTimeout</a>; 
  473.   DWORD <a class="synParam" onclick="showTip(this)" href>ReadTotalTimeoutMultiplier</a>; 
  474.   DWORD <a class="synParam" onclick="showTip(this)" href>ReadTotalTimeoutConstant</a>; 
  475.   DWORD <a class="synParam" onclick="showTip(this)" href>WriteTotalTimeoutMultiplier</a>; 
  476.   DWORD <a class="synParam" onclick="showTip(this)" href>WriteTotalTimeoutConstant</a>; 
  477. } COMMTIMEOUTS,*LPCOMMTIMEOUTS; </font></pre>
  478.     </td>
  479.   </tr>
  480. </table>
  481. <p><font size="2" face="Verdana">&nbsp;&nbsp;
  482. <br>
  483. For a description of all these fields consult MSDN documentation. But one thing
  484. I want to point out is this:&nbsp;<br>
  485. &quot;...A value of MAXDWORD, combined with zero values for both the <b>ReadTotalTimeoutConstant</b>
  486. and <b>ReadTotalTimeoutMultiplier</b> members, specifies that the read operation
  487. is to return immediately with the characters that have already been received,
  488. even if no characters have been received...&quot;<br>
  489. This is exactly what we want . We do NOT want the ReadFile to get stuck if there
  490. is no data available as we will know with WaitCommEvent() API.<br>
  491. and also &quot;...A value of zero for both the <b>WriteTotalTimeoutMultiplier</b>
  492. and <b>WriteTotalTimeoutConstant</b> members indicates that total time-outs are
  493. not used for write operations...&quot; is what we want. In short we need to do
  494. this:</font></p>
  495. <table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
  496.   <tr>
  497.     <td width="100%">
  498.       <pre> COMMTIMEOUTS timeouts;
  499. timeouts.ReadIntervalTimeout = MAXDWORD; 
  500. timeouts.ReadTotalTimeoutMultiplier = 0;
  501. timeouts.ReadTotalTimeoutConstant = 0;
  502. timeouts.WriteTotalTimeoutMultiplier = 0;
  503. timeouts.WriteTotalTimeoutConstant = 0;
  504. if (!SetCommTimeouts(m_hCommPort, &amp;timeouts))
  505. {
  506. ASSERT(0);
  507. TRACE ( &quot;CSerialCommHelper :  Error setting time-outs. %d&quot;,GetLastError());
  508. return E_FAIL;
  509. }
  510. </pre>
  511.     </td>
  512.   </tr>
  513. </table>
  514. <p><font size="2" face="Verdana"><br>
  515. Now we have discussed almost everything that needs to be discussed for the sake
  516. of this article.<br>
  517. <b><br>
  518. Putting it all together&nbsp;<br>
  519. </b>All this I have put together in a form of two classes:&nbsp;<br>
  520. The main class is <b>CSerialCommHelper</b> - the main class that does performs
  521. all the communication .<br>
  522. The helper class called <b>CSerialBuffer</b> that is an internal buffer used by
  523. the CSerialCommHelper.<br>
  524. <br>
  525. Here is the main API of the <b>CSerialCommHelper:<br>
  526. </b></font></p>
  527. <table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
  528.   <tr>
  529.     <td width="100%">
  530.       <pre><font face="Verdana" size="2">inline bool IsInputAvailable()
  531. inline bool IsConnection() {return m_abIsConnected ;}
  532. inline void SetDataReadEvent() { SetEvent ( m_hDataRx ); }
  533. HRESULT Read_N (std::string&amp; data,long alCount,long alTimeOut);
  534. HRESULT Read_Upto (std::string&amp; data,char chTerminator ,long* alCount,long alTimeOut);
  535. HRESULT ReadAvailable(std::string&amp; data);
  536. HRESULT Write (const char* data,DWORD dwSize);
  537. HRESULT Init(std::string szPortName, DWORD dwBaudRate,BYTE byParity,BYTE byStopBits,BYTE byByteSize);
  538. HRESULT Start();
  539. HRESULT Stop();
  540. HRESULT UnInit();</font></pre>
  541.     </td>
  542.   </tr>
  543. </table>
  544. <p><font size="2" face="Verdana">and the interface for CSerialBuffer is :<br>
  545. </font></p>
  546. <table border="0" cellspacing="1" width="70%" bgcolor="#DADADA">
  547.   <tr>
  548.     <td width="100%">
  549.       <pre><font size="2" face="Verdana">inline void LockBuffer(); 
  550. inline void UnLockBuffer();
  551. void AddData( char ch ) ;
  552. void AddData( std::string&amp; szData ) ;
  553. void AddData( std::string&amp; szData,int iLen ) ;
  554. void AddData( char *strData,int iLen ) ;
  555. std::string GetData() ;
  556. void Flush();
  557. long Read_N( std::string &amp;szData,long alCount,HANDLE&amp; hEventToReset);
  558. bool Read_Upto( std::string &amp;szData,char chTerm,long &amp;alBytesRead ,HANDLE&amp; hEventToReset);
  559. bool Read_Available( std::string &amp;szData,HANDLE &amp; hEventToReset);
  560. inline long GetSize() ;
  561. inline bool IsEmpty() ;</font></pre>
  562.     </td>
  563.   </tr>
  564. </table>
  565. <p><font size="2" face="Verdana">&nbsp;</font></p>
  566. <p><font size="2" face="Verdana">Here is the logic and working behind the
  567. classes:</font></p>
  568. <p><font size="2" face="Verdana">First of let me show you how to use the class.
  569. In your application create an object of CSerialCommHelper like this:</font></p>
  570. <p><font size="2" face="Verdana">CSerialCommHelper m_theCommPort;<br>
  571. <br>
  572. Call m_theCommPort.Init() passing in the necessary information. If you want you
  573. can use default values.<br>
  574. Next call m_theCommPort.Start()&nbsp;</font></p>
  575. <p><font size="2" face="Verdana">If you want to get notification about when the
  576. some data is available you can get the kernel event object to wait on by
  577. calling&nbsp; m_theCommPort.GetWaitForEvent().</font></p>
  578. <p><font size="2" face="Verdana">What CSerialCommHelper does is that on call to
  579. Init(), it opens the specified COM port and also starts a thread. The thread
  580. starts &quot;listening&quot; for any incoming data and once the data has been
  581. received it reads&nbsp; all the data into a local buffer which is of type
  582. CSerialBuffer . Once its done reading all the data it sets the event in case you
  583. want to get the notification. Now you have three options </font></p>
  584. <ul>
  585.   <li><font size="2" face="Verdana">read all the data by calling ReadAvailable()
  586.     which reads all the data . </font></li>
  587.   <li><font size="2" face="Verdana">read up to some character by calling
  588.     Read_Upto and passing character upto which you want to read.</font></li>
  589.   <li><font size="2" face="Verdana">read N character calling Read_N passing the
  590.     numbers to be read. </font></li>
  591. </ul>
  592. <p><font size="2" face="Verdana">There is one more thing that needs to be paid
  593. attention. If you want to read 10 characters&nbsp; but there are only 5
  594. characters in the local buffer, the read_N makes a blocking call and waits for
  595. the timeout passed as the last parameter .&nbsp; Same is true for Read_Upto.</font></p>
  596. <p><font size="2" face="Verdana">One more thing. If there are 10 characters in
  597. the local buffer but you made a call to Read_N() for 5 characters you will be
  598. returned first 5 characters. If you made a next call Read_N() for 5 characters
  599. again, it would returned next 5 characters.<br>
  600. <br>
  601. Thats all there is to it. </font></p>
  602. <p><font size="2" face="Verdana">If you think I have left something please feel
  603. free to email me at <a href="mailto:ashishdhar@hotmail.com">ashishdhar@hotmail.com</a>
  604. <br>
  605. <br>
  606. &nbsp;</font></p>
  607.       </td>
  608.     </tr>
  609.   </tbody>
  610. </table>
  611. <hr width="70%" color="#000000">
  612. <p align="center"><font face="Verdana" size="1">copyright