Serial Ports

The SerialPort class is an abstract subclass of CommPort that provides various methods and constants useful for working with RS-232 serial ports and devices. The main purposes of the class are to allow the programmer to inspect, adjust, and monitor changes in the settings of the serial port. Simple input and output is accomplished with the methods of the superclass, CommPort. SerialPort has a public constructor, but that shouldn be used by applications. Instead, you should call the open( ) method of a CommPortIdentifier that maps to the port you want to communicate with, then cast the result to SerialPort. For example:

CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier("COM2"); if (cpi.getType( ) == CommPortIdentifier.PORT_SERIAL) { try { SerialPort modem = (SerialPort) cpi.open( ); } catch (PortInUseException ex) {} }

Methods in the SerialPort class fall into roughly three categories:

.4.1. Control Functions

At the lowest level, wires are analog, not digital. Issues, like timing, noise, and the fundamentally analog nature of electronics have to be considered. Therefore, theres a host of layered protocols so that the receiving end can recognize when data is being sent, whether the data was received correctly, and more.

Serial communication uses some very basic, simple protocols. Sending between 3 and 15 volts across the serial cable for a number of nanoseconds inversely proportional to the baud rate of the connection is a zero bit. Sending between -3 and -15 volts for the same amount of time is a one bit. (Sending between 3 and -3 volts is a hardware error.)

These bits are grouped into serial data units, SDUs for short. Common SDU lengths are 8 (used for binary data) and 7 (used for basic ASCII text). Most modern devices use eight data bits per SDU. However, some older devices use seven, six, or even five data bits per SDU. Once an SDU is begun, the rest of the SDU follows in close order. However, there may be gaps of indeterminate length between SDUs.

One of the problems faced by asynchronous serial devices is determining SDU boundaries. If a modem receives eight data bits, how is it to tell whether thats an entire SDU or the last four bits of one SDU and the first four bits of another, especially if the connection has some noise and isn particularly reliable? To assist with this, each SDU is preceded by a single start bit thats always 0, and followed by between one and two stop bits. Stop bits last longer than data bits so they can always be identified.

In addition to the data and the start and stop bits, an SDU may have a parity bit. Parity is a very simple error detection scheme that can detect (but not correct) single bit errors in an SDU. There are two basic parity schemes. Even parity adds an extra one bit to the end of the SDU if there are an even number of one bits in the data. Odd parity adds an extra one bit to the end of the SDU if there are an odd number of one bits in the data.[*] No parity simply omits the parity bit. The combination of data bits, parity scheme, and stop bits is abbreviated in forms like 8N1 or 7E1. 8N1 means a connection uses eight data bits, no parity, and one stop bit; 7E1 means seven data bits, even parity, and one stop bit. Virtually all modern systems use 8N1.

[*] There are two more parity schemes you may hear about. Mark parity always adds a one bit for the parity; space parity always adds a zero bit. These convey no useful information and are almost never used.

The baud rate is the number of times per second the state of the communication channel changes. This is not the same as bits per second. Modern modems send multiple bits per baud. Most U.S. phone lines, configured primarily for voice calls, have a maximum baud rate of 3200. Modems that send higher bit rates send multiple bits with each baud. A 28,800 bps modem is a 3200 baud modem with nine states, for example.

The Java Communications API lets you set all of these parameters, including baud rate, data bits, stop bits, and parity. They should all be familiar to anyone whos struggled with modem init strings and terminal software in the bad old days before the Internet separated connectivity from content. Four methods in the SerialPort class return the values of these settings. They are:

public abstract int getBaudRate( ) public abstract int getDataBits( ) public abstract int getStopBits( ) public abstract int getParity( )

A little surprisingly, you can set these values independently. Instead, all four values (baud, data bits, stop bits, and parity) are set at once with the setSerialPortParams( ) method:

public abstract void setSerialPortParams(int baud, int dataBits, int stopBits, int parity) throws UnsupportedCommOperationException

If the requested values are not supported by the driver (e.g., a 240,000 baud connection), an UnsupportedCommOperationException is thrown. Except for the baud rate, these arguments should be one of several mnemonic constants in the SerialPort class:

SerialPort.DATABITS_5 // 5 data bits per byte SerialPort.DATABITS_6 // 6 data bits per byte SerialPort.DATABITS_7 // 7 data bits per byte SerialPort.DATABITS_8 // 8 data bits per byte SerialPort.STOPBITS_1 // 1 stop bit SerialPort.STOPBITS_2 // 2 stop bits SerialPort.STOPBITS_1_5 // 1.5 stop bits[*] SerialPort.PARITY_NONE // no parity SerialPort.PARITY_ODD // odd parity SerialPort.PARITY_EVEN // even parity

.4.2. Flow Control

Serial ports and the devices connected to them need a protocol to determine when the port is sending and the device is receiving, when the device is sending and the port is receiving, and how to switch between the two states. The two most common protocols are XON/XOFF and RTS/CTS. They are not mutually exclusive, though its rare to use both at the same time, and nothing is gained by doing so. XON/XOFF is a software-based protocol; it sends special characters down the communication line to tell the other end when to stop and start sending. RTS/CTS is implemented in hardware and requires a special hardware handshaking cable that supports it. Almost all modern hardware, including all modems faster than 2400 bps, support hardware flow control.

The Java Communications API contains two methods to get and set the flow-control protocol:

public abstract int getFlowControlMode( ) public abstract void setFlowControlMode(int protocol) throws UnsupportedCommOperationException

The int returned by getFlowControlMode( ) and the argument passed to setFlowControlMode( ) should be a bitwise OR of the following constants:

SerialPort.FLOWCONTROL_NONE // no flow control SerialPort.FLOWCONTROL_RTSCTS_IN // RTS/CTS for input SerialPort.FLOWCONTROL_RTSCTS_OUT // RTS/CTS for output SerialPort.FLOWCONTROL_XONXOFF_IN // XON/XOFF for input SerialPort.FLOWCONTROL_XONXOFF_OUT // XON/XOFF for output

To set the flow control of the SerialPort object com1 to RTS/CTS for both input and output, you would write:

com1.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);

.4.3. Control Wires

A serial port sends data one bit at a time, but it actually uses eight wires to do it. One wire is used for sending, one for receiving, and the other six for various control information. One or two more pins are connected to ground. Modern serial ports generally come in a nine-pin configuration that reflects this, though most modems and some PCs and terminals use a 25-pin connector. Table 22-1 shows the "pin-outs" of the standard 9-pin serial port you e likely to find on the back of a PC. Table 22-2 shows the "pin-outs" of the standard 25-pin serial port you e likely to find on a modem.

Table 22-1. 9-Pin serial port pin-outs

Pin

Name

Code

Direction

1

Carrier Detect

CD

Device Computer

2

Receive Data

RD

Device Computer

3

Transmit Data

TD

Computer Device

4

Data Terminal Ready

DTR

Computer Device

5

Signal Ground

GND

6

Data Set Ready

DSR

Device Computer

7

Request To Send

RTS

Computer Device

8

Clear To Send

CTS

Device Computer

9

Ring Indicator

RI

Device Computer

Table 22-2. 25-pin serial port pin-outs

Pin

Name

Code

Direction

1

Chassis ground

2

Transmit Data

TD

Computer Device

3

Receive Data

RD

Device Computer

4

Request To Send

RTS

Computer Device

5

Clear To Send

CTS

Device Computer

6

Data Set Ready

DSR

Device Computer

7

Signal Ground

GND

8

Carrier Detect

CD

Device Computer

20

Data Terminal Ready

DTR

Computer Device

22

Ring Indicator

RI

Device Computer

The 15 extra pins on the 25-pin port are generally not connected to anything; Java does not provide methods for manipulating them even if they are.

On a straight DB-25-to-DB-25 connection, about the simplest connection imaginable, used on some early PCs and Unix workstations, the serial cable that connects the PC to the modem runs wires between the corresponding pins. That is, the CD pin is connected to the CD pin, the TD pin is connected to the TD pin, and so forth. Figure 22-1 shows the connection from a PC DB-25 serial port to a DB-25 modem.

Figure 22-1. PC DB-25 serial port to a DB-25 modem

The computer and the modem communicate with each other by raising or lowering voltages on these lines. Each line is one-way. A device reads from or writes to that line but never both. The computer sends data to the modem across the TD line. The modem sends data to the computer across the RD line. The computer tells the modem its ready to send by raising the voltage on the RTS line. The modem says its OK for the PC to send using the CTS line. The modem indicates to the computer its ready using the DSR line and that its detected a carrier by using the DCD line. If the modem loses the carrier signal (i.e., the phone hangs up), it lowers the voltage on the DCD line. Finally, the computer indicates its ready by raising the voltage on the DTR line.

These cables can get a little more complicated as different kinds of ports get connected. However, the main reason for the complexity is that not all ports put the same pins in the same positions. For example, Figure 22-2 shows a standard DB-9 PC port connected to a standard DB-25 modem port. It looks hairier, but if you look closer, youll see that all that happened was that the pins swapped positions, taking their connections with them. The TD pin is still connected to the TD pin, the RD pin is still connected to the RD pin, and so forth. The only changes are the numbers of the pins and the omission of one ground pin from the DB-9 port.

Figure 22-2. PC DB-9 serial port to a DB-25 modem

A standard modem cable connects the same pin on one end of the wire to the corresponding pin on the other end of the wire (e.g., DTR to DTR), as shown in Figure 22-1 and Figure 22-2. Cables for connecting other kinds of devices often deliberately cross or split wires. For instance, a null modem cable, shown in Figure 22-3 and used for direct connections between PCs, connects the TD pins to the RD pins, the RTS pin to the CTS pin, and the DTR pin to the DCD and DSR pins. This allows two PCs to communicate using a communications program and a direct serial connection without any modem. This is why not all serial cables are created equal, and the cable that works for one device may not work for another.

Figure 22-3. PC null modem cable

Data is sent from computer to device across the TD line and from device to computer across the RD line. You access these lines through the output and input streams returned by CommPorts getOutputStream( ) and getInputStream( ) methods.

You do not directly manipulate these pins. The ground pins only maintain a common reference voltage between the devices. No program ever sends voltage over these lines. This leaves six pins to read or write. These are:

Each of these has an effectively boolean value: true if its showing voltage relative to ground or false if it isn . The SerialPort class provides methods to read the current state of all these pins. It provides methods to write to those pins that would normally be written to by the computer end of the connection.

.4.3.1. DTR

Data Terminal Ready, DTR, means the computer is ready to send or receive data. CR, Computer Ready, would be more likely true nowadays, but the RS-232 standard was developed in the days of dumb terminals, when personal computers were still an oddity.

public abstract void setDTR(boolean dtr) public abstract boolean isDTR( )

.4.3.2. RTS

Request To Send, RTS, is one-half of hardware handshaking. The computer raises voltage on the RTS line to tell the modem its waiting to send.

public abstract void setRTS(boolean rts) public abstract boolean isRTS( )

.4.3.3. CTS

Clear To Send, CTS, is the other half of hardware handshaking. The modem raises the voltage on this wire to tell the computer that its ready to receive data. It drops the voltage when its no longer ready to receive data.

public abstract boolean isCTS( )

You cannot set the Clear To Send wire directly. Only the serial device can tell you when it is ready to receive. You cannot force it to be ready.

.4.3.4. DSR

The modem raises the voltage on the DSR line, Data Set Ready, to indicate that its turned on and operating. This line is also read-only.

public abstract boolean isDSR( )

.4.3.5. RI

The modem raises the voltage on the RI wire, Ring Indicator, to tell the computer that the phone is ringing.

public abstract boolean isRI( )

You cannot set the Ring Indicator bit directly. This is used only for one-way communication from the device back to the computer, not for the computer to send information to the device. (In other words, the computer can tell the modem the phone is ringing.)

.4.3.6. CD

The modem uses the CD wire, Carrier Detect, to tell the computer that it has successfully negotiated the low-level modem protocols with the modem on the other end of the connection.

public abstract boolean isCD( )

You cannot set the Carrier Detect bit directly. This is also a one-way communication from the device back to the computer.

.4.4. Serial Port Events

The examples so far all depend on the computer taking the initiative. The computer tells the modem when to dial, the printer when to print, and so on. By analogy with network programming, this is client-based. However, theres another model for port programs, the server-based program. Just as an Internet server waits for an incoming connection, a program can wait for incoming faxes through a fax modem, incoming BBS connections through a modem, notifications of impending shutdown from an uninterruptible power supply, paper-empty messages from a printer on a parallel port, and more. However, unlike the abstract network ports of Chapter 5, computers have no concept of binding to a serial port. Although you can check the various pins used to send information from a modem or other serial port device to the computer whenever you want to, its more convenient to do it asynchronously.

Incoming port access relies on an event-based model. When the runtime detects a change in state at a monitored serial port, it fires a serial port event to the registered serial port listener.

.4.4.1. SerialPortEventListener

There are three steps to respond to serial port events:

  1. Implement the SerialPortEventListener interface.

  2. Register your SerialPortEventListener object with the SerialPort object representing the serial port you want to monitor.

  3. Tell the SerialPort object the types of events you want to be notified of.

Step 1

As you might guess, you listen for serial port events with a SerialPortEventListener:

public interface SerialPortEventListener extends EventListener

This interface declares a single method, serialEvent( ):

public abstract void serialEvent(SerialPortEvent spe)

Inside this method, the getEventType( ) method of SerialPortEvent determines exactly what caused the serial port event and responds appropriately.

Step 2

Once youve constructed a SerialPortEventListener, you pass it to the SerialPort objects addEventListener( ) method:

public abstract void addEventListener(SerialPortEventListener listener) throws TooManyListenersException

You are limited to one event listener per port. Adding a second event listener throws a java.util.TooManyListenersException. If this is a problem, you can install an intermediate event listener directly with the SerialPort object. This listener could keep a list of other SerialPortEventListener objects and dispatch the events it receives to the other event listeners.

Should you need to, you can remove a listener from the port with the SerialPort objects removeEventListener( ) method. This method takes no arguments because theres never more than one event listener registered directly with the port.

public abstract void removeEventListener( )

Step 3

In many circumstances, you may not be interested in some or all of these events. By default, none of these events are fired unless you first enable them with one of the 10 notify methods in SerialPort:

public abstract void notifyOnDataAvailable(boolean enable) public abstract void notifyOnOutputEmpty(boolean enable) public abstract void notifyOnCTS(boolean enable) public abstract void notifyOnDSR(boolean enable) public abstract void notifyOnRingIndicator(boolean enable) public abstract void notifyOnCarrierDetect(boolean enable) public abstract void notifyOnOverrunError(boolean enable) public abstract void notifyOnParityError(boolean enable) public abstract void notifyOnFramingError(boolean enable) public abstract void notifyOnBreakInterrupt(boolean enable)

By default, no events are fired when the serial ports state changes. If you pass true to any of these methods, the VM fires a serial port event when the matching state changes.

.4.4.2. SerialPortEvent

The VM creates and fires serial port events to indicate a change on one of the standard serial port lines. The SerialPortEvent class declares these three public methods:

public int getEventType( ) public boolean getNewValue( ) public boolean getOldValue( )

The getEventType( ) method returns a named constant from the SerialPortEvent class that specifies what caused the event to be fired. There are 10 possibilities:

SerialPortEvent.DATA_AVAILABLE // Data has arrived at the port. SerialPortEvent.OUTPUT_BUFFER_EMPTY // Output buffer on the port is empty. SerialPortEvent.CTS // The Clear To Send pin has changed state. SerialPortEvent.DSR // The Data Set Ready pin has changed state. SerialPortEvent.RI // The Ring Indicator pin has changed state. SerialPortEvent.CD // The Carrier Detect pin has changed state. SerialPortEvent.OE // An overrun error occurred. SerialPortEvent.PE // A parity error occurred. SerialPortEvent.FE // A framing error occurred. SerialPortEvent.BI // A break interrupt was detected.

SerialPortEvent.DATA_AVAILABLE and SerialPortEvent.OUTPUT_BUFFER_EMPTY are enough information all by themselves. The other eight possible types, however, represent a boolean change from one state to another, from on to off or off to on. Therefore, there are also getNewValue( ) and getOldValue( ) methods to tell you what the state of the pin was before and after the event:

public boolean getNewValue( ) public boolean getOldValue( )

Example 22-7 activates the Ring Indicator and prints a message on System.out when the modem tells the computer the phone is ringing.

Example 22-7. PhoneListener

import javax.comm.*; import java.util.TooManyListenersException; public class PhoneListener implements SerialPortEventListener { public static void main(String[] args) { String portName = "COM1"; if (args.length > 0) portName = args[0]; PhoneListener pl = new PhoneListener( ); try { CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier(portName); if (cpi.getPortType( ) == CommPortIdentifier.PORT_SERIAL) { SerialPort modem = (SerialPort) cpi.open("Phone Listener", 1000); modem.notifyOnRingIndicator(true); modem.addEventListener(pl); } } catch (NoSuchPortException ex) { System.err.println("Usage: java PhoneListener port_name"); } catch (TooManyListenersException ex) { // shouldn happen in this example } catch (PortInUseException ex) {System.err.println(ex);} } public void serialEvent(SerialPortEvent evt) { System.err.println(evt.getEventType( )); if (evt.getEventType( ) == SerialPortEvent.RI) { System.out.println("The phone is ringing"); } } }

Категории

© amp.flylib.com,