In-depth introduction to VC ++ serial Programming Based on Win32 API

Source: Internet
Author: User
1. API description

In Win32 API, the serial port uses the file access method, and its operation API is basically the same as the file operation API.

Open serial port

In Win32, the API function used to open the serial port is createfile, and its prototype is:

HANDLE CreateFile (
Lptstr lpFileName, // name of the serial port to be opened, such as COM1 or COM2
DWORD dwACCESS, // specifies the serial access type, which can be read, write, or parallel
DWORD dw1_mode, // specify the sharing attribute. This parameter must be set to 0 because the serial port cannot be shared.
LPSECURITY_ATTRIBUTES lpsa, // reference the Security Attribute structure. The default value is NULL.
DWORD dwCreate, // create a flag. For serial port operations, this parameter must be set to open existing.
DWORD dwAttrsAndFlags, // attribute description, used to specify whether asynchronous operations can be performed on the serial port,
// FILE_FLAG_OVERLAPPED: asynchronous I/O can be used.
Handle htemplatefile // The handle pointing to the template file. For the serial port, this parameter must be set to null.
);

For example, the following program is used to open the serial port COM1 in synchronous read/write mode:

Handle hcom;
DWORD dwerror;
Hcon = createfile ("COM1", generic_read | generic_write, 0, null, open_existing, 0, null );
If (hcom = (handle) 0 xffffffff)
{
Dwerror = getlasterror ();
MessageBox (dwerror );
}

The dwattrsandflags parameter and the file_flag_overlapped flag are described as follows: Windows file operations are divided into synchronous I/O and overlapping I/O (overlapped I/O) methods, in synchronous I/O mode, the API will be blocked until the operation is complete (in multi-threaded mode, although the main thread will not be blocked, the listener thread will still be blocked ); in the overlapping I/O mode, the API will return immediately and the operation will be performed in the background to avoid thread blocking. Overlapping I/O is flexible, and it can also implement blocking (for example, we can set that we must read a data before proceeding to the next operation ). If the I/O operation is returned when the API does not complete the operation, we can call the getoverlappedresult () function to block the operation and return the result.

Configure serial port

The serial port configuration is implemented by changing the member variable value of the device control block DCB (Device Control Block). The size of the receiving buffer and sending buffer can be set through the setupcomm function.

DCB struct is defined:

Typedef struct _ DCB {// DCB
DWORD dcblength; // sizeof (DCB)
DWORD baudrate; // current baud rate
DWORD fbinary: 1; // binary mode, no EOF check
DWORD fparity: 1; // enable parity checking
DWORD foutxctsflow: 1; // CTS output flow control
DWORD foutxdsrflow: 1; // DSR output flow control
DWORD fdtrcontrol: 2; // DTR Flow Control Type
DWORD fdsrsensiti.pdf: 1; // DSR sensiti.pdf
DWORD fTXContinueOnXoff: 1; // XOFF continues Tx
DWORD fOutX: 1; // XON/XOFF out flow control
DWORD fInX: 1; // XON/XOFF in flow control
DWORD fErrorChar: 1; // enable error replacement
DWORD fNull: 1; // enable null stripping
DWORD fRtsControl: 2; // RTS flow control
DWORD fAbortOnError: 1; // abort reads/writes on error
DWORD fDummy2: 17; // reserved
WORD wReserved; // not currently used
WORD XonLim; // transmit XON threshold
WORD XoffLim; // transmit XOFF threshold
BYTE ByteSize; // number of bits/byte, 4-8
BYTE Parity; // 0-4 = no, odd, even, mark, space
BYTE StopBits; // 0, 1, 2 = 1, 1.5, 2
Char XonChar; // Tx and Rx XON character
Char XoffChar; // Tx and Rx XOFF character
Char ErrorChar; // error replacement character
Char EofChar; // end of input character
Char EvtChar; // encoded ed event character
WORD wReserved1; // reserved; do not use
} DCB;
The prototype of the SetupComm function is:
BOOL SetupComm (
HANDLE hFile, // handle to communications device
DWORD dwInQueue, // size of input buffer
DWORD dwOutQueue // size of output buffer
);

