c + + read / write serial port

This article is from: https://wangbaiyuan.cn/c-serial-communication-write-reading.html
In industrial control, industrial computers (usually based on Windows platform) often need to communicate with intelligent instruments through serial port. Serial communication is convenient and widely used. Generally, the industrial computer communicates with each intelligent instrument through RS485 bus. The communication mode of RS485 is half duplex, and the industrial control PC as the main node can only poll the sub nodes of each intelligent control unit on the network in turn. The PC sends commands to the intelligent control unit through the serial port for each communication, and the intelligent control unit responds after receiving the correct command.

Under Win32, two programming methods can be used to realize serial communication. One is to use ActiveX control. This method is simple, but not flexible. The second is to call the API function of Windows. This method can clearly grasp the mechanism of serial communication and is free and flexible. In this article, we only introduce the API serial communication part. There are two operation modes for serial port: synchronous operation mode and overlapping operation mode (also known as asynchronous operation mode). During synchronous operation, API functions will be blocked and will not return until the operation is completed (in multithreading mode, although the main thread will not be blocked, the listening thread will still be blocked); In the overlapping operation mode, the API function will return immediately, and the operation will be carried out in the background to avoid thread blocking. Regardless of the operation mode, it is generally completed in four steps:

(1) Open serial port
(2) Configure serial port
(3) Read write serial port
(4) Close the serial port

1. Open serial port

Win32 system extends the concept of file. Whether it is a file, communication device, named pipe, mail slot, disk, or console, it is opened or created with the API function CreateFile. The prototype of this function is:

C + + code

HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD 
dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD
 dwCreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);

lpFileName: logical name of the serial port to be opened, such as "COM1";
dwDesiredAccess: Specifies the type of serial port access, which can be read, write or both in parallel;
dwShareMode: Specifies the sharing attribute. Since the serial port cannot be shared, this parameter must be set to 0;
lpSecurityAttributes: refers to the security attribute structure. The default value is NULL;
dwCreationDistribution: create flag. For serial port operation, this parameter must be set to OPEN_EXISTING;
dwFlagsAndAttributes: attribute description, used to specify whether the serial port performs asynchronous operation. The value is FILE_FLAG_OVERLAPPED means asynchronous I/O is used; This value is 0, indicating synchronous I/O operation;
hTemplateFile: for serial port, this parameter must be set to NULL.

Example code of opening serial port in synchronous I/O mode: C + + code

HANDLE hCom; //Global variable, serial port handle
hCom=CreateFile("COM1",//COM1 port
GENERIC_READ|GENERIC_WRITE, //Allow reading and writing
0, //exclusive mode 
NULL,
OPEN_EXISTING, //Open instead of create
0, //Synchronization mode
NULL);
if(hCom==(HANDLE)-1)
{
AfxMessageBox("open COM fail!");
return FALSE;
}
return TRUE;

Example code of overlapping I/O open serial port:
C + + code

HANDLE hCom; //Global variable, serial port handle
hCom =CreateFile("COM1", //COM1 port
GENERIC_READ|GENERIC_WRITE, //Allow reading and writing
0, //exclusive mode 
NULL,
OPEN_EXISTING, //Open instead of create
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //Overlapping mode
NULL);
if(hCom ==INVALID_HANDLE_VALUE)
{
AfxMessageBox("open COM fail!");
return FALSE;
}
return TRUE;

2. Configure serial port

After opening the communication device handle, it is often necessary to initialize and configure the serial port. This needs to be done through a DCB structure. The DCB structure contains information such as baud rate, data bits, parity and stop bits. When querying or configuring the properties of the serial port, the DCB structure should be used as the buffer.

Generally, after opening the serial port with CreateFile, you can call GetCommState function to obtain the initial configuration of the serial port. To modify the configuration of the serial port, you should first modify the DCB structure, and then call the SetCommState function to set the serial port.

