Advanced C 1992
.pdf
Part II • Managing Data in C
The PC Communications Ports
The serial communications ports are more complex than the printer ports. First, they require more initialization because the speed (baud rate) and the format of the characters (number of bits) must be set. To make things easy, I have DOS initialize (in my AUTOEXEC.BAT file) all the communications ports to a known state.
Following are the addresses used by the PC communications ports. When a communications port is accessed, the first address used is referred to as the I/O base address. The communications port I/O address starts at 0x03F8 or 0x02F8 (COM1 and COM2) in most PCs. All addresses are accessed using input and output functions, either in C or assembler.
In the following discussion, the COM1 base I/O address of 0x3F8 is used. To access COM2, you must use the COM2 base I/O address of 0x2F8. To access a port other than COM1 or COM2, you must know the port’s base I/O address. The addresses for COM1 through COM4 are stored in the BIOS data page as follows:
COM1 0000:0400
COM2 0000:0402
COM3 0000:0404
COM4 0000:0406
The UART (Universal Asynchronous Receiver/Transmitter) is the part of the communications port that converts a byte of data to a serial data stream. The UART in the PC’s communications port has eight separate addresses. The following paragraphs and Table 9.5 describe each of these addresses.
Table 9.5. The serial board’s I/O port usage.
|
|
|
Name in |
|
Name |
Address |
Description |
SENDCOMM.C |
Bits |
|
|
|
|
|
Receive |
I/O base |
Characters received |
RBR_PORT 0–7 |
Eight bits of |
buffer |
address |
may be retrieved |
|
received data |
|
+ 0 |
from this I/O |
|
|
|
|
address. LCR_PORT |
|
|
|
|
bit 80 must be clear. |
|
|
296
|
|
|
|
Disk Files and Other I/O |
C C C |
||
|
|
|
|
|
|
C9C |
|
|
|
|
|
|
|
C C C |
|
|
|
|
|
|
|
C |
|
|
|
|
|
|
|
|
|
|
|
|
|
Name in |
|
|
|
|
Name |
Address |
Description |
SENDCOMM.C |
Bits |
|
|
|
|
|
|
|
|
|
|
|
Divisor |
I/O base |
Used to set the baud |
DLL_PORT 0–7 |
Divisor’s LSB |
|
|
|
register |
address |
rate, which is deter- |
|
(eight bits) |
|
|
|
(LSB) |
+ 0 |
mined using a 16-bit |
|
|
|
|
|
|
|
divisor. |
|
|
|
|
|
|
|
LCR_PORT bit |
|
|
|
|
|
|
|
80 must be set. |
|
|
|
|
|
Transmit |
I/O base |
Characters to be |
THR_PORT 0–7 |
Eight bits of |
|
|
|
buffer |
address |
transmitted are |
|
data to be |
|
|
|
|
+ 0 |
output to this I/O |
|
transmitted |
|
|
|
|
|
address. LCR_PORT |
|
|
|
|
|
|
|
bit 80 must be clear. |
|
|
|
|
|
Interrupt |
I/O base |
Enables the con- |
IER_PORT 01 |
Received data |
|
|
|
enable |
address |
ditions that cause the |
|
available. A |
|
|
|
register |
+ 1 |
UART to generate an |
|
character has |
|
|
|
|
|
interrupt to be gener- |
|
been received. |
|
|
|
|
|
ated. When a specified |
|
|
|
|
|
|
|
bit is set and the con- |
|
|
|
|
|
|
|
dition occurs, an inter- |
|
|
|
|
|
|
|
rupt is generated. |
|
|
|
|
|
|
|
|
02 |
Transmit |
|
|
|
|
|
|
|
holding register |
||
|
|
|
|
|
is empty. The |
|
|
|
|
|
|
|
character that |
|
|
|
|
|
|
|
was being sent |
|
|
|
|
|
|
|
has completed. |
||
|
|
|
|
|
A new character |
||
|
|
|
|
|
can now be |
|
|
|
|
|
|
|
sent. |
|
|
|
|
|
|
04 |
Receive line status. |
||
|
|
|
|
|
The receive line has |
||
|
|
|
|
|
changed. |
|
|
|
|
|
|
08 |
MODEM status. |
||
|
|
|
|
|
The modem status |
||
|
|
|
|
|
line has changed. |
||
|
|
|
|
10 |
Not used |
|
|
continues
297
Part II • Managing Data in C
Table 9.5. continued
|
|
|
Name in |
|
Name |
Address |
Description |
SENDCOMM.C |
Bits |
|
|
|
|
|
|
|
|
20 |
Not used |
|
|
|
40 |
Not used |
|
|
|
80 |
Not used |
Divisor |
I/O base |
Used to set the baud |
DLM_PORT 0–7 |
Divisor’s MSB |
register |
address |
rate, which is deter- |
|
(eight bits) |
(MSB) |
+ 1 |
mined using a 16-bit |
|
|
|
|
divisor. LCR_PORT |
|
|
|
|
bit 80 must be set. |
|
|
Interrupt |
I/O base |
Tells the program |
IIR_PORT 01 |
If clear, an |
identifier |
address |
what condition caused |
|
interrupt is |
register |
+ 2 |
the interrupt. |
|
pending |
|
|
|
02 |
Interrupt ID, bit 0 |
|
|
|
04 |
Interrupt ID, bit 1 |
|
|
|
08 |
Not used |
|
|
|
10 |
Not used |
|
|
|
20 |
Not used |
|
|
|
40 |
Not used |
|
|
|
80 |
Not used |
Line |
I/O base |
Controls the format |
LCR_PORT 01 |
Word length, |
control |
address |
of the characters be- |
|
bit 0 |
register |
+ 3 |
ing transmitted, in- |
|
|
|
|
cluding the number |
|
|
|
|
of bits, the number of |
|
|
|
|
stop bits, and the |
|
|
|
|
parity. The divisor |
|
|
|
|
latch (see the divi- |
|
|
|
|
sor register) is also |
|
|
|
|
located at this |
|
|
|
|
address, bit 80. |
|
|
|
|
|
02 |
Word length, bit 1 |
|
|
|
04 |
Stop bits (clear = 1, |
|
|
|
|
set = 2) |
|
|
|
08 |
Parity enable |
298
|
|
|
|
Disk Files and Other I/O |
C C C |
||
|
|
|
|
|
|
C9C |
|
|
|
|
|
|
|
C C C |
|
|
|
|
|
|
|
C |
|
|
|
|
|
|
|
|
|
|
|
|
|
Name in |
|
|
|
|
Name |
Address |
Description |
SENDCOMM.C |
Bits |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
Even parity |
|
|
|
|
|
|
20 |
Stick parity |
|
|
|
|
|
|
40 |
Set break |
|
|
|
|
|
|
80 |
Enable divisor |
|
|
|
|
|
|
|
latches |
|
|
|
MODEM |
I/O base |
Sets the output control |
MCR_PORT 01 |
DTR |
|
|
|
control |
address |
lines. |
02 |
RTS |
|
|
|
register |
+ 4 |
|
04 |
OUT1 |
|
|
|
|
|
|
08 |
OUT2 |
|
|
|
|
|
|
10 |
Loop back |
|
|
|
|
|
|
20 |
Not used |
|
|
|
|
|
|
40 |
Not used |
|
|
|
|
|
|
80 |
Not used |
|
|
|
Line status |
I/O base |
Status of various |
LSR_PORT 01 |
Data has been |
|
|
|
register |
address + 5 |
parameters. |
|
received. |
|
|
|
|
|
|
02 |
Overrun error. The |
||
|
|
|
|
|
previous character |
||
|
|
|
|
|
received was not |
||
|
|
|
|
|
read by the |
|
|
|
|
|
|
|
computer and has |
||
|
|
|
|
|
been overwritten |
||
|
|
|
|
|
by the following |
||
|
|
|
|
|
character. |
|
|
|
|
|
|
04 |
Parity error. There |
||
|
|
|
|
|
was a parity error |
||
|
|
|
|
|
in the character |
||
|
|
|
|
|
being received. |
||
|
|
|
|
08 |
Framing error. The |
||
|
|
|
|
|
start/stop bits could |
||
|
|
|
|
|
not be detected |
||
|
|
|
|
|
correctly. |
|
|
|
|
|
|
10 |
Break interrupt. A |
||
|
|
|
|
|
break has been |
|
|
|
|
|
|
|
detected. |
|
|
|
|
|
|
20 |
Transmit buffer |
||
|
|
|
|
|
empty |
|
|
continues
299
Part II • Managing Data in C
Table 9.5. continued
|
|
|
Name in |
|
Name |
Address |
Description |
SENDCOMM.C |
Bits |
|
|
|
|
|
|
|
|
40 |
Transmit shift |
|
|
|
|
register is empty. |
|
|
|
|
The output shift |
|
|
|
|
register is currently |
|
|
|
|
empty. |
|
|
|
80 |
Not used |
MODEM |
I/O base |
Status of MODEM |
MSR_PORT 01 |
DCTS |
status |
address + 6 |
signal lines. |
02 |
DDSR |
register |
|
|
04 |
TERI |
|
|
|
08 |
DDCD |
|
|
|
10 |
CTS |
|
|
|
20 |
DSR |
|
|
|
40 |
RI (ring indicator) |
|
|
|
80 |
DCD |
|
|
|
|
|
The receive buffer is located at base address + 0. The functionality of base address + 0 is controlled by bit 0x80 of LCR_PORT. If this bit is clear (zero), base address + 0 is used as the receive buffer.
The baud rate divisor register (LSB) is also located at base address + 0. The functionality of base address + 0 is controlled by bit 0x80 of LCR_PORT. If this bit is set, base address + 0 is used as the divisor register (LSB).
The transmit buffer is located at base address + 0. The functionality of base address + 1 is controlled by bit 0x80 of LCR_PORT. If this bit is clear (zero), base address + 1 is used as the interrupt enable register.
The baud rate divisor register (MSB) is also located at base address + 1. The functionality of base address + 1 is controlled by bit 0x80 of LCR_PORT. If this bit is set, base address + 1 is used as the divisor register (MSB).
The interrupt enable register is located at base address + 1. Only the first four bits are used in this register.
The interrupt identifier register is located at base address + 2. It is used to tell the program what condition caused the interrupt.
300
Disk Files and Other I/O |
C C C |
|
C9C |
|
C C C |
|
C |
The line control register is located at base address + 3. This register controls the character format and the mapping of the divisor latch (see offset +0 and +1).
The modem control register is located at base address + 4. This register is used to control the port’s I/O connector control signals. These signals are then used to control the modem (or whatever else is connected to the port).
The line status register is located at base address + 5. It is used to pass to the program information regarding the status of the UART and the data being received.
The MODEM status register is located at base address + 6. The program uses this register to determine the status of the device connected to the port.
Note how the DLL and DLM registers (the speed registers) have the same address as the RBR and IER registers. This is accomplished with the divisor latch enable bit (0x80) of the LCR register. If this bit is set, these registers are used for the speed divisor. If this bit is cleared, these registers serve their other purpose.
A communications program can rely on the DOS MODE command setting the communications port parameters, or the program can set the parameters itself. Each of the port’s registers may be read (except transmitted data), modified, and written back. This process of reading, modifying, and writing a register is all that is required to initialize the serial port.
Listing 9.8, SENDCOMM.C, sends a single string, followed by a LF/CR character pair, to COM1:. Before you run SENDCOMM.C, COM1: must be initialized with the DOS MODE command.
Listing 9.8. SENDCOMM.C.
/* SENDCOMM, written 1992 by Peter D. Hipson
*This program outputs to the serial port. You should
*run this program under DOS on a PC. If your computer
*is not a PC-compatible, DO NOT RUN this program. Also,
*the program should be compiled with Microsoft C.
*/ |
|
|
#include <stdio.h> |
// Make includes first part of file |
|
#include <conio.h> |
// |
Console I/O functions |
#include <string.h> |
// |
For string functions |
continues
301
Part II • Managing Data in C
Listing 9.8. continued
#include <stdlib.h> |
// Standard include items |
||
#include |
<process.h> |
// |
For exit(), etc. |
#include |
<time.h> |
// |
To seed random numbers |
#define MAKELONG(low, high) ((long)(((unsigned short int)(low)) \ |
|
|||
| (((unsigned long int)((unsigned short int)(high))) << 16))) |
|
|||
#define MAKELP(sel, off) |
((void _far*)MAKELONG((off), (sel))) |
|
||
/* Comm port definitions */ |
|
|
|
|
#define BIOS_DATA_PAGE |
|
0x0040 |
|
|
#define COM1 |
|
0x0000 |
|
|
/* Receive a character port (read only) */ |
|
|||
#define RBR_PORT |
|
(nPort) |
|
|
/* Send (transmit) a character port (write only) */ |
|
|||
#define THR_PORT |
|
(nPort) |
|
|
/* Interrupt enable register */ |
|
|
|
|
#define IER_PORT |
|
(nPort + 1) |
|
|
#define RECEIVED_DATA_AVAILABLE |
0x01 |
|
|
|
#define TRANSMIT_HOLD_EMPTY |
|
0x02 |
|
|
#define RECIEVER_LINE_STATUS |
|
0x04 |
|
|
#define MODEM_STATUS |
|
0x08 |
|
|
/* Other bits undefined */ |
|
|
|
|
/* Interrupt identify register (read only) */ |
|
|||
#define IIR_PORT |
|
(nPort + 2) |
|
|
#define INTERUPT_PENDING_0 |
|
0x01 |
|
|
#define INTERUPT_ID_BIT_1 |
|
0x02 |
|
|
#define INTERUPT_ID_BIT_2 |
|
0x04 |
|
|
/* Other bits undefined */ |
|
|
|
|
/* Line control register */ |
|
|
|
|
#define LCR_PORT |
|
(nPort + 3) |
|
|
#define WORD_LENGTH_SELECT_1 |
|
0x01 |
/* 00 = 5 bits, 01 = 6 bits */ |
|
#define WORD_LENGTH_SELECT_2 |
|
0x02 |
/* 10 = 7 bits, 11 = 8 bits |
*/ |
#define NUMBER_STOP_BITS |
|
0x04 |
/* 0 = 1 stop, 1 = 2 stop |
*/ |
#define PARITY_ENABLE |
|
0x08 |
|
|
#define EVEN_PARITY_SELECT |
|
0x10 |
|
|
302
Disk Files and Other I/O
#define STICK_PARITY |
0x20 |
|
#define SET_BREAK |
0x40 |
|
#define DIVISOR_LATCH_BIT |
0x80 |
/* For DLL and DLH access */ |
/* Other bits undefined */ |
|
|
/* Modem control register */ |
|
|
#define MCR_PORT |
(nPort + 4) |
|
#define DTR |
0x01 |
|
#define RTS |
0x02 |
|
#define OUT_1 |
0x04 |
|
#define OUT_2 |
0x08 |
|
#define LOOP |
0x10 |
|
/* Other bits undefined */ |
|
|
/* Line status register */ |
|
|
#define LSR_PORT |
(nPort + 5) |
|
#define DATA_READY |
0x01 |
|
#define OVERRUN_ERROR |
0x02 |
|
#define PARITY_ERROR |
0x04 |
|
#define FRAMING_ERROR |
0x08 |
|
#define BREAK_INTERUPT |
0x10 |
|
#define TRANS_HOLDING_REGISTER |
0x20 |
|
#define TRANS_SHIFT_REGISTER |
0x40 |
|
/* Other bits undefined */ |
|
|
/* Modem status register */ |
|
|
#define MSR_PORT |
(nPort + 6) |
|
#define DCTS |
0x01 |
|
#define DDSR |
0x02 |
|
#define TERI |
0x04 |
|
#define DDCD |
0x08 |
|
#define CTS |
0x10 |
|
#define DSR |
0x20 |
|
#define RI |
0x40 |
|
#define DCD |
0x80 |
|
C C C
C9C C
C C C
/* Divisor latch least significant (sets speed) */ #define DLL_PORT (nPort + 0)
/* Bits 0 - 7 */
continues
303
Part II • Managing Data in C
Listing 9.8. continued
/* Divisor latch most significant (sets speed) */ #define DLM_PORT (nPort + 1)
/* Bits 8 - 15 */
#define BAUD_50 |
0x0900 |
#define BAUD_75 |
0x0600 |
#define BAUD_110 |
0x0417 |
#define BAUD_134 |
0x0359 |
#define BAUD_150 |
0x0300 |
#define BAUD_300 |
0x0180 |
#define BAUD_600 |
0x00C0 |
#define BAUD_1200 |
0x0060 |
#define BAUD_1800 |
0x0040 |
#define BAUD_2000 |
0x003A |
#define BAUD_2400 |
0x0030 |
#define BAUD_3600 |
0x0020 |
#define BAUD_4800 |
0x0018 |
#define BAUD_7200 |
0x0010 |
#define BAUD_9600 |
0x000C |
#define BAUD_14400 |
0x0008 |
#define BAUD_19200 |
0x0006 |
#define BAUD_38400 |
0x0003 |
#define BAUD_56000 |
0x0002 |
#define BAUD_112000 |
0x0001 |
/* End serial port definitions */
int main( |
// Define main() and the fact that this |
int |
argc, // program uses the passed parameters |
char |
*argv[], |
char |
*envp[] |
); |
|
int SerialCharacter(char chChar);
int |
SerialStatus(void); |
|
int |
main( |
|
|
int |
argc, |
|
char |
*argv[], |
304
Disk Files and Other I/O
char |
*envp[] |
) |
|
{ |
|
char |
szNowIsTheTime[] = { |
“Now is the time for all good men to come to the aid...\n\r”}; |
|
int |
nStatus; |
int |
i; |
if (!SerialStatus())
{
printf(“There was a serial error!\n”); exit(4);
}
for (i = 0; i < strlen(szNowIsTheTime); i++)
{
if (SerialCharacter(szNowIsTheTime[i]) == 0)
{
printf(“\nCouldn’t send from character ‘%s’\n”, &szNowIsTheTime[i]);
|
break; |
|
} |
} |
|
return (0); |
|
} |
|
int |
SerialCharacter( |
char |
chChar) |
{ |
|
int _far |
*pSerialPort; |
int |
nPort; |
C C C
C9C C
C C C
continues
305
