In depth analysis of Visual C++ serial port communication programming

  • 2020-04-01 23:40:06
  • OfStack

Using Visual C++ to design asynchronous serial communication program in Windows environment can use different methods. One method can use the serial port API function provided by Windows system. Another approach is to use directly the ActiveX control mscomm.ocx provided by Microsoft. Using mscomm.ocx control for serial port program design is relatively simple, as long as the control's properties, events and methods to set and operate, can complete the simple serial communication function. The serial port API function provided by Windows system is relatively flexible. In the experiment, you can program any one of them according to your own situation. The following is a brief introduction of how to program using the serial port API function provided by Windows system

In Windows, serial ports and other communication devices are processed as files. The functions used to open, close, send, and receive the serial port are the same as those used to manipulate the file. In general, the application of Visual C++ for asynchronous serial communication program design can usually be divided into four major stages, which are the serial port opening stage, the serial port state value reading and property setting stage, the serial data sending and receiving stage, and the serial port closing stage.

(1) open the serial port
The serial port is first opened before all operations are performed on it. The opening of the serial port can be done using the CreateFile function, which returns a handle for use in subsequent operations related to the serial port. As with file operations, when the serial port is opened with the CreateFile, the serial port can be specified as "read access", "write access", or "read access".

HANDLE CreateFile ( 
LPCTSTR  lpFileName
DWORD   dwDesiredAccess
DWORD   dwSharedMode
LPSECURITY_ATTRIBUTES  lpSecurityAttributes
DWORD   dwCreationDisposition
DWORD   dwFlagsAndAttributes
HANDLE   hTemplateFile
) ; 

On a successful call, the CreateFile returns a handle to the open file, which will be used in subsequent call functions related to the serial port. If the call fails, the CreateFile returns INVALID_HANDLE_VALUE.
(2) state reading and property setting of serial port
Once the serial port is opened, the properties of the serial port can be set. Because the properties of the serial port are very complex, it is common to read the current state value of the serial port and modify it based on it.
Gets the current state of the serial port

BOOL  GetCommState ( 
  HANDLE hFile
  LPDCB  lpDCB
 ); 

The first argument to the GetCommState function, hFile, is a handle to the open serial port returned by the CreateFile function. The second parameter points to the device control block DCB. DCB is a very important data structure in which almost all serial port properties and states are stored in member variables.
Set the serial port
Windows system USES SetCommState function to modify the current parameter configuration of the serial port. The SetCommState function is declared as follows:

BOOL  SetCommState ( 
  HANDLE hFile
  LPDCB  lpDCB
 ); 

The first argument to the GetCommState function, hFile, is a handle to the open serial port returned by the CreateFile function. The second parameter points to the device control block DCB. If the function is called successfully, the return value is non-0. If the function call fails, the return value is 0. When the application only needs to modify the configuration value of a part of the serial port, it can obtain the current DCB structure through the GetCommState function, change the parameters, and then call the SetCommState function to set the modified DCB to configure the serial port.
Allocate receive and send buffers for the serial port
When a serial port is opened, a send buffer and a receive buffer can be allocated to the serial port. The configuration of the serial port send and receive buffers can be implemented by the function SetupComm. If SetupComm is not called, the system allocates the default send and receive buffers for the serial port. However, in order to ensure that the buffer size is consistent with the actual needs, it is best to call this function to set it up. The prototype of SetupComm function is as follows:

BOOL  SetupComm ( 
 HANDLE hFile
 DWORD dwInQueue
 DWORD dwOutQueue
 ); 

Where the hFile is a handle to the open serial port returned by the CreateFile function. The parameters dwInQueue and dwOutQueue specify the size of the receive buffer and the send buffer recommended by the application, respectively.
Clear the receive and send buffers
It is best to use the PurgeComm function to clean the data in the send and receive buffers of the serial port before performing all the send and receive data operations of the serial port. The PurgeComm function prototype is as follows:

BOOL  PurgeComm ( 
 HANDLE  hFile
 DWORD   dwFlages
 ); 

The parameter hFile is a handle to the open serial port returned by the CreateFile function, and the parameter dwFlags indicates the action to be performed. If dwFlags is PURGE_TXCLEAR, the system is told to clear the send buffer. If dwFlags is PURGE_RXCLEAR, the system is told to empty the receive buffer. If you need to empty both the send and receive buffers, set dwFlags to PURGE_TXCLEAR|PURGE_RXCLEAR. If the PurgeComm function is called successfully, the return value is non-0; If the function call fails, the return value is 0.
(3) sending and receiving serial data
Similar to ordinary file operations, ReadFile function is usually used to read the data received by the serial port while WriteFile is used to write the data to be sent to the serial port.
The reception of serial data
The ReadFile function is used to read the data received from the serial port. The ReadFile function prototype is as follows:

BOOL  ReadFile ( 
HANDLE  hFile
LPVIOD   lpBuffer
DWORD   nNumberOfBytesToRead
LPDWORD   lpNumberOfBytesRead
LPOVERLAPPED  lpOverlapped
 ); 

Where the parameter hFile points to the open serial port handle; LpBuffer points to a read data buffer; NNumberOfBytesToRead specifies the number of bytes to read from a serial device; LpNumberOfBytesRead indicates the number of bytes actually read from the serial port; LpOverlapped points to an OVERLAPPED structure variable that contains a synchronized event.

Normally, if the call is successful, ReadFile returns a non-0 value; Otherwise the return value is 0. However, a return value of 0 does not necessarily indicate a function call failure for a serial port where the receive operation is performed in the background. At this point you can call the GetLastError function for further information. If the GetLastError returns ERROR_IO_PENDING, the read serial port operation is still in the background waiting state, not a real error.
The sending of serial data
The WriteFile function allows you to write data to a serial port. The prototype of the WriteFile function is as follows:

BOOL  WriteFile ( 
HANDLE  hFile
LPVIOD   lpBuffer
DWORD   nNumberOfBytesToWrite
LPDWORD   lpNumberOfBytesWritten
LPOVERLAPPED  lpOverlapped
 ); 

Where the parameter hFile points to the open serial port handle; LpBuffer points to a send data buffer; NNumberOfBytesToRead specifies the number of bytes to send from a serial device; LpNumberOfBytesRead indicates the number of bytes actually sent from the serial port; LpOverlapped points to an OVERLAPPED structure variable that contains a synchronized event.

Normally, if the call succeeds, the WriteFile returns a non-0 value; Otherwise the return value is 0. But a return value of 0 does not necessarily mean that the function call failed for a serial port where the send operation takes place in the background. At this point you can call the GetLastError function for further information. If the GetLastError returns ERROR_IO_PENDING, the write to the serial port is still in the background waiting state, not a real error.
(4) close the serial port
The serial port is usually closed after use. If you forget to close it, the serial port is always open and cannot be opened or used by other applications.
Close the serial port using the function CloseHandle, whose function prototype is as follows:

BOOL  CloseHandle ( 
HANDLE  hObject
 ); 

The CloseHandle function is very simple, where hObject opens the handle for the serial port. If the function is successfully called, the return value is non-0. Otherwise the return value is 0.

Related articles: