⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial.html

📁 这是一个串口调试程序,带有源代码的,还是比较好用的.
💻 HTML
📖 第 1 页 / 共 3 页
字号:
 <li>
  <code>CSerial</code> is the base serial class, which provides a
  wrapper around the Win32 API. It is a lot easier to use, because it
  combines all relevant calls in one single class. It allows the
  programmer to mix overlapped and non-overlapped calls, provides
  reasonable default settings, better readability, etc, etc.
 </li>
 <li>
  <code>CSerialWnd</code> fits in the Windows event driven model.
  Whenever a communication event occurs a message is posted to the
  owner window, which can process the event.
 </li>
 <li>
  <code>CSerialMFC</code> is an MFC wrapper around
  <code>CSerialWnd</code>, which make the serial classes fit better in
  MFC based programs.
 </li>
</ul>

<p>
 If you're not using a message pump in the thread that performs the
 serial communication, then you should use the <code>CSerial</code>
 class. You can use blocking calls (the easiest solution) or one of
 the synchronization functions (i.e.
 <code>WaitForMultipleObjects</code>) to wait for communication events.
 This approach is also used in most Unix programs, which has a similar
 function as <code>WaitForMultipleObjects</code> called 'select'. This
 approach is often the best solution in non-GUI applications, such as
 NT services.
</p>

<p>
 GUI applications, which want to use the event-driven programming model
 for serial communications should use <code>CSerialWnd</code>. It is a
 little less efficient, but the performance degradation is minimal
 if you read the port efficiently. Because it fits perfectly in the
 event-driven paradigm the slight performance degradation is a minimal
 sacrifice. Note that you can use <code>CSerial</code> in GUI based
 applications (even MFC/WTL based), but then you might block the
 message pump. This is, of course, bad practice in in a commercial
 application (blocking the message pump hangs the application from the
 user's point of view for a certain time). As long as you know what the
 impact is of blocking the message pump, you can decide for yourself if
 it is acceptable in your case (could be fine for testing).
</p>

<p>
 MFC application should use the <code>CSerialMFC</code> wrapper if
 they want to pass CWnd pointers instead of handles. Because this
 wrapper is very thin you can also choose to use CSerialWnd directly.
</p>

<h2>
 Using the serial classes in your program
</h2>

<p>
 Using the serial classes can be divided into several parts. First
 you need to open the serial port, then you set the appropriate
 baudrate, databits, handshaking, etc... This is pretty
 straightforward. The tricky part is actually transmitting and
 receiving the data, which will probably cause the most time to
 implement. At last you need to close the serial port and as a
 bonus if you don't then the library will do it for you.
</p>

<h3>
 Sending data
</h3>

<p>
 Let's start with a classic example from K&amp;R and be polite and say
 hello. The implementation is very straightforward and looks like this
 (there is no error checking here for simplicity, it is there in the
 actual project):
</p>

<pre>
#define STRICT
#include &lt;tchar.h&gt;
#include &lt;windows.h&gt;
#include &quot;Serial.h&quot;

int WINAPI _tWinMain
          (
           HINSTANCE /*hInst*/, 
           HINSTANCE /*hInstPrev*/, 
           LPTSTR    /*lptszCmdLine*/, 
           int       /*nCmdShow*/
          )
{
    CSerial serial;

    // Attempt to open the serial port (COM1)
    serial.Open(_T(&quot;COM1&quot;));

    // Setup the serial port (9600,N81) using hardware handshaking
    serial.Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
    serial.SetupHandshaking(CSerial::EHandshakeHardware);

    // The serial port is now ready and we can send/receive data. If
    // the following call blocks, then the other side doesn't support
    // hardware handshaking.
    serial.Write(&quot;Hello world&quot;);

    // Close the port again
    serial.Close();
    return 0;
}
</pre>

<p>
 Of course you need to include the serial class' header-file. Make sure
 that the header-files of this library are in your compiler's include
 path. All classes depend on the Win32 API, so make sure that you have
 included them as well. I try to make all of my programs ANSI and
 Unicode compatible, so that's why the tchar stuff is in there. So
 far about the header-files.
</p>

<p>
 The interesting part is inside the main routine. At the top we declare
 the <code>serial</code> variable, which represents exactly one COM
 port. Before you can use it, you need to open the port. Of course
 there should be some error handling in the code, but that's left as an exercise
 for the reader. Besides specifying the COM port, you can
 also specify the input and output buffer sizes. The default is 2Kb for
 both buffers which will be sufficient for most applications. If you
 need larger buffers, then specify them yourself.
</p>

<p>
 Setting up the serial port is also pretty straightforward. The default
 settings (9600,8N1) are applied when the device has been opened. Call
 <code>Setup</code> if these settings do not apply for your
 application. If you prefer to use integers instead of the enumerated
 types then just cast the integer to the required type. So the
 following two initializations are equivalent:
</p>

<pre>
Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
  
Setup(CSerial::EBaudrate(9600),
      CSerial::EDataBits(8),
      CSerial::EParity(NOPARITY),
      CSerial::EStopBits(ONESTOPBIT));
</pre>

<p>
 In the latter case, the types are not validated. So make sure that you
 specify the appropriate values. Once you know which type of
 handshaking you need, then just call <code>SetupHandshaking</code>
 with one of the appropriate handshaking.
</p>

<p>
 Writing data is also very easy. Just call the <code>Write</code>
 method and supply a string. The <code>Write</code> routine will detect
 how long the string is and send these bytes across the cable. If you
 have written Unicode applications (like this one) then you might have
 noticed that I didn't send a Unicode string. I think that it's pretty
 useless to send Unicode strings, so you need to send them as binary or
 convert them back to ANSI yourself. Because we are using hardware
 handshaking and the operation is non-overlapped, the
 <code>Write</code> method won't return until all bytes have been sent
 to the receiver. If there is no other side, then you might block
 forever at this point.
</p>

<p>
 Finally, the port is closed and the program exits. This program is
 nice to display how easy it is to open and setup the serial
 communication, but it's not really useful. The more interesting
 programs will be discussed later.
</p>

<h3>
 Receiving data
</h3>

<p>
 Like in real life it's easier to tell something what to do then
 listening to another and take appropriate actions. The same holds for
 serial communication. As we saw in the Hello world example writing to
 the port is just as straightforward as writing to a file. Receiving
 data is a little more difficult. Reading the data is not that hard,
 but knowing that there is data and how much makes it more difficult.
 You'll have to wait until data arrives and when you're waiting you
 cannot do something else. That is exactly what causes problems in
 single-threaded applications. There are three common approaches to
 solve this.
</p>

<p>
 The first solution is easy. Just block until some data arrives on the
 serial port. Just call <code>WaitEvent</code> without specifying the
 overlapped structure. This function blocks until a communication event
 occurs (or an optional time-out expires). Easy, but the thread is
 blocked and only wakes up for communication events or a time-out.
</p>

<p>
 The second solution is to use the synchronization objects of Win32.
 Whenever something happens, the appropriate event handles are signaled and you can take appropriate action to handle the event.
 This method is available in most modern operating systems, but the
 details vary. Unix systems use the <code>select</code> call, where
 Win32 applications mostly use <code>WaitForMultipleObjects</code> or
 one of the related functions. The trick is to call the
 <code>WaitEvent</code> function asynchronously by supplying an
 overlapped structure, which contains a handle which will be signaled when an event occurred. Using <code>WaitForMultipleObjects</code> you
 can wait until one of the handles become signaled. I think this is
 the most suitable for most non-GUI applications. It's definitely the
 most efficient option available. When you choose to use this option,
 you'll notice that the serial classes are only a thin layer around
 the Win32 API.
</p>

<p>
 The last solution is one which will be appreciated by most Windows GUI
 programmers. Whenever something happens a message is posted to the
 application's message queue indicating what happened. Using the
 standard message dispatching this message will be processed
 eventually. This solution fits perfect in the event-driven programming
 environment and is therefore useful for most GUI (both non-MFC and
 MFC) applications. Unfortunately, the Win32 API offers no support to
 accomplish this, which is the primary reasons why the serial classes
 were created. The old Win16 API uses the <code>SetCommEventMask</code>
 and <code>EnableCommNotification</code> to do exactly this, but these
 were dropped from the Win32 API.
</p>

<h3>Block until something happens</h3>

<p>
 Blocking is the easiest way to wait for data and will therefore be
 discussed first. The <code>CSerial</code> class exposes a method
 called <code>WaitEvent</code>, which will block until an event has
 been received. You can (optionally) specify a time-out for this call,
 so it won't block forever if no data arrives anymore. The
 <code>WaitEvent</code> method can wait for several events, which must
 be registered during setup. The following events can occur on a COM
 port:
</p>

<ul>
 <li>
  <code>EEventBreak</code> is sent whenever a break was detected on
  input.
 </li>
 <li>
  <code>EEventCTS</code> means that the CTS (clear to sent) signal has
  changed.
 </li>
 <li>
  <code>EEventDSR</code> means that the DSR (data set ready) signal has
  changed.
 </li>
 <li>
  <code>EEventError</code> indicates that a line-status error has
  occured.
 </li>
 <li>
  <code>EEventRing</code> indicates that the ring indicator was set
  high. Only transitions from low to high will generate this event.
 </li>
 <li>
  <code>EEventRLSD</code> means that the RLSD
  (receive line signal detect) signal has changed. Note that this
  signal is often called CD (carrier detect).
 </li>
 <li>
  <code>EEventRecv</code> is probably one of the most important events,
  because it signals that data has been received on the COM-port.
 </li>
 <li>
  <code>EEventRcvEv</code> indicates that a certain character (the
  event character) has been received. This character can be set using
  the <code>SetEventChar</code> method.
 </li>
 <li>
  <code>EEventSend</code> indicates that the entire output buffer has
  been sent to the other side.
 </li>
</ul>

<p>
 When a serial port is opened, then the <code>EEventBreak</code>,
 <code>EEventError</code> and <code>EEventRecv</code> are being
 registered. If you would like to receive the other events then you
 have to register them using the <code>SetMask</code> method.
</p>

<p>
 Now you can use the <code>WaitEvent</code> method to wait for an
 event. You can then call <code>GetEventType</code> to obtain the
 actual event. This function will reset the event, so make sure you
 call it only once after each <code>WaitEvent</code> call. Multiple
 events can be received simultaneously (i.e. when the event character
 is being received, then <code>(EEventRecv|EEventRcvEv)</code> is
 returned. Never use the <code>==</code> operator to check for events,
 but use the <code>&</code> operator instead.
</p>

<p>
 Reading can be done using the <code>Read</code> method, but reading is
 more tricky then you might think at first. You get only an event that
 there is some data, but not how much. It could be a single byte, but
 it can also be several kilobytes. There is only one way to deal with
 this. Just read as much as you can handle (efficiently) and process
 it.
</p>

<p>
 First make sure that the port is in
 <code>EReadTimeoutNonblocking</code> mode by issuing the following
 call:
</p>

<pre>
    // Use 'non-blocking' reads, because we don't know how many bytes
    // will be received. This is normally the most convenient mode
    // (and also the default mode for reading data).
    serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);
</pre>

<p>
 The <code>Read</code> method will now read as much as possible, but
 will never block. If you would like <code>Read</code> to block, then
 specify <code>EReadTimeoutBlocking</code>. <code>Read</code> always
 returns the number of bytes read, so you can determine whether you have
 read the entire buffer. Make sure you always read the entire buffer
 after receiving the <code>EEventRecv</code> event to avoid you lose
 data. A typical <code>EEventRecv</code> will look something like this:
</p>

<pre>
    // Read data, until there is nothing left

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -