Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced C 1992

.pdf
Скачиваний:
92
Добавлен:
17.08.2013
Размер:
4.28 Mб
Скачать

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