The DCB structure contains various parameter settings of the serial port. Here are only a few common variables of the structure: typedef struct_ DCB{ ………

DWORD BaudRate: baud rate, which specifies the transmission rate of communication equipment. This member can be the actual baud rate value or one of the following constant values: CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000, CBR_ fourteen thousand and four hundred

DWORD fParity: Specifies parity enable. If this member is 1, allow parity check

BYTE ByteSize: number of communication bytes, 4-8

BYTE Parity: Specifies the parity method. This member can have the following values: EVENPARITY parity
NOPARITY no parity mark parity odd parity

BYTE StopBits: Specifies the number of stop bits. This member can have the following values: ONESTOPBIT 1 stop bit TWOSTOPBITS 2 stop bits ON 5STOPBITS 1.5 stop bits

GetCommState function can obtain the device control block of COM port, so as to obtain relevant parameters

BOOL GetCommState(
HANDLE hFile, //Handle identifying the communication port
LPDCB lpDCB //Pointer to a device control block (DCB structure);

SetCommState function sets the device control block of COM port:

BOOL SetCommState( HANDLE hFile, LPDCB lpDCB );

In addition to the settings in DCB, programs generally need to set the size and timeout of I/O buffer. Windows uses I/O buffer to temporarily store the input and output data of serial port. If the communication rate is high, a large buffer should be set. Call the SetupComm function to set the size of the input and output buffers of the serial port.

 BOOL SetupComm( HANDLE hFile, // Handle to the communication device 
 DWORD dwInQueue, // Size of the input buffer (in bytes)
  DWORD dwOutQueue // Size of output buffer (bytes); 

When using ReadFile and WriteFile to read and write serial ports, you need to consider the timeout problem. The function of timeout is that if the specified number of characters are not read or sent within the specified time, the operation of ReadFile or WriteFile will still end.

To query the current timeout setting, call the GetCommTimeouts function, which populates a COMMTIMEOUTS structure. Call SetCommTimeouts to set the timeout with the contents of a COMMTIMEOUTS structure.

There are two kinds of timeout for reading and writing serial port: interval timeout and total timeout. Interval timeout refers to the maximum delay between two characters when receiving. Total timeout refers to the maximum total time spent by read and write operations. Write operation only supports total timeout, while read operation supports both timeout.

The commtimeout structure can be used to specify the timeout of read and write operations. The commtimeout structure is defined as:

typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout; //Read interval timeout
DWORD ReadTotalTimeoutMultiplier; //Reading time coefficient
DWORD ReadTotalTimeoutConstant; //Read time constant
DWORD WriteTotalTimeoutMultiplier; // Write time coefficient
DWORD WriteTotalTimeoutConstant; //Write time constant
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

Members of the commtimeout structure are in milliseconds. The calculation formula of total timeout is:
Total timeout = time factor × Number of characters required to read / write + time constant
For example, if you want to read 10 characters, the calculation formula of the total timeout of the read operation is: total timeout of the read = readtotaltimeoutmultipler × 10 + ReadTotalTimeoutConstant shows that the setting of interval timeout and total timeout is irrelevant, which can facilitate the communication program to set various timeouts flexibly. If all write timeout parameters are 0, the write timeout is not used. If ReadIntervalTimeout is 0, the read interval timeout is not used.

If both readtotaltimeoutmultipler and ReadTotalTimeoutConstant are 0, the total read timeout is not used. If the read interval timeout is set to maxword and the read time coefficient and read time constant are 0, the read operation returns immediately after reading the contents of the input buffer once, regardless of whether the required characters are read in or not. When reading and writing serial ports in overlapping mode, although ReadFile and WriteFile may return before completing the operation, the timeout still works. In this case, the timeout specifies the completion time of the operation, not the return time of ReadFile and WriteFile.

Sample code for configuring serial port:

SetupComm(hCom,1024,1024); //The size of input buffer and output buffer is 1024

COMMTIMEOUTS TimeOuts; //Set read timeout
TimeOuts.ReadIntervalTimeout=1000;
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000; //Set write timeout
TimeOuts.WriteTotalTimeoutMultiplier=500;
TimeOuts.WriteTotalTimeoutConstant=2000;
SetCommTimeouts(hCom,&TimeOuts); //Set timeout

DCB dcb;
GetCommState(hCom,&dcb);

dcb.BaudRate=9600; //The baud rate is 9600
dcb.ByteSize=8; //Each byte has 8 bits
dcb.Parity=NOPARITY; //No parity bit
dcb.StopBits=TWOSTOPBITS; //Two stop bits

SetCommState(hCom,&dcb);

PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

Before reading and writing the serial port, the buffer should be emptied with the PurgeComm() function. The prototype of this function is:

 BOOL PurgeComm( HANDLE hFile, //Serial port handle 
 DWORD dwFlags // Operations to be completed); 

The parameter dwFlags specifies the operation to be completed, which can be a combination of the following values:
PURGE_TXABORT interrupts all write operations and returns immediately, even if the write operation has not been completed
PURGE_RXABORT interrupts all read operations and returns immediately, even if the read operation has not been completed. PURGE_TXCLEAR clear output buffer
PURGE_RXCLEAR clears the input buffer

3. Read write serial port

We use ReadFile and WriteFile to read and write serial ports. The following are the declarations of the two functions:

BOOL ReadFile( 
//Serial port handle
HANDLE hFile, 
// The address of the read data store,
// That is, the read data will be stored in a memory area with the value of the pointer as the first address
LPVOID lpBuffer,
// The number of bytes of data to be read in
DWORD nNumberOfBytesToRead,
// Points to a DWORD value that returns the number of bytes actually read in by the read operation
LPDWORD lpNumberOfBytesRead,
// During overlapping operation, this parameter points to an OVERLAPPED structure. During synchronization operation, this parameter is NULL.
LPOVERLAPPED lpOverlapped );
BOOL WriteFile( 
//Serial port handle
HANDLE hFile, 
// Address of the written data store,
// That is, the address starting with the value of the pointer
LPCVOID lpBuffer,
//Number of bytes of data to write
DWORD nNumberOfBytesToWrite,
// Points to a DWORD value that returns the number of bytes actually written
LPDWORD lpNumberOfBytesWritten,
// During overlapping operation, this parameter points to an OVERLAPPED structure,
// This parameter is NULL during synchronization operation.
LPOVERLAPPED lpOverlapped );

When reading and writing serial ports with ReadFile and WriteFile, they can be executed synchronously or overlapped.

During synchronous execution, the function does not return until the operation is completed. This means that threads will be blocked during synchronous execution, resulting in reduced efficiency.

When overlapping execution, the two functions will return immediately even if the operation has not been completed, and the time-consuming I/O operation is carried out in the background.

Whether the ReadFile and WriteFile functions are synchronous or asynchronous depends on the CreateFile function. If file is specified when calling CreateFile to create a handle_ FLAG_ Overlapped flag, then the operation of calling ReadFile and WriteFile on the handle should overlap; If the overlap flag is not specified, the read and write operations should be synchronized.

The synchronization or asynchrony of the ReadFile and WriteFile functions should be consistent with the CreateFile function. As long as the ReadFile function reads a specified number of characters in the serial port input buffer, the operation is completed. The WriteFile function not only copies the specified number of characters into the output buffer, but also waits until these characters are sent out from the serial port. Both functions return TRUE if the operation is successful. It should be noted that when ReadFile and WriteFile return FALSE, the operation does not necessarily fail. The thread should call the GetLastError function to analyze the returned results. For example, in overlapping operations, if the function returns before the operation is completed, the function returns FALSE, and the GetLastError function returns ERROR_IO_PENDING. This indicates that the overlap operation has not been completed. The synchronous reading and writing serial port is relatively simple. The following is an example of the code of synchronous reading and writing serial port:

Synchronous read serial port

char str[100];

DWORD wCount;//Number of bytes read

BOOL bReadStat;

bReadStat=ReadFile(hCom,str,100,&wCount,NULL);

if(!bReadStat) { AfxMessageBox("Failed to read serial port!"); return FALSE; } return TRUE; //Synchronous write serial port

char lpOutBuffer[100];

DWORD dwBytesWrite=100;

COMSTAT ComStat;

DWORD dwErrorFlags;

BOOL bWriteStat;

ClearCommError(hCom,&dwErrorFlags,&ComStat);

bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);

if(!bWriteStat) { AfxMessageBox("Failed to write serial port!"); }

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

Asynchronous read serial port

When using ReadFile and WriteFile overlapping operations, the thread needs to create an OVERLAPPED structure for the use of these two functions. The thread obtains the current operation state through the OVERLAPPED structure. The most important member of this structure is hEvent. Heevent is a read-write event. When the serial port uses asynchronous communication, the operation may not be completed when the function returns. The program can know whether the reading and writing is completed by checking this event. When calling ReadFile and WriteFile functions, the member will be automatically set to no signal state; When the overlapping operation is completed, the member variable will be automatically set to the signaled state. GetOverlappedResult function bool GetOverlappedResult (handle hfile, / / handle to the serial port / / points to the OVERLAPPED structure specified at the beginning of the overlap operation, LPOVERLAPPED lpOverlapped, / / points to a 32-bit variable whose value returns the number of bytes actually transferred in the read-write operation. Lpdword lpnumberofbytesttransferred, / / this parameter is used to specify whether the function waits until the end of the overlap operation. / / if this parameter is TRUE, the function Do not return until the end of the operation// If the parameter is FALSE, the function returns directly. At this time, if the operation is not completed, / / error will be returned by calling GetLastError() function_ IO_ INCOMPLETE. BOOL bWait ); This function returns the result of overlapping operation and is used to judge whether the asynchronous operation is completed. It is realized by judging whether the hEvent in the OVERLAPPED structure is set.

Sample code of asynchronous read serial port:

char lpInBuffer[1024];

DWORD dwBytesRead=1024;

COMSTAT ComStat;

DWORD dwErrorFlags;

OVERLAPPED m_osRead;

memset(&m_osRead,0,sizeof(OVERLAPPED));

m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

ClearCommError(hCom,&dwErrorFlags,&ComStat);

dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);

if(!dwBytesRead) return FALSE;

BOOL bReadStatus;

bReadStatus=ReadFile(hCom,lpInBuffer, dwBytesRead,&dwBytesRead,&m_osRead);

if(!bReadStatus)

//If the ReadFile function returns FALSE

{

if(GetLastError()==ERROR_IO_PENDING)

//The GetLastError() function returns ERROR_IO_PENDING indicates that the serial port is reading

{

WaitForSingleObject(m_osRead.hEvent,2000);

//Use the WaitForSingleObject function to wait until the read operation is completed or the delay has reached 2 seconds

//When the serial port read operation is completed, M_ The heevent event of osread will become signaled

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

return dwBytesRead;

}

return 0;

}

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

return dwBytesRead;

Briefly explain the above Codes:
Before using the ReadFile function to read, you should first use the ClearCommError function to clear the error. The prototype of ClearCommError function is as follows:

BOOL ClearCommError(
 HANDLE hFile,// Serial port handle
LPDWORD lpErrors, // A variable that points to the error code received
LPCOMSTAT lpStat // Point to communication status buffer);

This function obtains the communication error and reports the current status of the serial port. At the same time, this function clears the error flag of the serial port to continue the input and output operations.

The parameter lpStat points to a COMSTAT structure that returns serial port status information. COMSTAT structure COMSTAT structure contains the information of serial port. The structure definition is as follows:

typedef struct _COMSTAT { // cst 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 rec''d 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;

This article only uses the cbInQue member variable, whose value represents the number of bytes of the input buffer. Finally, use the PurgeComm function to clear the input and output buffer of the serial port. This code uses the WaitForSingleObject function to wait for the hEvent member of the OVERLAPPED structure. Next, we will demonstrate a waiting process by calling the GetOverlappedResult function
Asynchronous read serial port example code:

char lpInBuffer[1024];
DWORD dwBytesRead=1024;
BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;
OVERLAPPED m_osRead;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
if(!ComStat.cbInQue) return 0;
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
bReadStatus=ReadFile(hCom, lpInBuffer,dwBytesRead, &dwBytesRead,&m_osRead);
if(!bReadStatus) //If the ReadFile function returns FALSE
{ if(GetLastError()==ERROR_IO_PENDING)
{ GetOverlappedResult(hCom, &m_osRead,&dwBytesRead,TRUE);
// The last parameter of GetOverlappedResult function is set to TRUE,
//The function waits until the read operation completes or returns due to an error.
return dwBytesRead; }
return 0; }
return dwBytesRead;

Sample code of asynchronous write serial port:

char buffer[1024];
DWORD dwBytesWritten=1024;
DWORD dwErrorFlags;
COMSTAT ComStat;
OVERLAPPED m_osWrite;
BOOL bWriteStat;
bWriteStat=WriteFile(hCom,buffer,dwBytesWritten, &dwBytesWritten,&m_OsWrite);
if(!bWriteStat)
{ if(GetLastError()==ERROR_IO_PENDING)
{ WaitForSingleObject(m_osWrite.hEvent,1000);
return dwBytesWritten; }
return 0; }
return dwBytesWritten;

4. Close the serial port

Closing the serial port with API function is very simple. You only need to call CloseHandle with the handle returned by CreateFile function as a parameter:

BOOL CloseHandle(
HANDLE hObject; //handle to object to close
);

Keywords: MFC

Added by god_zun on Thu, 10 Feb 2022 12:45:14 +0200