The following Program sets the serial port as follows: the baud rate is 9600, the number of data digits is 7 bits, the Stop bits are 2 bits, the parity check, the size of the receiving buffer and the sending buffer are both 1024 bytes, finally, use the purgecomm function to terminate all background read/write operations and clear the receiving and sending buffers:

DCB dcb;
Dcb. BaudRate = 9600; // The baud rate is 9600.
Dcb. ByteSize = 7; // The number of data digits is 7 digits.
Dcb. Parity = EVENPARITY; // Parity check
Dcb. StopBits = 2; // two stop bits
Dcb. fBinary = TRUE;
Dcb. fParity = TRUE;
If (! SetCommState (hCom, & dcb ))
{
MessageBox ("An error occurred while setting the serial port! ");
}
SetupComm (hCom, 1024,102 4 );
Purgecomm (hcom, purce_txabort | purge_rxabort | purge_txclear | purge_rxclear );

Timeout settings

Timeout settings are implemented by changing the value of the member variable of the commtimeouts struct. The prototype of commtimeouts is:

Typedef struct _ commtimeouts
{
DWORD readintervaltimeout; // defines the maximum time interval between two characters, in milliseconds
// When the value of readintervaltimeout is exceeded after one character is read, it will
// Timeout
DWORD readtotaltimeoutmultiplier;
DWORD readtotaltimeoutconstant;
// The relationship between each time is as follows:
// Readtotaltimeout = readtotaltimeoutmultiplier * bytestoread + readtotaltimeoutconstant
DWORD writetotaltimeoutmultiplier;
DWORD writetotaltimeoutconstant;
} Commtimeouts, * lpcommtimeouts;

Set the timeout function to setcommtimeouts. In its prototype, the pointer to receive commtimeouts is a parameter:

Bool setcommtimeouts (
Handle hfile, // handle to communications device
Lpcommtimeouts // pointer to comm time-out Structure
);

The following procedure sets the serial port read operation timeout to 10 milliseconds:

Commtimeouts;
Memset (& to, 0, sizeof ());
To. ReadIntervalTimeout = 10;
SetCommTimeouts (hCom, & );

The prototype of the getcommtimeouts () function corresponding to setcommtimeouts is:

BOOL GetCommTimeouts (
HANDLE hFile, // handle of communications device
LPCOMMTIMEOUTS lpCommTimeouts // pointer to comm time-out structure
);

Event settings

Before reading and writing the serial port, you need to use the setcommmask () function to set the event mask to monitor the events on the specified communication port. Its prototype is:

BOOL SetCommMask (
HANDLE hFile, // The HANDLE that identifies the communication port
DWORD dwEvtMask // communication event that can be enabled
);

With set, get will also be available. The prototype of the getcommmask () function corresponding to setcommmask is:

BOOL GetCommMask (
HANDLE hFile, // The HANDLE that identifies the communication port
LPDWORD lpEvtMask // address of variable to get event mask
);

Events that can occur on the serial port can be one or any combination of the following events: ev_break, ev_cts, ev_dsr, ev_err, ev_ring, ev_rlsd, ev_rxchar, ev_rxflag, and ev_txempty.

We can use the waitcommevent () function to wait for the event we set using the setcommmask () function on the serial port:

BOOL WaitCommEvent (
HANDLE hFile, // The HANDLE that identifies the communication port
LPDWORD lpEvtMask, // address of variable for event that occurred
LPOVERLAPPED lpOverlapped, // address of overlapped structure
);

The waitcommevent () function is blocked until a communication event set by the setcommmask () function occurs on the serial port. Generally, when waitcommevent () returns an event, the programmer can analyze * lpevtmask to obtain the event type and then process it accordingly.

Read serial port

The functions used to read the serial port are the same as those used to read the file. The READ function is prototype as follows:

BOOL ReadFile (
HANDLE hFile, // handle of file to read
LPVOID lpBuffer, // pointer to buffer that records es data
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // pointer to number of bytes read
LPOVERLAPPED lpOverlapped // pointer to structure for overlapped I/O
);

Serial Port writing

The functions used for writing data to the serial port are the same as those used for writing data to the file. The writing function is prototype as follows:

BOOL WriteFile (
HANDLE hFile, // handle to file to write
Lpvoid lpBuffer, // pointer to data to write to file
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
LPOVERLAPPED lpOverlapped // pointer to structure for overlapped I/O
);

Close serial port

It is very easy to disable the serial port when using the API function for serial communication. You only need to use the handle returned by the createfile function as the parameter to call closehandle:

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

2. Routine

In my comprehensive example of Win32 multi-threaded program design, we have provided an example of Serial Communication Using win api. Here is a similar example, to further deepen understanding.

The content in the resource file (. RC) corresponding to the control on the dialog box is as follows:

BEGIN
EDITTEXT IDC_RECV_EDIT, 28,119,256, 46, ES_AUTOHSCROLL
GROUPBOX "send data", IDC_STATIC, 19,15, 282,70
GROUPBOX "receive data", IDC_STATIC, 19,100,282, 80
EDITTEXT IDC_SEND_EDIT, 29,33, 214,39, ES_AUTOHSCROLL
PUSHBUTTON "clear", IDC_CLEAR_BUTTON, 248-33, 50,14
PUSHBUTTON "send", IDC_SEND_BUTTON, 248,55, 50,14
END

The message ing of the entire dialog box (describes the message and its corresponding behavior) is as follows:

BEGIN_MESSAGE_MAP (CSerialPortAPIDlg, CDialog)
// {AFX_MSG_MAP (CSerialPortAPIDlg)
ON_WM_SYSCOMMAND ()
ON_WM_PAINT ()
ON_WM_QUERYDRAGICON ()
ON_BN_CLICKED (IDC_CLEAR_BUTTON, OnClearButton)
ON_BN_CLICKED (IDC_SEND_BUTTON, OnSendButton)
ON_MESSAGE (COM_RECVDATA, OnRecvData)
//} AFX_MSG_MAP
END_MESSAGE_MAP ()

We added a CString variable m_recv and m_send for the IDC_SEND_EDIT and IDC_RECV_EDIT edit box controls respectively. The following code describes this line:

Class CSerialPortAPIDlg: public CDialog
{
// Construction
Public:
Cserialportapidlg (cwnd * pparent = NULL); // standard Constructor

// Dialog data
// {Afx_data (cserialportapidlg)
Enum {IDD = idd_serialportapi_dialog };
Cstring m_recv; // The variable corresponding to the idc_recv_edit Control
Cstring m_send; // variable corresponding to the idc_send_edit Control
//} Afx_data

// Classwizard generated virtual function overrides
// {Afx_virtual (cserialportapidlg)
Protected:
Virtual void dodataexchange (cdataexchange * PDX); // DDX/DDV support
//} Afx_virtual

// Implementation
Protected:
BOOL OpenSerialPort1 ();
HICON m_hIcon;

// Generated message map functions
// {AFX_MSG (CSerialPortAPIDlg)
Virtual BOOL OnInitDialog ();
Afx_msg void OnSysCommand (UINT nID, LPARAM lParam );
Afx_msg void OnPaint ();
Afx_msg HCURSOR OnQueryDragIcon ();
Afx_msg void OnClearButton ();
Afx_msg void OnSendButton ();
Afx_msg void OnRecvData (WPARAM wParam, LPARAM lParam );
//} AFX_MSG
DECLARE_MESSAGE_MAP ()
};

CSerialPortAPIDlg: CSerialPortAPIDlg (CWnd * pParent/* = NULL */)
: CDialog (CSerialPortAPIDlg: IDD, pParent)
{
// {AFX_DATA_INIT (CSerialPortAPIDlg)
// Initialize the variable in the constructor
M_recv = _ T (""); // initialize the variable in the constructor
M_send = _ T ("");
//} AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
M_hIcon = AfxGetApp ()-> LoadIcon (IDR_MAINFRAME );
}

// Create a ing between the edit box control and the variable
Void CSerialPortAPIDlg: DoDataExchange (CDataExchange * pDX)
{
CDialog: DoDataExchange (pDX );
// {AFX_DATA_MAP (CSerialPortAPIDlg)
DDX_Text (pDX, IDC_RECV_EDIT, m_recv );
DDX_Text (pDX, IDC_SEND_EDIT, m_send );
//} AFX_DATA_MAP
}

In the OnInitDialog () function of the dialog box, we start the window listening thread and pass the main window handle to the thread control function:

