As mentioned earlier, the simplest serial port program is a loopback testing program. The term loopback indicates that the transmitted data (TD) pins and the received data (RD) pins in an RS-232 serial connector are connected to form a loop between the transmitting and receiving terminals. (Refer to Section 1.10.1 and Figure 1.8 in Chapter 1 for more detailed information on obtaining a loopback-compatible serial port connector.)
FIGURE 2.1 The different operation modes.
Real Mode Protected Mode
Windows 95 Windows 95 Windows 98 Windows 98 Windows ME Windows ME Windows NT Windows 2000 Windows XP ANSI C & Assembly Programming Win32 API & Visual C++ Programming
92 The Windows Serial Port Programming Handbook
2.2.1 A LOOPBACK TESTING PROGRAM DEVELOPED IN C
All programs developed in this chapter are compiled and built with Microsoft Visual C++ 6.0. To develop an MS-DOS-compatible program in the Windows 95/98/ME environment, it is necessary to use the console project format. Because of the simplicity of the following program, flow control and handshaking signals are not necessary. The only registers that must be accessed in this testing program are the transmitter holding register (THR) and the receiver holding register (RHR) in the 8250 or 16550 UART. To make this program as generic as possible, COM1 will be used as the testing port. The THR and the RHR will have the same address, 03F8H, which is the base address of COM1.
2.2.1.1 The _outp() and _inp() Functions
The key to testing a serial port using a console program developed in ANSI C is to directly communicate with the UART using two ANSI C functions, _outp() and _inp(), respectively. The _outp() function is used to send a byte to the THR, and the _inp() function is used to receive a byte from the RHR. The protocols of these two functions are shown in Figure 2.2.
Both functions return an integer value. The _outp() function returns the integer that is sent out to the THR, and the _inp() function returns the data received from the RHR. The _outp() function has two arguments. The first argument is the port number, which is an unsigned short data type, and the second is the data value to be sent out. The _inp() function has only one argument, port, which is the port number from which the data will be retrieved.
Both of these functions are defined in the libc.lib system library in Visual C++ 6.0, and the system header file <conio.h>should be included when using these two functions.
2.2.1.2 The Detailed Program Code
The purpose of this loopback testing program is to repeatedly send a sequence of integers to the THR and, after a period of time, to pick up that integer sequence from the RHR. For testing purposes, the sequence of integers is selected from one to ten. The testing will be successful if the transmitted data matches the received data.
Now, let’s begin to develop the loopback testing program. Launch Visual C++ 6.0 and open a new project with the following properties:
• Project type: Win32 Console Application • Project name: DirectCSPort
• Project location: C:\Chapter 2
TIP: The project location is a folder where you want the project to be stored on your computer. Here we have used C:\Chapter 2, but you can select any valid folder on your computer as the location for storing your project.
In the open project, create a new C++ source file named DirectCSPort.cpp. (Due to its simplicity, this project needs no header file.)
FIGURE 2.2 The protocols of _outp() and _inp() functions.
int _outp( unsigned short port, int databyte );
Serial Port Programming for MS-DOS in ANSI C and Assembly Languages 93
Enter the code shown in Figure 2.3 into the DirectCSPort.cpp file.
The purpose of each line of code is described here:
A: Useful header filesare declared in this section. These header files contain the protocol definitions of the system functions. Among them, two header files are important. One is stdio.h, which provides the protocols for all standard I/O functions, such as printf() and scanf(), that are used in the system. The other is conio.h, which provides the protocols for the _outp() and _inp() functions.
B: Useful global constants are defined here. You can declare these constants as any data type by using the macro #define and by placing the associated cast operator in front of each constant. For example, COM1 is defined as 03F8 preceded by an unsigned short casting operator, and MAX is defined as the maximum length of the delay. Once you declare the constants, they will be automatically translated to the associated data values when used in the program. For instance, COM1 in the program is equivalent to a data value of 03F8.
C: Two user-defined functions are declared in this section. The TestSerial() function is used to call two I/O functions, _outp() and _inp(), to test the serial port COM1. The delay() function is used to delay the program until the data is stable on the THR. This function uses one argument, an integer representing the length of the delay interval. FIGURE 2.3 The main program of The Loopback test.
/************************************************************************************************************************ * NAME: DirectCSPort.cpp
* DATE: July 1, 2003 * PGMR: Y. Bai
* DESC: Serial communication test program using C code directly access the serial port. ************************************************************************************************************************/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <conio.h> #include <ctype.h> #include <math.h> #include <dos.h>
#define MAX 5000 // max length of delay period of time
#define COM1 (unsigned short)0x3F8 // serial port COM1
#define COM2 (unsigned short)0x2F8 // serial port COM2
int TestSerial();
void Delay(int num);
void main(void) { int rc = 0; printf("\n"); rc = TestSerial(); if (rc != 0)
printf("Error in TestSerial()!\n"); else
printf("Success in TestSerial()!\n"); return; } A B C D E
94 The Windows Serial Port Programming Handbook
D: In the body of the main function, the TestSerial() function is called to perform the serial port testing.
E: A return value, rc, is used to check the feedback of TestSerial(). A return value of 0 means the function call has been successful. If the return value is not 0, an error has occurred during the function execution, and the error source will be printed on the screen.
Figure 2.4 shows the complete code of the user-defined TestSerial() function.
F: Local variables are declared here. The variable index is used as a cycle variable in the for loop, and value is used to pick up the received data from the function call _inp(). result is a feedback variable that is returned to the main function to indicate whether or not the _inp() function has been successful.
G: A for loop is executed to obtain a sequence of integers and to send each integer to the COM1 port using the _outp() system function.
H: After each integer has been sent out, the delay() function is called to wait for a specified period of time. This time delay is necessary for stabilizing the data sent to the THR. After the delay, and once the data received in the RHR is stable, the _inp() function is called to pick up the received data. To display the testing result to the user in real time, the transmitted and received data is printed on the screen for each loop. The delay() function is shown in Figure 2.5.
This is not a standard time delay function; the standard time delay function would use the system clock as the timing standard and compare the current time with the timing standard to calculate the difference, or the elapsed time. The elapsed time would then be compared with the FIGURE 2.4 The function body of TestSerial().
/*************************************************************************** * Subroutine to loop testing for the RS-232C port. * ***************************************************************************/
int TestSerial() {
int index, value, result = 0;
printf("Begin to execute outp() & inp()...\n"); for (index = 0; index < 10; index++)
{
printf("Data sent to COM1 is: %d\n", index); _outp(COM1, index);
Delay(500);
value = _inp(COM1);
printf("Data returned from COM1 is: %d\n", value); printf("\n");
if (value != index) {
printf("Error in loop testing of the COM1!\n"); result = -1; return result; } } return result; } F G H i J
desired time period to determine whether the desired interval had been exceeded. However, for simplicity here, we’ll use a nonstandard delay function.
I: To make sure that the transmitted data is identical to the received data, a comparison between them is performed. If the two are identical, the testing for the data transmission has been successful, and the program continues to test the next data. Otherwise, an error occurs and the error source is printed out. A value of —1, which indicates an error, is returned to the main function, and the program is terminated.
J: Finally, if no error has been encountered during the loop, a value of 0 is returned to the main function to indicate that the function call has been successful. In other words, the port testing process has worked successfully.
Compile and build the program, and then run it if no error is encountered. The results (if no errors occur) are shown in Figure 2.6.
Notice that in Figure 2.6 the transmitted and received data are identical, which means that the port testing has been successful.
You can modify the size of the sequence of the integers, by increasing the size to 1000 or greater. You can also define the maximum testing integer as a constant in the program. Then, each time you want to modify the size of the integer sequence, you need only modify that maximum number; you will not need to change any code in the program. You can find this program with the complete source code and executable code, as well as a project description file, in the folder Chapter 2\DirectCSPort, which is located on the CD included with this book.