In the development of Windows applications, we often need to face problems with peripheral data source devices. Both computer and microcontrollers (such as MCS-51) have serial communication ports to design corresponding serial communication programs to complete data communication tasks between them.
It is very much when using serial ports in actual work. There are some articles to introduce serial programming published on computer magazines. But the total feelings are not comprehensive, especially the 32-bit programming, and it is very unharequentiful. The author has accumulated more experience in practical work, combined with hardware, software, and focusing on comparing new technologies, and needs to pay attention to the point of view. I hope to have some help to friends who need to write a serial communication program.
One. Basic principle of serial communication
The essential function of the serial port is an encoded converter between the CPU and the serial device. When the data is sent from the CPU through the serial port, byte data is converted to a serial bit. When receiving data, the serial bit is converted to byte data.
Under the Windows environment (Windows NT, Win98, Windows 2000), serial port is part of the system resource.
The application is to communicate using the serial port. The resource application requirements (open serial port) must be filed to the operating system before use, and the resources must be released after the communication is complete.
The process of serial communication program is as follows:
two. Serial port signal line connection
A complete RS-232C interface has 22 lines, using standard 25-core socket (or 9-core plug). The 25 core and the main signal line of the 9th core are the same. The following introduction is as an example of 25 core RS-232C.
1 Main signal line definition:
2 foot: send data TXD; 3 foot: receive data RXD; 4 foot: request to send RTS; 5 foot: Clear Send CTS;
6-foot: Data Equipment Ready DSR; 20 Heels: Data Terminal Ready DTR; 8 Heart: Data Serial Detection DCD;
1 foot: Protection; 7 foot: Signal ground.
2 Electrical characteristics:
The data transmission rate is up to 20K BPS, and the maximum distance is only 15m.
Note: After watching Microsoft's MSDN 6.0, its Windows API is set to serial communication devices (not necessarily serial port RS-232C or RS-422 or RS-449), the maximum support to RS_256000, 256K bps! I don't know what serial communication equipment is it? However, in any case, the general host and the single-chip serial communication are mostly at 9600 bps, which can meet the communication needs.
3 Typical application of the interface:
Most computer application systems and intelligent units can work with 3 to 5 signal lines. At this time, in addition to TXD, RXD, RTS, CTS, DCD, DTR, DSR, etc. are required. (Of course, the corresponding signal line needs to be set in the program.)
The above, when designing the program, the reception and transmission of the data directly can be judged or set without determining the status of the signal line. (If you need to use a handshake signal, you need to use a handshake signal, etc., you need to monitor or set the status of the corresponding signal line.)
three. Simple review of 16-bit serial application applications
16-bit serial application, use 16-bit Windows API communication function:
1 OpenComm () opens the serial resource and specifies the input, and outputs the size of the buffer (in bytes);
CloseComm () Close the serial port;
Example: int idcomdev;
IDCOMDEV = OpenCommmm ("COM1", 1024, 128); CloseComm (IDCOMDEV);
2 BuildCommdcb (), setcommState () fill in the device control block DCB, and then the open serial port is configured;
Example: DCB DCB;
BuildCommdcb ("COM1: 2400, N, 8, 1", & DCB);
SetCommstate (& DCB);
3 Readcomm, WriteComm () reads and writes the serial port, that is, the data is received and transmitted.
Example: char * m_precieve; int count;
Readcomm (IDCOMDEV, M_PRECIEVE, COUNT);
CHAR WR [30]; int count2;
WriteComm (IDCOMDEV, WR, Count2);
The maximum feature of the serial port communication program under the 16-bit is that the operation of external devices such as serial port has its own unique API function; and 32-bit programs put serial operation (and parallel port, etc.) and file operations, using similar operations .
four. 32-bit serial application under MFC
32-bit serial port communication programs can be implemented in two ways: utilizing ActiveX controls; using API communication functions.
Using the ActiveX control, the program is very simple, the structure is clear, the disadvantage is that it is indeed inherently; the advantages and disadvantages of the API communication function are basically opposite.
The following describes a program that adds serial communication capabilities in a single document (SDI) application.
(I) Using the ActiveX control:
The MSCOMM control provided by VC 6.0 transmits and receives data via a serial port, providing a serial communication function for the application. It is very convenient to use, but unfortunately, there are very few information about the MSCOMM control.
(1). Insert the MSCOMM control in the current Workspace.
Project Menu ------> Add to Project ----> Components and Controls -----> Registered
ActiveX Controls ---> Select Components: Microsoft Communications Control,
Version 6.0 is inserted into the current Workspace.
The result is added CMSCOMM (and corresponding files: MSCOMM.H and MSComm.cpp).
(2). Add MSCOMM controls in Mainfrm.h.
protected:
CMSCOMM M_COMPORT;
In mainfrm.cpp :: oncreare ():
DWORD style = ws_visible | WS_CHILD;
IF (! m_comport.create (null, style, crect (0, 0), this, id_commctrl) {
Trace0 ("Failed to Create Ole Communications Control);
Return -1; // fail to create
}
(3). Initializing the serial port
m_comport.setcommport (1); // Select COM?
m_comport. setinbuffersize (1024); // Set the size of the input buffer, bytes
m_comport. setoutBuffersize (512); // Set the size of the input buffer, bytes //
IF (! m_comport.getportopen ()) // Open the serial port
m_comport.setportopen (TRUE);
m_comport.setInputMode (1); // Setting the input mode is binary
m_comport.setSettings ("9600, N, 8, 1"); // Setting the baud rate and other parameters
m_comport.serthreshold (1); / / 1 indicates a character to trigger an event
m_comport.setInputlen (0);
. Capture the serial entity. The MSCOMM control can obtain data from the port with polling or event-driven methods. We introduce the comparison Event Drive Method: The program is notified when there is an event (such as receiving data). These communication events are required and processed in the program.
In Mainfrm.h:
protected:
AFX_MSG void oncommmsComm ();
Declare_eventsink_map ()
In mainfrm.cpp:
Begin_Eventsink_Map (CMAINFRAME, CFRAMEWND)
ON_EVENT (CMAINFRAME, ID_COMMCTRL, 1, ONCOMMMSCOMM, VTS_NONE)
/ / Map ActiveX Control Event
END_EVENTSINK_MAP ()
. The serial port is read or written. The function of the read and written function is indeed very simple, getInput () and setOutput () can. The prototype of the two functions is:
Variant getInput (); and void setput (const variant & newvalue); use the Variant type (all IDispatch :: invoke parameters and return values are processed as Variant objects).
Whether in the PC read data or when the PC is sent to the downlink command, we are used to using the form of a string (or as an array form). Check out the Variant documentation, you can use BSTR to represent a string, but unfortunately all BSTR is included in the wide character, even if we don't define _unicode_unicode is also true! Winnt supports wide characters, while Win95 does not support. In order to solve the above problem, we use CBYTEARRAY in actual work to give the appropriate part programs as follows:
Void CMAINFRAME :: OnCommmscomm () {
Variant Vresponse; INTK;
IF (m_commctrl.getcommmevent () == 2) {
K = m_commctrl.getinbuffercount (); // Number of characters received
IF (k> 0) {
Vresponse = m_commctrl.getinput (); // r
Savedata (K, (unsigned char *) VRESPONSE.PARRAY-> PVDATA);
} // Receive the character, MSCOMM control send event}
. . . . . // Handle other MSCOMM controls
}
Void CMAINFRAME :: oncommsend () {
. . . . . . . . // Prepare the command to send, put it in TXData []
CbyteArray array;
Array.removeall ();
Array.setsize (count);
For (i = 0; i
Array.Setat (i, txdata [i]);
m_comport.setOutput (Colevariant (array)); // Send data
}
Please pay attention to the contents of the first, ⑸, focus on actual work, difficult to point.
(Ii) Use 32-bit API communication functions:
Many friends will feel weird: write a serial communication program with a 32-bit API function, isn't it to change the 16-bit API to 32? The 16-bit serial port communication program can be discounted many people many years ago ...
This article mainly wants to introduce how to combine non-blocking communication, multi-threading, etc. in API serial communication, write high quality communication programs. In particular, when the CPU processing task is more heavy, there is a large amount of communication data in the peripheral device, more practical. (1). Define global variables in Chinafrm.cpp
Handle hcom; // Prepare the handle of the serial port
Handle hcommwatchthread; // auxiliary thread global function
(2). Open the serial port and set the serial port
HCOM = CREATEFILE ("COM2", Generic_Read | generic_write, // Allowed to write
0, // This item must be 0
Null, // no security attnce
Open_existing, // Setting the generation
FILE_FLAG_OVERLAPPED, // We are ready to use asynchronous communication
NULL);
Please note that we use the file_flag_overlapped structure. This is the key to using the API to achieve non-blocking communications.
AskERT (hcom! = Invalid_handle_value); // Detect whether to open the serial port operation is successful
Setcommmask (hcom, ev_rxchar | ev_txempty); // Set the type of event-driven
SetupComm (hcom, 1024, 512); // Setting the input, the size of the output buffer
PurgeComm (hcom, purge_txabort | purge_rxabort | purge_txclear
| Purge_rxclear); // Clean net input, output buffer
CommTIMEOUTS COMMTIMEOUTS; / / Define the timeout structure and fill in the structure
..........
Setcommtimeouts (hcom, & commtimeouts); // Setting the timeout allowed by reading and writing operations
DCB DCB; / / Define Data Control Block Structure
Getcommstate (HCOM, & DCB); // Read Serial Port The original parameter setting
Dcb.baudrate = 9600; dcb.bytesize = 8; dcb.parity = noparity;
DCB.Stopbits = onestopbit; dcb.fbinary = true; dcb.fparity = false;
SetCommState (hcom, & dcb); // Serial port parameter configuration
All of the above COMMTIMEOUTS structures and DCB are important, and the parameters need to be carefully selected in actual work.
(3) Start an auxiliary thread for processing of serial events.
Windows offers two threads, auxiliary threads, and user interface threads. The difference is that the auxiliary thread has no windows, so it does not have its own message loop. But the auxiliary thread is easy to program, and is usually very useful.
At times, we use the auxiliary thread. It mainly uses it to monitor the serial port state, see if there is no number, there is no error; and the main thread can focus on data processing, providing a friendly user interface and other important work.
hcommwatchthread =
CreateThread ((lpsecurity_attributes) null, // security properties
0, // The size of the initial line stack, the default is the same as the main thread size
(LPTHREAD_START_ROUTINE) CommWatchProc, // Thread global function
Getsafehwnd (), // Incorporate the handle of the main frame
0, & DWTHREADID;
ASSERT (hcommwatchthread! = Null);
⑷ For the auxiliary thread, write a full-time function, mainly completing the work of data reception. Note how the Overlapped structure is used, and how to achieve non-blocking communication.
Uint CommwatchProc (HWnd HsendWnd) {dWord dwevtmask = 0;
Setcommmask (hcom, ev_rxchar | ev_txempty); // What are the serial events need to be monitored?
Waitcommmevent (hcom, & dwew, os); // Waiting for the occurrence of serial communication events
Detect the returned DWEVTMASK, know what the serial event happened:
IF (DWEVTMASK & EV_RXCHAR) == ev_rxchar) {// buffer has data arrival
COMSTAT COMSTAT; DWORD DWLENGTH;
Clearcommerror (HCOM, & DWERRORFLAGS, & Comstat);
DWLENGTH = Comstat.cbinque; // How much data is input to the buffer?
IF (DWLENGTH> 0) {
Bool freadstat;
FreadStat = Readfile (HCOM, LPBUFFER, DWLENGTH, & DWBYTESREAD)
& Read_OS (nPTTYINFO)); // read data
Note: We use file_flag_overlapped when crearefile (), now readfile () must also use
LPOVERLAPPED structure. Otherwise, the function will not report the read operation correctly.
Using the lpoverlapped structure, readfile () immediately returns, do not have to wait for the reading operation to achieve non-blocking
Communication. At this point, readFile () returns false, and getLastError () returns ERROR_IO_PENDING.
IF (!freadstat) {
IF (getLastError () == Error_io_pending) {
While (! GetoverlappedResult (hcom,
& Read_OS (nPTTYINFO), & DWBYTESREAD, TRUE) {
Dwerror = getLastError ();
IF (dwerror == error_io_incumplete) Continue;
// Buffer data is not finished, continue
...... ...
:: PostMessage ((hwnd) hsendwnd, wm_notifyprocess, 0,0); // Notify the main thread, the serial port receives the data}
The so-called non-blocking communication is also asynchronous communication. When the data read and write operation that needs to spend a lot of time (not only the serial communication operation), the readFile (), WriteFile () can be called immediately, and the actual read and write operation is running in the background. Conversely, if using blocking communication, you must return after the read or write operation is completed. Since the operation may require any long time to complete, then the problem will appear.
Very obstruction operation also allows reading, write operations can be carried out at the same time (ie overlapping operations?) Is very useful in actual work.
To use non-blocking communication, you must use file_flag_overlapped when CreateFile (); then at ReadFile (), the lpoverlapped parameter must not be NULL, then check the return value of the function call, call getLastError (), see if returning error_io_pending. For example, finally call GetOverlappedResult () to return the result of overlapped operation; WriteFile () is similar.
. Send downlink commands in the main thread.
Bool fwritestat; char szbuffer [count]; ............ // Prepare the data sent, placed in szbuffer []
FWRITESTAT = Writefile (HCOM, SZBuffer, Dwbytestowrite,
& dwbyteswritten, & write_os (npttyinfo)); // Write data
Note: We use file_flag_overlapped when CreareFile (), and now WriteFile () must also use the LPOVERLAPPED structure. Otherwise, the function will not correct the write operation has been completed.
Using the LPOVERLAPPED structure, WriteFile () is returned immediately, do not wait for the write operation to complete, implement non-blocking communication. At this point, WriteFile () returns false, and getLastError () returns ERROR_IO_PENDING.
Int err = getLastError ();
IF (! fwritestat) {
IF (getLastError () == Error_io_pending) {
While (! GetoverlappedResult (hcom, & write_os (npttyinfo),
& dwbyteswritten, true)) {
Dwerror = getLastError ();
IF (dwerror == error_io_incumplete) {
// Normal Result if not finished
DWBYTESSENT = dwbyteswritten; contact;}
....................
In summary, we use multi-threaded technology to monitor serial ports in the auxiliary thread. If you have data arrival, you can rely on event driver, read data and report to the main thread (send data in the main thread, relatively, the data of the downlink command It is much less); and Waitcommevent (), readfile (), writefile () uses non-blocking communication technologies, relying on overlapped read and write operations, so that the serial port read and write operation runs in the background.
Relying on VC6.0 rich features, combining the technologies we mentioned, a serial communication application with powerful control capabilities is written. In terms of personal concern, I prefer API technology because the control means is much flexible, and the function is much more powerful.