Bool cserialportapidlg: oninitdialog ()
{
Cdialog: oninitdialog ();

// Add "About..." menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.
ASSERT (IDM_ABOUTBOX & 0xFFF0) = IDM_ABOUTBOX );
ASSERT (IDM_ABOUTBOX <0xF000 );

CMenu * pSysMenu = GetSystemMenu (FALSE );
If (pSysMenu! = NULL)
{
CString strAboutMenu;
StrAboutMenu. LoadString (IDS_ABOUTBOX );
If (! StrAboutMenu. IsEmpty ())
{
PSysMenu-> AppendMenu (MF_SEPARATOR );
PSysMenu-> AppendMenu (MF_STRING, IDM_ABOUTBOX, strAboutMenu );
}
}

// Set the icon for this dialog. The framework does this automatically
// When the application's main window is not a dialog
SetIcon (m_hIcon, TRUE); // Set big icon
SetIcon (m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here
// Start the serial port monitoring thread
DWORD threadID;
HCommThread =: CreateThread (LPSECURITY_ATTRIBUTES) NULL, 0,
(LPTHREAD_START_ROUTINE) SerialPort1ThreadProcess,
Afxgetmainwnd ()-> m_hwnd, 0, & threadid );
If (hcommthread = NULL)
{
: Afxmessagebox ("failed to create the serial port 1 processing thread ");
: Postquitmessage (0 );
}
Return true; // return true unless you set the focus to a control
}

// "Clear" Button Function
Void cserialportapidlg: onclearbutton ()
{
// Todo: add your control notification handler code here
M_send = "";
Updatedata (false );
}

// Data sending function ("send" button function)
Void cserialportapidlg: onsendbutton ()
{
// Todo: add your control notification handler code here
Updatedata (true );
DWORD wcount = 0;
Writefile (hcom, m_send, m_send.getlength (), & wcount, null); // send data
}

// After receiving data (user-defined messages sent by the listening thread) display
Void CSerialPortAPIDlg: OnRecvData (WPARAM wParam, LPARAM lParam)
{
CString recvStr (char *) wParam );
M_recv + = recvStr;
UpdateData (false );
}

Add the SerialPortControl. h and SerialPortControl. cpp files to the project. The former declares the interface functions and external global variables of the serial port control, and the latter implements the serial port interface functions and the serial port listening thread control functions.

SerialPortControl. h file

# Ifndef _ serial_port_control_h
# DEFINE _ serial_port_control_h

# Define COM_RECVDATA WM_USER + 1000 // custom message

Extern HANDLE hCom; // global variable, serial port HANDLE
Extern HANDLE hCommThread; // global variable, serial port thread
// Serial port monitoring thread control function
Extern dword winapi SerialPort1ThreadProcess (HWND hWnd );
// Open and set PC serial port 1 (COM1)
Extern BOOL OpenSerialPort1 ();

# Endif
SerialPortControl. cpp File
# Include "StdAfx. h"
# Include "SerialPortControl. h"

HANDLE hCom; // global variable, serial port HANDLE
HANDLE hCommThread; // global variable, serial thread

BOOL OpenSerialPort1 ()
{
// Open and set COM1
HCom = CreateFile ("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
If (hCom = (HANDLE)-1)
{
AfxMessageBox ("failed to enable COM1 ");
Return false;
}
Else
{
DCB wdcb;
GetCommState (hCom, & wdcb );
Wdcb. BaudRate = 9600; // baud rate: 9600; others: UNCHANGED
SetCommState (hCom, & wdcb );
PurgeComm (hCom, PURGE_TXCLEAR );
}
Return true;
}

// Data received by different monitoring serial lines in one thread
Dword winapi SerialPort1ThreadProcess (HWND hWnd // Main Window handle)
{
Char str [101];
DWORD wCount; // number of bytes read
While (1)
{
ReadFile (hCom, str, 100, & wCount, NULL );
If (wCount> 0) // receives data
{
Str [wCount] = '/0 ';
: PostMessage (hWnd, COM_RECVDATA, (unsigned int) str, wCount );
// Send a message to the main window of the dialog box to display the received content
}
}
Return TRUE;
}

To verify the correctness of the program, we use the serial port debugging assistant to work with the program and send and receive each other. The following screenshot shows that the program works correctly and the sending and receiving characters are correct.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.