You are on page 1of 108

SB in a NutShell

Making sense of the USB standard


Starting out new with USB can be quite daunting. With the USB 2.0 specification at 650
pages one could easily be put off just by the sheer size of the standard. This is only the
beginning of a long list of associated standards for USB. There are USB Class Standards
such as the HID Class Specification which details the common operation of devices
(keyboards, mice etc) falling under the HID (Human Interface Devices) Class - only
another 97 pages. If you are designing a USB Host, then you have three Host Controller
Interface Standards to choose from. None of these are detailed in the USB 2.0 Spec.
The good news is you don’t even need to bother reading the entire USB standard. Some
chapters were churned out by marketing, others aimed at the lower link layer normally
taken care off by your USB controller IC and a couple aimed at host and hub developers.
Lets take a little journey through the various chapters of the USB 2.0 specification and
briefly introduce the key points.
Chapter Name Description Pages
Includes the motivation and scope for USB. The most important piece of
1 Introduction information in this chapter is to make reference to the Universal Serial Bus 2
Device Class Specifications. No need reading this chapter.
Terms and
2 This chapter is self-explanatory and a necessary evil to any standard. 8
Abbreviations
Specifies the goals of USB which are Plug’n’Play and simplicity to the end
3 Background user (not developer). Introduces Low, Full and High Speed ranges with a 4
feature list straight from marketing. No need reading this chapter either.
This is where you can start reading. This chapter provides a basic overview of
Architectural
4 a USB system including topology, data rates, data flow types, basic electrical 10
Overview
specs etc.
This chapter starts to talk about how data flows on a Universal Serial Bus. It
introduces terms such as endpoints and pipes then spends most of the chapter
USB Data Flow
5 on each of the data flow types (Control, Interrupt, Isochronous and Bulk). 60
Model
While it’s important to know each transfer type and its properties it is a little
heavy on for a first reader.
This chapter details the USB’s two standard connectors. The important
information here is that a type A connector is oriented facing downstream and
a type B connector upstream. Therefore it should be impossible to plug a
cable into two upstream ports. All detachable cables must be full/high speed,
6 Mechanical 33
while any low speed cable must be hardwired to the appliance. Other than a
quick look at the connectors, you can skip this chapter unless you intend to
manufacture USB connectors and/or cables. PCB designers can find standard
footprints in this chapter.
This chapter looks at low level electrical signalling including line impedance,
rise/fall times, driver/receiver specifications and bit level encoding, bit
stuffing etc. The more important parts of this chapter are the device speed
identification by using a resistor to bias either data line and bus powered
7 Electrical 75
devices vs self powered devices. Unless you are designing USB transceivers
at a silicon level you can flip through this chapter. Good USB device
datasheets will detail what value bus termination resistors you will need for
bus impedance matching.
Now we start to get into the protocol layers. This chapter describes the USB
packets at a byte level including the sync, pid, address, endpoint, CRC fields.
Once this has been grasped it moves on to the next protocol layer, USB
8 Protocol Layer 45
packets. Most developers still don’t see these lower protocol layers as their
USB device IC’s take care of this. However a understanding of the status
reporting and handshaking is worthwhile.
This is the most frequently used chapter in the entire specification and the
only one I ever bothered printing and binding. This details the bus
USB Device
9 enumeration and request codes (set address, get descriptor etc) which make 36
Frame Work
up the most common protocol layer USB programmers and designers will
ever see. This chapter is a must read in detail.
This chapter covers issues relating to the host. This includes frame and
USB Host
microframe generation, host controller requirements, software mechanisms
10 Hardware and 23
and the universal serial bus driver model. Unless you are designing Hosts,
Software
you can skip this chapter.
Details the workings of USB hubs including hub configuration, split
11 Hub Specification transactions, standard descriptors for hub class etc. Unless you are designing 143
Hubs, you can skip this chapter.
So now we can begin to read the parts of the standard relevant to our needs. If you
develop drivers (Software) for USB peripherals then you may only need to read chapters,

o 4 - Architectural Overview
o 5 - USB Data Flow Model
o 9 - USB Device Frame Work, and
o 10 - USB Host Hardware and Software.

Peripheral hardware (Electronics) designers on the other hand may only need to read
chapters,

o 4 - Architectural Overview
o 5 - USB Data Flow Model
o 6 - Mechanical, and
o 7 - Electrical.

USB in a NutShell for Peripheral


Designers
Now lets face it, (1) most of us are here to develop USB peripherals and (2) it's common
to read a standard and still have no idea how to implement a device. So in the next 7
chapters we focus on the relevant parts needed to develop a USB device. This allows you
to grab a grasp of USB and its issues allowing you to further research the issues specific
to your application.
The USB 1.1 standard was complex enough before High Speed was thrown into USB 2.0.
In order to help understand the fundamental principals behind USB, we omit many areas
specific to High Speed devices.
Introducing the Universal Serial Bus
USB version 1.1 supported two speeds, a full speed mode of 12Mbits/s and a low speed
mode of 1.5Mbits/s. The 1.5Mbits/s mode is slower and less susceptible to EMI, thus
reducing the cost of ferrite beads and quality components. For example, crystals can be
replaced by cheaper resonators. USB 2.0 which is still yet to see day light on mainstream
desktop computers has upped the stakes to 480Mbits/s. The 480Mbits/s is known as High
Speed mode and was a tack on to compete with the Firewire Serial Bus.
USB Speeds

• High Speed - 480Mbits/s


• Full Speed - 12Mbits/s
• Low Speed - 1.5Mbits/s

The Universal Serial Bus is host controlled. There can only be one host per bus. The
specification in itself, does not support any form of multimaster arrangement. However
the On-The-Go specification which is a tack on standard to USB 2.0 has introduced a Host
Negotiation Protocol which allows two devices negotiate for the role of host. This is
aimed at and limited to single point to point connections such as a mobile phone and
personal organiser and not multiple hub, multiple device desktop configurations. The
USB host is responsible for undertaking all transactions and scheduling bandwidth. Data
can be sent by various transaction methods using a token-based protocol.
In my view the bus topology of USB is somewhat limiting. One of the original intentions
of USB was to reduce the amount of cabling at the back of your PC. Apple people will say
the idea came from the Apple Desktop Bus, where both the keyboard, mouse and some
other peripherals could be connected together (daisy chained) using the one cable.
However USB uses a tiered star topology, simular to that of 10BaseT Ethernet. This
imposes the use of a hub somewhere, which adds to greater expense, more boxes on
your desktop and more cables. However it is not as bad as it may seem. Many devices
have USB hubs integrated into them. For example, your keyboard may contain a hub
which is connected to your computer. Your mouse and other devices such as your digital
camera can be plugged easily into the back of your keyboard. Monitors are just another
peripheral on a long list which commonly have in-built hubs.
This tiered star topology, rather than simply daisy chaining devices together has some
benefits. Firstly power to each device can be monitored and even switched off if an
overcurrent condition occurs without disrupting other USB devices. Both high, full and
low speed devices can be supported, with the hub filtering out high speed and full speed
transactions so lower speed devices do not receive them.
Up to 127 devices can be connected to any one USB bus at any one given time. Need
more devices? - simply add another port/host. While most earlier USB hosts had two
ports, most manufacturers have seen this as limiting and are starting to introduce 4 and
5 port host cards with an internal port for hard disks etc. The early hosts had one USB
controller and thus both ports shared the same available USB bandwidth. As bandwidth
requirements grew, we are starting to see multi-port cards with two or more controllers
allowing individual channels.
The USB host controllers have their own specifications. With USB 1.1, there were two
Host Controller Interface Specifications, UHCI (Universal Host Controller Interface)
developed by Intel which puts more of the burden on software (Microsoft) and allowing
for cheaper hardware and the OHCI (Open Host Controller Interface) developed by
Compaq, Microsoft and National Semiconductor which places more of the burden on
hardware(Intel) and makes for simpler software. Typical hardware / software engineer
relationship. . .
With the introduction of USB 2.0 a new Host Controller Interface Specification was
needed to describe the register level details specific to USB 2.0. The EHCI (Enhanced
Host Controller Interface) was born. Significant Contributors include Intel, Compaq, NEC,
Lucent and Microsoft so it would hopefully seem they have pooled together to provide us
one interface standard and thus only one new driver to implement in our operating
systems. Its about time.
USB as its name would suggest is a serial bus. It uses 4 shielded wires of which two are
power (+5v & GND). The remaining two are twisted pair differential data signals. It uses
a NRZI (Non Return to Zero Invert) encoding scheme to send data with a sync field to
synchronise the host and receiver clocks.
USB supports plug’n’plug with dynamically loadable and unloadable drivers. The user
simply plugs the device into the bus. The host will detect this addition, interrogate the
newly inserted device and load the appropriate driver all in the time it takes the
hourglass to blink on your screen provided a driver is installed for your device. The end
user needs not worry about terminations, terms such as IRQs and port addresses, or
rebooting the computer. Once the user is finished, they can simply lug the cable out, the
host will detect its absence and automatically unload the driver.
The loading of the appropriate driver is done using a PID/VID (Product ID/Vendor ID)
combination. The VID is supplied by the USB Implementor's forum at a cost and this is
seen as another sticking point for USB. The latest info on fees can be found on the USB
Implementor’s Website
Other standards organisations provide a extra VID for non-commercial activities such as
teaching, research or fiddling (The Hobbyist). The USB Implementors forum has yet to
provide this service. In these cases you may wish to use one assigned to your
development system's manufacturer. For example most chip manufacturers will have a
VID/PID combination you can use for your chips which is known not to exist as a
commercial device. Other chip manufacturers can even sell you a PID to use with their
VID for your commercial device.
Another more notable feature of USB, is its transfer modes. USB supports Control,
Interrupt, Bulk and Isochronous transfers. While we will look at the other transfer modes
later, Isochronous allows a device to reserve a defined amount of bandwidth with
guaranteed latency. This is ideal in Audio or Video applications where congestion may
cause loss of data or frames to drop. Each transfer mode provides the designer trade-
offs in areas such as error detection and recovery, guaranteed latency and bandwidth.

Connectors
All devices have an upstream connection to the host and all hosts have a downstream
connection to the device. Upstream and downstream connectors are not mechanically
interchangeable, thus eliminating illegal loopback connections at hubs such as a
downstream port connected to a downstream port. There are commonly two types of
connectors, called type A and type B which are shown below.

Type A USB Connector Type B USB Connector


Type A plugs always face upstream. Type A sockets will typically find themselves on
hosts and hubs. For example type A sockets are common on computer main boards and
hubs. Type B plugs are always connected downstream and consequently type B sockets
are found on devices.
It is interesting to find type A to type A cables wired straight through and an array of USB
gender changers in some computer stores. This is in contradiction of the USB
specification. The only type A plug to type A plug devices are bridges which are used to
connect two computers together. Other prohibited cables are USB extensions which has
a plug on one end (either type A or type B) and a socket on the other. These cables
violate the cable length requirements of USB.
USB 2.0 included errata which introduces mini-usb B connectors. The details on these
connectors can be found in Mini-B Connector Engineering Change Notice The reasoning
behind the mini connectors came from the range of miniature electronic devices such as
mobile phones and organisers. The current type B connector is too large to be easily
integrated into these devices.
Just recently released has been the On-The-Go specification which adds peer-to-peer
functionality to USB. This introduces USB hosts into mobile phone and electronic
organisers, and thus has included a specification for mini-A plugs, mini-A receptacles,
and mini-AB receptacles. I guess we should be inundated with mini USB cables soon and
a range of mini to standard converter cables.
Pin Number Cable Colour Function
1 Red VBUS (5 volts)
2 White D-
3 Green D+
4 Black Ground
Standard internal wire colours are used in USB cables, making it easier to identify wires
from manufacturer to manufacturer. The standard specifies various electrical parameters
for the cables. It is interesting to read the detail the original USB 1.0 spec included. You
would understand it specifying electrical attributes, but paragraph 6.3.1.2 suggested the
recommended colour for overmolds on USB cables should be frost white - how boring!
USB 1.1 and USB 2.0 was relaxed to recommend Black, Grey or Natural.
PCB designers will want to reference chapter 6 for standard foot prints and pinouts.

Electrical
Unless you are designing the silicon for a USB device/transceiver or USB host/hub, there
is not all that much you need to know about the electrical specifications in chapter 7. We
briefly address the essential points here.
As we have discussed, USB uses a differential transmission pair for data. This is encoded
using NRZI and is bit stuffed to ensure adequate transitions in the data stream. On low
and full speed devices, a differential ‘1’ is transmitted by pulling D+ over 2.8V with a
15K ohm resistor pulled to ground and D- under 0.3V with a 1.5K ohm resistor pulled to
3.6V. A differential ‘0’ on the other hand is a D- greater than 2.8V and a D+ less than
0.3V with the same appropriate pull down/up resistors.
The receiver defines a differential ‘1’ as D+ 200mV greater than D- and a differential ‘0’
as D+ 200mV less than D-. The polarity of the signal is inverted depending on the speed
of the bus. Therefore the terms ‘J’ and ‘K’ states are used in signifying the logic levels. In
low speed a ‘J’ state is a differential 0. In high speed a ‘J’ state is a differential 1.
USB transceivers will have both differential and single ended outputs. Certain bus states
are indicated by single ended signals on D+, D- or both. For example a single ended zero
or SE0 can be used to signify a device reset if held for more than 10mS. A SE0 is
generated by holding both D- and D+ low (< 0.3V). Single ended and differential outputs
are important to note if you are using a transceiver and FPGA as your USB device. You
cannot get away with sampling just the differential output.
The low speed/full speed bus has a characteristic impedance of 90 ohms +/- 15%. It is
therefore important to observe the datasheet when selecting impedance matching series
resistors for D+ and D-. Any good datasheet should specify these values and tolerances.
High Speed (480Mbits/s) mode uses a 17.78mA constant current for signalling to reduce
noise.

Speed Identification
A USB device must indicate its speed by pulling either the D+ or D- line high to 3.3 volts.
A full speed device, pictured below will use a pull up resistor attached to D+ to specify
itself as a full speed device. These pull up resistors at the device end will also be used by
the host or hub to detect the presence of a device connected to its port. Without a pull
up resistor, USB assumes there is nothing connected to the bus. Some devices have this
resistor built into its silicon, which can be turned on and off under firmware control,
others require an external resistor.
For example Philips Semiconductor has a SoftConnectTM technology. When first
connected to the bus, this allows the microcontroller to initialise the USB function device
before it enables the pull up speed identification resistor, indicating a device is attached
to the bus. If the pull up resistor was connected to Vbus, then this would indicate a device
has been connected to the bus as soon as the plug is inserted. The host may then
attempt to reset the device and ask for a descriptor when the microprocessor hasn’t
even started to initialise the usb function device.
Other vendors such as Cypress Semiconductor also use a programmable resistor for Re-
NumerationTM purposes in their EzUSB devices where the one device can be enumerated
for one function such as In field programming then be disconnected from the bus under
firmware control, and enumerate as another different device, all without the user lifting
an eyelid. Many of the EzUSB devices do not have any Flash or OTP ROM to store code.
They are bootstraped at connection.
Figure 2 : Full Speed Device with pull up resistor connected to D+

Figure 3 : Low Speed Device with pull up resistor connected to D-


You will notice we have not included speed identification for High Speed mode. High
speed devices will start by connecting as a full speed device (1.5k to 3.3V). Once it has
been attached, it will do a high speed chirp during reset and establish a high speed
connection if the hub supports it. If the device operates in high speed mode, then the
pull up resistor is removed to balance the line.
A USB 2.0 compliant device is not required to support high-speed mode. This allows
cheaper devices to be produced if the speed isn’t critical. This is also the case for a low
speed USB 1.1 devices which is not required to support full speed.
However a high speed device must not support low speed mode. It should only support
full speed mode needed to connect first, then high speed mode if successfully negotiated
later. A USB 2.0 compliant downstream facing device (Hub or Host) must support all
three modes, high speed, full speed and low speed.
Power (VBUS)
One of the benefits of USB is bus-powered devices - devices which obtain its power from
the bus and requires no external plug packs or additional cables. However many leap at
this option without first considering all the necessary criteria.
A USB device specifies its power consumption expressed in 2mA units in the
configuration descriptor which we will examine in detail later. A device cannot increase
its power consumption, greater than what it specifies during enumeration, even if it
looses external power. There are three classes of USB functions,

• Low-power bus powered functions


• High-power bus powered functions
• Self-powered functions

Low power bus powered functions draw all its power from the VBUS and cannot draw any
more than one unit load. The USB specification defines a unit load as 100mA. Low power
bus powered functions must also be designed to work down to a VBUS voltage of 4.40V
and up to a maximum voltage of 5.25V measured at the upsteam plug of the device. For
many 3.3V devices, LDO regulators are mandatory.
High power bus powered functions will draw all its power from the bus and cannot draw
more than one unit load until it has been configured, after which it can then drain 5 unit
loads (500mA Max) provided it asked for this in its descriptor. High power bus functions
must be able to be detected and enumerated at a minimum 4.40V. When operating at a
full unit load, a minimum VBUS of 4.75 V is specified with a maximum of 5.25V. Once
again, these measurements are taken at the upstream plug.
Self power functions may draw up to 1 unit load from the bus and derive the rest of it’s
power from an external source. Should this external source fail, it must have provisions
in place to draw no more than 1 unit load from the bus. Self powered functions are easier
to design to specification as there is not so much of an issue with power consumption.
The 1 unit bus powered load allows the detection and enumeration of devices without
mains/secondary power applied.
No USB device, whether bus powered or self powered can drive the VBUS on its upstream
facing port. If VBUS is lost, the device has a lengthy 10 seconds to remove power from the
D+/D- pull-up resistors used for speed identification.
Other VBUS considerations are the Inrush current which must be limited. This is outlined in
the USB specification paragraph 7.2.4.1 and is commonly overlooked. Inrush current is
contributed to the amount of capacitance on your device between VBUS and ground. The
spec therefore specifies that the maximum decoupling capacitance you can have on
your device is 10uF. When you disconnect the device after current is flowing through the
inductive USB cable, a large flyback voltage can occur on the open end of the cable. To
prevent this, a 1uF minimum VBUS decoupling capacitance is specified.
For the typical bus powered device, it can not drain any more than 500mA which is not
unreasonable. So what is the complication you ask? Perhaps Suspend Mode?

Suspend Current
Suspend mode is mandatory on all devices. During suspend, additional constrains come
into force. The maximum suspend current is proportional to the unit load. For a 1 unit
load device (default) the maximum suspend current is 500uA. This includes current from
the pull up resistors on the bus. At the hub, both D- and D+ have pull down resistors of
15K ohms. For the purposes of power consumption, the pull down resistor at the device
is in series with the 1.5K ohms pull up, making a total load of 16.5K ohms on a VTERM of
typically 3.3v. Therefore this resistor sinks 200uA before we even start.
Another consideration for many devices is the 3.3V regulator. Many of the USB devices
run on 3.3V. The PDIUSBD11 is one such example. Linear regulators are typically quite
inefficient with average quiescent currents in the order of 600uA, therefore more
efficient and thus expensive regulators are called for. In the majority of cases, you must
also slow down or stop clocks on microcontrollers to fall within the 500uA limit.
Many developers ask in the USB Implementor's Forum, what are the complications of
exceeding this limit? It is understood, that most hosts and hubs don’t have the ability to
detect such an overload of this magnitude and thus if you drain maybe 5mA or even
10mA you should still be fine, bearing in mind that at the end of the day, your device
violates the USB specification. However in normal operation, if you try to exceed the
100mA or your designated permissible load, then expect the hub or host to detect this
and disconnect your device, in the interest of the integrity of the bus.
Of course these design issues can be avoided if you choose to design a self powered
device. Suspend currents may not be a great concern for desktop computers but with
the introduction of the On-The-Go Specification we will start seeing USB hosts built into
mobile phones and mobile organisers. The power consumption pulled from these devices
will adversely effect the operating life of the battery.

Entering Suspend Mode


A USB device will enter suspend when there is no activity on the bus for greater than
3.0ms. It then has a further 7ms to shutdown the device and draw no more than the
designated suspend current and thus must be only drawing the rated suspend current
from the bus 10mS after bus activity stopped. In order to maintain connected to a
suspended hub or host, the device must still provide power to its pull up speed selection
resistors during suspend.
USB has a start of frame packet or keep alive sent periodically on the bus. This prevents
an idle bus from entering suspend mode in the absence of data.

• A high speed bus will have micro-frames sent every 125.0 µs ±62.5 ns.
• A full speed bus will have a frame sent down each 1.000 ms ±500 ns.
• A low speed bus will have a keep alive which is a EOP (End of Packet) every 1ms only in the absence of
any low speed data.

The term "Global Suspend" is used when the entire USB bus enters suspend mode
collectively. However selected devices can be suspended by sending a command to the
hub that the device is connected too. This is referred to as a "Selective Suspend."
The device will resume operation when it receives any non idle signalling. If a device has
remote wakeup enabled then it may signal to the host to resume from suspend.

Data Signalling Rate


Another area which is often overlooked is the tolerance of the USB clocks. This is
specified in the USB specification, section 7.1.11.
• High speed data is clocked at 480.00Mb/s with a data signalling tolerance of ± 500ppm.
• Full speed data is clocked at 12.000Mb/s with a data signalling tolerance of ±0.25% or 2,500ppm.
• Low speed data is clocked at 1.50Mb/s with a data signalling tolerance of ±1.5% or 15,000ppm.

This allows resonators to be used for low cost low speed devices, but rules them out for
full or high speed devices.

USB Protocols
Unlike RS-232 and similar serial interfaces where the format of data being sent is not
defined, USB is made up of several layers of protocols. While this sounds complicated,
don’t give up now. Once you understand what is going on, you really only have to worry
about the higher level layers. In fact most USB controller I.C.s will take care of the lower
layer, thus making it almost invisible to the end designer.
Each USB transaction consists of a

o Token Packet (Header defining what it expects to follow), an


o Optional Data Packet, (Containing the payload) and a
o Status Packet (Used to acknowledge transactions and to provide a means of error correction)

As we have already discussed, USB is a host centric bus. The host initiates all
transactions. The first packet, also called a token is generated by the host to describe
what is to follow and whether the data transaction will be a read or write and what the
device’s address and designated endpoint is. The next packet is generally a data packet
carrying the payload and is followed by an handshaking packet, reporting if the data or
token was received successfully, or if the endpoint is stalled or not available to accept
data.

Common USB Packet Fields


Data on the USBus is transmitted LSBit first. USB packets consist of the following fields,

o Sync

All packets must start with a sync field. The sync field is 8 bits long at low
and full speed or 32 bits long for high speed and is used to synchronise the
clock of the receiver with that of the transmitter. The last two bits indicate
where the PID fields starts.

o PID

PID stands for Packet ID. This field is used to identify the type of packet that
is being sent. The following table shows the possible values.
Group PID Value Packet Identifier
Token 0001 OUT Token
1001 IN Token
0101 SOF Token
1101 SETUP Token
0011 DATA0
1011 DATA1
Data
0111 DATA2
1111 MDATA
0010 ACK Handshake
1010 NAK Handshake
Handshake
1110 STALL Handshake
0110 NYET (No Response Yet)
1100 PREamble
1100 ERR
Special
1000 Split
0100 Ping
There are 4 bits to the PID, however to insure it is received correctly, the 4
bits are complemented and repeated, making an 8 bit PID in total. The
resulting format is shown below.
PID0 PID1 PID2 PID3 nPID0 nPID1 nPID2 nPID3

o ADDR

The address field specifies which device the packet is designated for. Being 7
bits in length allows for 127 devices to be supported. Address 0 is not valid,
as any device which is not yet assigned an address must respond to packets
sent to address zero.

o ENDP

The endpoint field is made up of 4 bits, allowing 16 possible endpoints. Low


speed devices, however can only have 2 additional endpoints on top of the
default pipe. (4 endpoints max)

o CRC

Cyclic Redundancy Checks are performed on the data within the packet
payload. All token packets have a 5 bit CRC while data packets have a 16 bit
CRC.

o EOP

End of packet. Signalled by a Single Ended Zero (SE0) for approximately 2 bit
times followed by a J for 1 bit time.

USB Packet Types


USB has four different packet types. Token packets indicate the type of transaction to
follow, data packets contain the payload, handshake packets are used for acknowledging
data or reporting errors and start of frame packets indicate the start of a new frame.
o Token Packets

There are three types of token packets,

 In - Informs the USB device that the host wishes to read information.
 Out - Informs the USB device that the host wishes to send information.
 Setup - Used to begin control transfers.

Token Packets must conform to the following format,


Sync PID ADDR ENDP CRC5 EOP

o Data Packets

There are two types of data packets each capable of transmitting up to 1024
bytes of data.

 Data0
 Data1

High Speed mode defines another two data PIDs, DATA2 and MDATA.
Data packets have the following format,
Sync PID Data CRC16 EOP

 Maximum data payload size for low-speed devices is 8 bytes.


 Maximum data payload size for full-speed devices is 1023 bytes.
 Maximum data payload size for high-speed devices is 1024 bytes.
 Data must be sent in multiples of bytes.
o Handshake Packets

There are three type of handshake packets which consist simply of the PID

 ACK - Acknowledgment that the packet has been successfully received.


 NAK - Reports that the device temporary cannot send or received data. Also used during
interrupt transactions to inform the host there is no data to send.
 STALL - The device finds its in a state that it requires intervention from the host.

Handshake Packets have the following format,


Sync PID EOP

o Start of Frame Packets

The SOF packet consisting of an 11-bit frame number is sent by the host
every 1ms ± 500ns on a full speed bus or every 125 µs ± 0.0625 µs on a
high speed bus.
Frame
Sync PID CRC5 EOP
Number
USB Functions
When we think of a USB device, we think of a USB peripheral, but a USB device could
mean a USB transceiver device used at the host or peripheral, a USB Hub or Host
Controller IC device, or a USB peripheral device. The standard therefore makes
references to USB functions which can be seen as USB devices which provide a capability
or function such as a Printer, Zip Drive, Scanner, Modem or other peripheral.
So by now we should know the sort of things which make up a USB packet. No? You're
forgotten how many bits make up a PID field already? Well don't be too alarmed.
Fortunately most USB functions handle the low level USB protocols up to the transaction
layer (which we will cover next chapter) in silicon. The reason why we cover this
information is most USB function controllers will report errors such as PID Encoding Error.
Without briefly covering this, one could ask what is a PID Encoding Error? If you
suggested that the last four bits of the PID didn't match the inverse of the first four bits
then you would be right.

Most functions will have a series of buffers, typically 8 bytes long. Each buffer will belong
to an endpoint - EP0 IN, EP0 OUT etc. Say for example, the host sends a device
descriptor request. The function hardware will read the setup packet and determine from
the address field whether the packet is for itself, and if so will copy the payload of the
following data packet to the appropriate endpoint buffer dictated by the value in the
endpoint field of the setup token. It will then send a handshake packet to acknowledge
the reception of the byte and generate an internal interrupt within the
semiconductor/micro-controller for the appropriate endpoint signifying it has received a
packet. This is typically all done in hardware.
The software now gets an interrupt, and should read the contents of the endpoint buffer
and parse the device descriptor request.

Endpoints
Endpoints can be described as sources or sinks of data. As the bus is host centric,
endpoints occur at the end of the communications channel at the USB function. At the
software layer, your device driver may send a packet to your devices EP1 for example.
As the data is flowing out from the host, it will end up in the EP1 OUT buffer. Your
firmware will then at its leisure read this data. If it wants to return data, the function
cannot simply write to the bus as the bus is controlled by the host. Therefore it writes
data to EP1 IN which sits in the buffer until such time when the host sends a IN packet to
that endpoint requesting the data. Endpoints can also be seen as the interface between
the hardware of the function device and the firmware running on the function device.
All devices must support endpoint zero. This is the endpoint which receives all of the
devices control and status requests during enumeration and throughout the duration
while the device is operational on the bus.

Pipes
While the device sends and receives data on a series of endpoints, the client software
transfers data through pipes. A pipe is a logical connection between the host and
endpoint(s). Pipes will also have a set of parameters associated with them such as how
much bandwidth is allocated to it, what transfer type (Control, Bulk, Iso or Interrupt) it
uses, a direction of data flow and maximum packet/buffer sizes. For example the default
pipe is a bi-directional pipe made up of endpoint zero in and endpoint zero out with a
control transfer type.
USB defines two types of pipes

• Stream Pipes have no defined USB format, that is you can send any type of data down a stream pipe
and can retrieve the data out the other end. Data flows sequentially and has a pre-defined direction,
either in or out. Stream pipes will support bulk, isochronous and interrupt transfer types. Stream pipes
can either be controlled by the host or device.

• Message Pipes have a defined USB format. They are host controlled, which are initiated by a request
sent from the host. Data is then transferred in the desired direction, dictated by the request. Therefore
message pipes allow data to flow in both directions but will only support control transfers.

Endpoint Types
The Universal Serial Bus specification defines four transfer/endpoint types,

o Control Transfers
o Interrupt Transfers
o Isochronous Transfers
o Bulk Transfers

Control Transfers
Control transfers are typically used for command and status operations. They are
essential to set up a USB device with all enumeration functions being performed
using control transfers. They are typically bursty, random packets which are
initiated by the host and use best effort delivery. The packet length of control
transfers in low speed devices must be 8 bytes, high speed devices allow a packet
size of 8, 16, 32 or 64 bytes and full speed devices must have a packet size of 64
bytes.
A control transfer can have up to three stages.

o The Setup Stage is where the request is sent. This consists of three packets. The setup token is
sent first which contains the address and endpoint number. The data packet is sent next and
always has a PID type of data0 and includes a setup packet which details the type of request. We
detail the setup packet later. The last packet is a handshake used for acknowledging successful
receipt or to indicate an error. If the function successfully receives the setup data (CRC and PID
etc OK) it responds with ACK, otherwise it ignores the data and doesn’t send a handshake
packet. Functions cannot issue a STALL or NAK packet in response to a setup packet.

o The optional Data Stage consists of one or multiple IN or OUT transfers. The
setup request indicates the amount of data to be transmitted in this stage. If
it exceeds the maximum packet size, data will be sent in multiple transfers
each being the maximum packet length except for the last packet.

The data stage has two different scenarios depending upon the direction of
data transfer.

 IN: When the host is ready to receive control data it issues an IN Token. If the function
receives the IN token with an error e.g. the PID doesn't match the inverted PID bits, then
it ignores the packet. If the token was received correctly, the device can either reply with
a DATA packet containing the control data to be sent, a stall packet indicating the
endpoint has had a error or a NAK packet indicating to the host that the endpoint is
working, but temporary has no data to send.
 OUT: When the host needs to send the device a control data packet, it issues an OUT
token followed by a data packet containing the control data as the payload. If any part of
the OUT token or data packet is corrupt then the function ignores the packet. If the
function's endpoint buffer was empty and it has clocked the data into the endpoint buffer
it issues an ACK informing the host it has successfully received the data. If the endpoint
buffer is not empty due to processing of the previous packet, then the function returns a
NAK. However if the endpoint has had a error and its halt bit has been set, it returns a
STALL.

o Status Stage reports the status of the overall request and this once again varies due to direction
of transfer. Status reporting is always performed by the function.

 IN: If the host sent IN token(s) during the data stage to receive data,
then the host must acknowledge the successful recept of this data.
This is done by the host sending an OUT token followed by a zero
length data packet. The function can now report its status in the
handshaking stage. An ACK indicates the function has completed the
command is now ready to accept another command. If an error
occurred during the processing of this command, then the function will
issue a STALL. However if the function is still processing, it returns a
NAK indicating to the host to repeat the status stage later.


 OUT: If the host sent OUT token(s) during the data stage to transmit
data, the function will acknowledge the successful recept of data by
sending a zero length packet in response to an IN token. However if an
error occurred, it should issue a STALL or if it is still busy processing
data, it should issue a NAK asking the host to retry the status phase
later.

Control Transfers : The bigger picture


Now how does all this fit together? Let's say for example, the Host wants to
request a device descriptor during enumeration. The packets which are sent are as
follows.
The host will send the Setup token telling the function that the following packet is
a Setup packet. The Address field will hold the address of the device the host is
requesting the descriptor from. The endpoint number should be zero, specifying
the default pipe. The host will then send a DATA0 packet. This will have an 8 byte
payload which is the Device Descriptor Request as outlined in Chapter 9 of the USB
Specification. The USB function then acknowledges the setup packet has been read
correctly with no errors. If the packet was received corrupt, the device just ignores
this packet. The host will then resend the packet after a short delay.
1. Setup Token Sync PID ADDR ENDP CRC5 EOP Address & Endpoint Number

2. Data0 Packet Sync PID Data0 CRC16 EOP Device Descriptor Request

3. Ack Handshake Sync PID EOP Device Ack. Setup Packet


The above three packets represent the first USB transaction. The USB device will
now decode the 8 bytes received, and determine it was a device descriptor
request. The device will then attempt to send the Device Descriptor, which will be
the next USB transaction.
1. In Token Sync PID ADDR ENDP CRC5 EOP Address & Endpoint Number

First 8 Bytes of Device


2. Data1 Packet Sync PID Data1 CRC16 EOP
Descriptor

3. Ack Handshake Sync PID EOP Host Acknowledges Packet

1. In Token Sync PID ADDR ENDP CRC5 EOP Address & Endpoint Number
2. Data0 Packet Sync PID Data0 CRC16 EOP Last 4 bytes + Padding

3. Ack Handshake Sync PID EOP Host Acknowledges Packet


In this case, we assume that the maximum payload size is 8 bytes. The host sends
the IN token, telling the device it can now send data for this endpoint. As the
maximum packet size is 8 bytes, we must split up the 12 byte device descriptor
into chunks to send. Each chunk must be 8 bytes except for the last transaction.
The host acknowledges every data packet we send it.
Once the device descriptor is sent, a status transaction follows. If the transactions
were successful, the host will send a zero length packet indicating the overall
transaction was successful. The function then replies to this zero length packet
indicating its status.
1. Out Token Sync PID ADDR ENDP CRC5 EOP Address & Endpoint Number

2. Data1 Packet Sync PID Data1 CRC16 EOP Zero Length Packet

Device Ack. Entire


3. Ack Handshake Sync PID EOP
Transaction

Interrupt Transfers
Any one who has had experience of interrupt requests on microcontrollers will
know that interrupts are device generated. However under USB if a device requires
the attention of the host, it must wait until the host polls it before it can report that
it needs urgent attention!
Interrupt Transfers

 Guaranteed Latency
 Stream Pipe - Unidirectional
 Error detection and next period retry.

Interrupt transfers are typically non-periodic, small device "initiated"


communication requiring bounded latency. An Interrupt request is queued by the
device until the host polls the USB device asking for data.

o The maximum data payload size for low-speed devices is 8 bytes.


o Maximum data payload size for full-speed devices is 64 bytes.
o Maximum data payload size for high-speed devices is 1024 bytes.
The above diagram shows the format of an Interrupt IN and Interrupt OUT
transaction.

o IN: The host will periodically poll the interrupt endpoint. This rate of polling is
specified in the endpoint descriptor which is covered later. Each poll will
involve the host sending an IN Token. If the IN token is corrupt, the function
ignores the packet and continues monitoring the bus for new tokens.

If an interrupt has been queued by the device, the function will send a data
packet containing data relevant to the interrupt when it receives the IN
Token. Upon successful recept at the host, the host will return an ACK.
However if the data is corrupted, the host will return no status. If on the
other hand a interrupt condition was not present when the host polled the
interrupt endpoint with an IN token, then the function signals this state by
sending a NAK. If an error has occurred on this endpoint, a STALL is sent in
reply to the IN token instead.

o OUT: When the host wants to send the device interrupt data, it issues an
OUT token followed by a data packet containing the interrupt data. If any
part of the OUT token or data packet is corrupt then the function ignores the
packet. If the function's endpoint buffer was empty and it has clocked the
data into the endpoint buffer it issues an ACK informing the host it has
successfully received the data. If the endpoint buffer is not empty due to
processing of a previous packet, then the function returns an NAK. However
if an error occurred with the endpoint consequently and its halt bit has been
set, it returns a STALL.

Isochronous Transfers
Isochronous transfers occur continuously and periodically. They typically contain
time sensitive information, such as an audio or video stream. If there were a delay
or retry of data in an audio stream, then you would expect some erratic audio
containing glitches. The beat may no longer be in sync. However if a packet or
frame was dropped every now and again, it is less likely to be noticed by the
listener.
Isochronous Transfers provide

 Guaranteed access to USB bandwidth.


 Bounded latency.
 Stream Pipe - Unidirectional
 Error detection via CRC, but no retry or guarantee of delivery.
 Full & high speed modes only.
 No data toggling.

The maximum size data payload is specified in the endpoint descriptor of an


Isochronous Endpoint. This can be up to a maximum of 1023 bytes for a full speed
device and 1024 bytes for a high speed device. As the maximum data payload size
is going to effect the bandwidth requirements of the bus, it is wise to specify a
conservative payload size. If you are using a large payload, it may also be to your
advantage to specify a series of alternative interfaces with varying isochronous
payload sizes. If during enumeration, the host cannot enable your preferred
isochronous endpoint due to bandwidth restrictions, it has something to fall back
on rather than just failing completely. Data being sent on an isochronous endpoint
can be less than the pre-negotiated size and may vary in length from transaction
to transaction.

The above diagram shows the format of an Isochronous IN and OUT transaction.
Isochronous transactions do not have a handshaking stage and cannot report
errors or STALL/HALT conditions.

Bulk Transfers
Bulk transfers can be used for large bursty data. Such examples could include a
print-job sent to a printer or an image generated from a scanner. Bulk transfers
provide error correction in the form of a CRC16 field on the data payload and error
detection/re-transmission mechanisms ensuring data is transmitted and received
without error.
Bulk transfers will use spare un-allocated bandwidth on the bus after all other
transactions have been allocated. If the bus is busy with isochronous and/or
interrupt then bulk data may slowly trickle over the bus. As a result Bulk transfers
should only be used for time insensitive communication as there is no guarantee of
latency.
Bulk Transfers
 Used to transfer large bursty data.
 Error detection via CRC, with guarantee of delivery.
 No guarantee of bandwidth or minimum latency.
 Stream Pipe - Unidirectional
 Full & high speed modes only.

Bulk transfers are only supported by full and high speed devices. For full speed
endpoints, the maximum bulk packet size is either 8, 16, 32 or 64 bytes long. For
high speed endpoints, the maximum packet size can be up to 512 bytes long. If the
data payload falls short of the maximum packet size, it doesn't need to be padded
with zeros. A bulk transfer is considered complete when it has transferred the
exact amount of data requested, transferred a packet less than the maximum
endpoint size of transferred a zero-length packet.

The above diagram shows the format of a bulk IN and OUT transaction.

o IN: When the host is ready to receive bulk data it issues an IN Token. If the
function receives the IN token with an error, it ignores the packet. If the
token was received correctly, the function can either reply with a DATA
packet containing the bulk data to be sent, or a stall packet indicating the
endpoint has had a error or a NAK packet indicating to the host that the
endpoint is working, but temporary has no data to send.
o OUT: When the host wants to send the function a bulk data packet, it issues
an OUT token followed by a data packet containing the bulk data. If any part
of the OUT token or data packet is corrupt then the function ignores the
packet. If the function's endpoint buffer was empty and it has clocked the
data into the endpoint buffer it issues an ACK informing the host it has
successfully received the data. If the endpoint buffer is not empty due to
processing a previous packet, then the function returns an NAK. However if
the endpoint has had an error and it's halt bit has been set, it returns a
STALL.

Bandwidth Management
The host is responsible in managing the bandwidth of the bus. This is done at
enumeration when configuring Isochronous and Interrupt Endpoints and
throughout the operation of the bus. The specification places limits on the bus,
allowing no more than 90% of any frame to be allocated for periodic transfers
(Interrupt and Isochronous) on a full speed bus. On high speed buses this limitation
gets reduced to no more than 80% of a microframe can be allocated for periodic
transfers.
So you can quite quickly see that if you have a highly saturated bus with periodic
transfers, the remaining 10% is left for control transfers and once those have been
allocated, bulk transfers will get its slice of what is left.

USB Descriptors
All USB devices have a hierarchy of descriptors which describe to the host
information such as what the device is, who makes it, what version of USB it
supports, how many ways it can be configured, the number of endpoints and their
types etc
The more common USB descriptors are

 Device Descriptors
 Configuration Descriptors
 Interface Descriptors
 Endpoint Descriptors
 String Descriptors

USB devices can only have one device descriptor. The device descriptor includes
information such as what USB revision the device complies to, the Product and
Vendor IDs used to load the appropriate drivers and the number of possible
configurations the device can have. The number of configurations indicate how
many configuration descriptors branches are to follow.
The configuration descriptor specifies values such as the amount of power this
particular configuration uses, if the device is self or bus powered and the number
of interfaces it has. When a device is enumerated, the host reads the device
descriptors and can make a decision of which configuration to enable. It can only
enable one configuration at a time.
For example, It is possible to have a high power bus powered configuration and a
self powered configuration. If the device is plugged into a host with a mains power
supply, the device driver may choose to enable the high power bus powered
configuration enabling the device to be powered without a connection to the
mains, yet if it is connected to a laptop or personal organiser it could enable the
2nd configuration (self powered) requiring the user to plug your device into the
power point.
The configuration settings are not limited to power differences. Each configuration
could be powered in the same way and draw the same current, yet have different
interface or endpoint combinations. However it should be noted that changing the
configuration requires all activity on each endpoint to stop. While USB offers this
flexibility, very few devices have more than 1 configuration.
The interface descriptor could be seen as a header or grouping of the endpoints
into a functional group performing a single feature of the device. For example you
could have a multi-function fax/scanner/printer device. Interface descriptor one
could describe the endpoints of the fax function, Interface descriptor two the
scanner function and Interface descriptor three the printer function. Unlike the
configuration descriptor, there is no limitation as to having only one interface
enabled at a time. A device could have 1 or many interface descriptors enabled at
once.
Interface descriptors have a bInterfaceNumber field specifying the Interface
number and a bAlternateSetting which allows an interface to change settings on
the fly. For example we could have a device with two interfaces, interface one and
interface two. Interface one has bInterfaceNumber set to zero indicating it is the
first interface descriptor and a bAlternativeSetting of zero.
Interface two would have a bInterfaceNumber set to one indicating it is the
second interface and a bAlternativeSetting of zero (default). We could then
throw in another descriptor, also with a bInterfaceNumber set to one indicating it
is the second interface, but this time setting the bAlternativeSetting to one,
indicating this interface descriptor can be an alternative setting to that of the other
interface descriptor two.
When this configuration is enabled, the first two interface descriptors with
bAlternativeSettings equal to zero is used. However during operation the host
can send a SetInterface request directed to that of Interface one with a alternative
setting of one to enable the other interface descriptor.
This gives an advantage over having two configurations, in that we can be
transmitting data over interface zero while we change the endpoint settings
associated with interface one without effecting interface zero.
Each endpoint descriptor is used to specify the type of transfer, direction, polling
interval and maximum packet size for each endpoint. Endpoint zero, the default
control endpoint is always assumed to be a control endpoint and as such never has
a descriptor.

Composition of USB Descriptors


All descriptors are made up of a common format. The first byte specifies the length
of the descriptor, while the second byte indicates the descriptor type. If the length
of a descriptor is smaller than what the specification defines, then the host shall
ignore it. However if the size is greater than expected the host will ignore the extra
bytes and start looking for the next descriptor at the end of actual length returned.
Offset Field Size Value Description

0 bLength 1 Number Size of Descriptor in Bytes

1 bDescriptionType 1 Constant DescriptorType

2 ... n Start of parameters for descriptor

Device Descriptors
The device descriptor of a USB device represents the entire device. As a result a
USB device can only have one device descriptor. It specifies some basic, yet
important information about the device such as the supported USB version,
maximum packet size, vendor and product IDs and the number of possible
configurations the device can have. The format of the device descriptor is shown
below.
Offset Field Size Value Description

0 bLength 1 Number Size of the Descriptor in Bytes (18 bytes)


1 bDescriptorType 1 Constant Device Descriptor (0x01)

2 bcdUSB 2 BCD USB Specification Number which device


complies too.

4 bDeviceClass 1 Class Class Code (Assigned by USB Org)

If equal to Zero, each interface specifies it’s own


class code

If equal to 0xFF, the class code is vendor


specified.

Otherwise field is valid Class Code.

5 bDeviceSubClass 1 SubClass Subclass Code (Assigned by USB Org)

6 bDeviceProtocol 1 Protocol Protocol Code (Assigned by USB Org)

7 bMaxPacketSize 1 Number Maximum Packet Size for Zero Endpoint. Valid


Sizes are 8, 16, 32, 64

8 idVendor 2 ID Vendor ID (Assigned by USB Org)

10 idProduct 2 ID Product ID (Assigned by Manufacturer)

12 bcdDevice 2 BCD Device Release Number

14 iManufacturer 1 Index Index of Manufacturer String Descriptor

15 iProduct 1 Index Index of Product String Descriptor

16 iSerialNumber 1 Index Index of Serial Number String Descriptor

17 bNumConfigurations 1 Integer Number of Possible Configurations

o The bcdUSB field reports the highest version of USB the device supports.
The value is in binary coded decimal with a format of 0xJJMN where JJ is the
major version number, M is the minor version number and N is the sub minor
version number. e.g. USB 2.0 is reported as 0x0200, USB 1.1 as 0x0110 and
USB 1.0 as 0x0100.
o The bDeviceClass, bDeviceSubClass and bDeviceProtocol are used by
the operating system to find a class driver for your device. Typically only the
bDeviceClass is set at the device level. Most class specifications choose to
identify itself at the interface level and as a result set the bDeviceClass as
0x00. This allows for the one device to support multiple classes.
o The bMaxPacketSize field reports the maximum packet size for endpoint
zero. All devices must support endpoint zero.
o The idVendor and idProduct are used by the operating system to find a
driver for your device. The Vendor ID is assigned by the USB-IF.
o The bcdDevice has the same format than the bcdUSB and is used to
provide a device version number. This value is assigned by the developer.
o Three string descriptors exist to provide details of the manufacturer, product
and serial number. There is no requirement to have string descriptors. If no
string descriptor is present, a index of zero should be used.
o bNumConfigurations defines the number of configurations the device
supports at its current speed.

Configuration Descriptors
A USB device can have several different configurations although the majority of
devices are simple and only have one. The configuration descriptor specifies how
the device is powered, what the maximum power consumption is, the number of
interfaces it has. Therefore it is possible to have two configurations, one for when
the device is bus powered and another when it is mains powered. As this is a
"header" to the Interface descriptors, its also feasible to have one configuration
using a different transfer mode to that of another configuration.
Once all the configurations have been examined by the host, the host will send a
SetConfiguration command with a non zero value which matches the
bConfigurationValue of one of the configurations. This is used to select the desired
configuration.
Offset Field Size Value Description

0 bLength 1 Number Size of Descriptor in Bytes

1 bDescriptorType 1 Constant Configuration Descriptor (0x02)

2 wTotalLength 2 Number Total length in bytes of data returned

4 bNumInterfaces 1 Number Number of Interfaces

5 bConfigurationValue 1 Number Value to use as an argument to select this


configuration

6 iConfiguration 1 Index Index of String Descriptor describing this


configuration

7 bmAttributes 1 Bitmap D7 Reserved, set to 1. (USB 1.0 Bus Powered)

D6 Self Powered

D5 Remote Wakeup

D4..0 Reserved, set to 0.

8 bMaxPower 1 mA Maximum Power Consumption in 2mA units

o When the configuration descriptor is read, it returns the entire configuration


hierarchy which includes all related interface and endpoint descriptors. The
wTotalLength field reflects the number of bytes in the hierarchy.
o bNumInterfaces specifies the number of interfaces present for this
configuration.
o bConfigurationValue is used by the SetConfiguration request to select this
configuration.
o iConfiguration is a index to a string descriptor describing the configuration
in human readable form.
o bmAttributes specify power parameters for the configuration. If a device is
self powered, it sets D6. Bit D7 was used in USB 1.0 to indicate a bus
powered device, but this is now done by bMaxPower. If a device uses any
power from the bus, whether it be as a bus powered device or as a self
powered device, it must report its power consumption in bMaxPower.
Devices can also support remote wakeup which allows the device to wake up
the host when the host is in suspend.
o bMaxPower defines the maximum power the device will drain from the bus.
This is in 2mA units, thus a maximum of approximately 500mA can be
specified. The specification allows a high powered bus powered device to
drain no more than 500mA from Vbus. If a device loses external power, then
it must not drain more than indicated in bMaxPower. It should fail any
operation it cannot perform without external power.

Interface Descriptors
The interface descriptor could be seen as a header or grouping of the endpoints
into a functional group performing a single feature of the device. The interface
descriptor conforms to the following format,
Offset Field Size Value Description
0 bLength 1 Number Size of Descriptor in Bytes (9 Bytes)

1 bDescriptorType 1 Constant Interface Descriptor (0x04)

2 bInterfaceNumber 1 Number Number of Interface

3 bAlternateSetting 1 Number Value used to select alternative setting

4 bNumEndpoints 1 Number Number of Endpoints used for this interface

5 bInterfaceClass 1 Class Class Code (Assigned by USB Org)

6 bInterfaceSubClass 1 SubClass Subclass Code (Assigned by USB Org)

7 bInterfaceProtocol 1 Protocol Protocol Code (Assigned by USB Org)

8 iInterface 1 Index Index of String Descriptor Describing this interface

o bInterfaceNumber indicates the index of the interface descriptor. This


should be zero based, and incremented once for each new interface
descriptor.
o bAlternativeSetting can be used to specify alternative interfaces. These
alternative interfaces can be selected with the Set Interface request.
o bNumEndpoints indicates the number of endpoints used by the interface.
This value should exclude endpoint zero and is used to indicate the number
of endpoint descriptors to follow.
o bInterfaceClass, bInterfaceSubClass and bInterfaceProtocol can be
used to specify supported classes (e.g. HID, communications, mass storage
etc.) This allows many devices to use class drivers preventing the need to
write specific drivers for your device.
o iInterface allows for a string description of the interface.

Endpoint Descriptors
Endpoint descriptors are used to describe endpoints other than endpoint zero.
Endpoint zero is always assumed to be a control endpoint and is configured before
any descriptors are even requested. The host will use the information returned
from these descriptors to determine the bandwidth requirements of the bus.
Offset Field Size Value Description

0 bLength 1 Number Size of Descriptor in Bytes (7 bytes)

1 bDescriptorType 1 Constant Endpoint Descriptor (0x05)

2 bEndpointAddress 1 Endpoint Endpoint Address


Bits 0..3b Endpoint Number.
Bits 4..6b Reserved. Set to Zero
Bits 7 Direction 0 = Out, 1 = In (Ignored for Control
Endpoints)

3 bmAttributes 1 Bitmap Bits 0..1 Transfer Type


00 = Control
01 = Isochronous
10 = Bulk
11 = Interrupt
Bits 2..7 are reserved. If Isochronous endpoint,
Bits 3..2 = Synchronisation Type (Iso Mode)
00 = No Synchonisation
01 = Asynchronous
10 = Adaptive
11 = Synchronous
Bits 5..4 = Usage Type (Iso Mode)
00 = Data Endpoint
01 = Feedback Endpoint
10 = Explicit Feedback Data Endpoint
11 = Reserved

4 wMaxPacketSize 2 Number Maximum Packet Size this endpoint is capable of


sending or receiving

6 bInterval 1 Number Interval for polling endpoint data transfers. Value in


frame counts. Ignored for Bulk & Control
Endpoints. Isochronous must equal 1 and field may
range from 1 to 255 for interrupt endpoints.

o bEndpointAddress indicates what endpoint this descriptor is describing.


o bmAttributes specifies the transfer type. This can either be Control,
Interrupt, Isochronous or Bulk Transfers. If an Isochronous endpoint is
specified, additional attributes can be selected such as the Synchronisation
and usage types.
o wMaxPacketSize indicates the maximum payload size for this endpoint.
o bInterval is used to specify the polling interval of certain transfers. The
units are expressed in frames, thus this equates to either 1ms for low/full
speed devices and 125us for high speed devices.

String Descriptors
String descriptors provide human readable information and are optional. If they are
not used, any string index fields of descriptors must be set to zero indicating there
is no string descriptor available.
The strings are encoded in the Unicode format and products can be made to
support multiple languages. String Index 0 should return a list of supported
languages. A list of USB Language IDs can be found in Universal Serial Bus
Language Identifiers (LANGIDs) version 1.0
Offset Field Size Value Description

0 bLength 1 Number Size of Descriptor in Bytes

1 bDescriptorType 1 Constant String Descriptor (0x03)

2 wLANGID[0] 2 number Supported Language Code Zero


(e.g. 0x0409 English - United States)

4 wLANGID[1] 2 number Supported Language Code One


(e.g. 0x0c09 English - Australian)

n wLANGID[x] 2 number Supported Language Code x


(e.g. 0x0407 German - Standard)
The above String Descriptor shows the format of String Descriptor Zero. The host
should read this descriptor to determine what languages are available. If a
language is supported, it can then be referenced by sending the language ID in the
wIndex field of a Get Descriptor(String) request.
All subsequent strings take on the format below,
Offset Field Size Value Description

0 bLength 1 Number Size of Descriptor in Bytes

1 bDescriptorType 1 Constant String Descriptor (0x03)

2 bString n Unicode Unicode Encoded String

The Setup Packet


Every USB device must respond to setup packets on the default pipe. The setup
packets are used for detection and configuration of the device and carry out
common functions such as setting the USB device’s address, requesting a device
descriptor or checking the status of a endpoint.
A USB compliant Host expects all requests to be processed within a maximum
period of 5 seconds. It also specifies stricter timing for specific requests :

o Standard Device requests without a data stage must be completed in 50ms.


o Standard Device requests with a data stage must start to return data 500ms
after the request.
 Each data packet must be sent within 500ms of the successful transmission of the
previous packet.
 The status stage must complete within 50ms after the transmission of the last data packet.
o The SetAddress command (which contains a data phase) must process the
command and return status within 50ms. The device then has 2ms to change
address before the next request is sent.

These timeout periods are quite acceptable for even the slowest of devices, but
can be a restriction during debugging. 50mS doesn't provide for many debugging
characters to be sent at 9600bps on an asynchronous serial port or for a In Circuit
Debugger/Emulator to single step or to break execution to examine the internal
Registers. As a result, USB requires some different debugging methods to that of
other microcontroller projects.
Casually reading through the XP DDK, one may note the Host Controller
Driver now has a USBUSER_OP_SEND_ONE_PACKET command which is
commented to read "This API is used to implement the 'single step' USB
transaction development tool." While such a tool has not been released yet,
we can only hope to see one soon.
Each request starts with a 8 byte long Setup Packet which has the following
format,
Offset Field Size Value Description

0 bmRequestType 1 Bit-Map D7 Data Phase Transfer Direction


0 = Host to Device
1 = Device to Host
D6..5 Type
0 = Standard
1 = Class
2 = Vendor
3 = Reserved
D4..0 Recipient
0 = Device
1 = Interface
2 = Endpoint
3 = Other
4..31 = Reserved

1 bRequest 1 Value Request

2 wValue 2 Value Value

Index or
4 wIndex 2 Index
Offset
Number of bytes to transfer if there is
6 wLength 2 Count a data phase
The bmRequestType field will determine the direction of the request, type of
request and designated recipient. The bRequest field determines the request
being made. The bmRequestType is normally parsed and execution is branched to
a number of handlers such as a Standard Device request handler, a Standard
Interface request handler, a Standard Endpoint request handler, a Class Device
request handler etc. How you parse the setup packet is entirely up to your
preference. Others may choose to parse the bRequest first and then determine the
type and recipient based on each request.
Standard requests are common to all USB devices and are detailed in the next
coming pages. Class requests are common to classes of drivers. For example, all
device conforming to the HID class will have a common set of class specific
requests. These will differ to a device conforming to the communications class and
differ again to that of a device conforming to the mass storage class.
And last of all is the vendor defined requests. These are requests which you as the
USB device designer can assign. These are normally different from device to
device, but this is all up to your implementation and imagination.
A common request can be directed to different recipients and based on the
recipient perform different functions. A GetStatus Standard request for example,
can be directed at the device, interface or endpoint. When directed to a device it
returns flags indicating the status of remote wakeup and if the device is self
powered. However if the same request is directed at the interface it always returns
zero, or should it be directed at an endpoint will return the halt flag for the
endpoint.
The wValue and wIndex fields allow parameters to be passed with the request.
wLength is used the specify the number of bytes to be transferred should there
be a data phase.

Standard Requests
Section 9.4 of the USB specification details the "Standard Device" requests
required to be implemented for every USB device. The standard provides a single
table grouping items by request. Considering most firmware will parse the setup
packet by recipient we will opt to break up the requests based by recipient for
easier examination and implementation.

Standard Device Requests


There are currently eight Standard Device requests, all of which are detailed in the
table below.
bmRequestType bRequest wValue wIndex wLength Data

1000 0000b GET_STATUS (0x00) Zero Zero Two Device Status

CLEAR_FEATURE Feature
0000 0000b Zero Zero None
(0x01) Selector

Feature
0000 0000b SET_FEATURE (0x03) Zero Zero None
Selector

Device
0000 0000b SET_ADDRESS (0x05) Zero Zero None
Address

Zero or
GET_DESCRIPTOR Descriptor Descriptor
1000 0000b Language Descriptor
(0x06) Type & Index Length
ID

Zero or
SET_DESCRIPTOR Descriptor Descriptor
0000 0000b Language Descriptor
(0x07) Type & Index Length
ID

GET_CONFIGURATION Configuration
1000 0000b Zero Zero 1
(0x08) Value

SET_CONFIGURATION Configuration
0000 0000b Zero Zero None
(0x09) Value

o The Get Status request directed at the device will return two bytes during
the data stage with the following format,
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

Remote Self
Reserved
Wakeup Powered
o If D0 is set, then this indicates the device is self powered. If clear, the device
is bus powered. If D1 is set, the device has remote wakeup enabled and can
wake the host up during suspend. The remote wakeup bit can be by the
SetFeature and ClearFeature requests with a feature selector of
DEVICE_REMOTE_WAKEUP (0x01)
o Clear Feature and Set Feature requests can be used to set boolean
features. When the designated recipient is the device, the only two feature
selectors available are DEVICE_REMOTE_WAKEUP and TEST_MODE. Test
mode allows the device to exhibit various conditions. These are further
documented in the USB Specification Revision 2.0.
o Set Address is used during enumeration to assign a unique address to the
USB device. The address is specified in wValue and can only be a maximum
of 127. This request is unique in that the device does not set its address until
after the completion of the status stage. (See Control Transfers.) All other
requests must complete before the status stage.
o Set Descriptor/Get Descriptor is used to return the specified descriptor in
wValue. A request for the configuration descriptor will return the device
descriptor and all interface and endpoint descriptors in the one request.
 Endpoint Descriptors cannot be accessed directly by a GetDescriptor/SetDescriptor
Request.
 Interface Descriptors cannot be accessed directly by a GetDescriptor/SetDescriptor
Request.
 String Descriptors include a Language ID in wIndex to allow for multiple language
support.
o Get Configuration/Set Configuration is used to request or set the current
device configuration. In the case of a Get Configuration request, a byte will
be returned during the data stage indicating the devices status. A zero value
means the device is not configured and a non-zero value indicates the
device is configured. Set Configuration is used to enable a device. It should
contain the value of bConfigurationValue of the desired configuration
descriptor in the lower byte of wValue to select which configuration to
enable.

Standard Interface Requests


The specification current defines five Standard Interface requests which are
detailed in the table below. Interestingly enough, only two requests do anything
intelligible.
bmRequestType bRequest wValue wIndex wLength Data

GET_STATUS Interface
1000 0001b Zero Interface Two
(0x00) Status

CLEAR_FEATURE Feature
0000 0001b Interface Zero None
(0x01) Selector

SET_FEATURE Feature
0000 0001b Interface Zero None
(0x03) Selector

GET_INTERFACE Alternate
1000 0001b Zero Interface One
(0x0A) Interface

SET_INTERFACE Alternative
0000 0001b Interface Zero None
(0x11) Setting

• wIndex is normally used to specify the referring interface for requests directed at
the interface. Its format is shown below.
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
Reserved Interface Number
• Get Status is used to return the status of the interface. Such a request to the
interface should return two bytes of 0x00, 0x00. (Both bytes are reserved for
future use)
• Clear Feature and Set Feature requests can be used to set boolean features.
When the designated recipient is the interface, the current USB Specification
Revision 2 specifies no interface features.
• Get Interface and Set Interface set the Alternative Interface setting which is
described in more detail under the Interface Descriptor.

Standard Endpoint Requests


Standard Endpoint requests come in the four varieties listed below.
bmRequestType bRequest wValue Windex wLength Data

GET_STATUS Endpoint
1000 0010b Zero Endpoint Two
(0x00) Status

CLEAR_FEATURE Feature
0000 0010b Endpoint Zero None
(0x01) Selector

SET_FEATURE Feature
0000 0010b Endpoint Zero None
(0x03) Selector

SYNCH_FRAME
1000 0010b Zero Endpoint Two FrameNumber
(0x12)

o The wIndex field is normally used to specify the referring endpoint and
direction for requests directed to an endpoint. Its format is shown below.
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

Reserved Dir Reserved Endpoint Number


o Get Status returns two bytes indicating the status (Halted/Stalled) of a
endpoint. The format of the two bytes returned is illustrated below.
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

Reserved Halt
o Clear Feature and Set Feature are used to set Endpoint Features. The
standard currently defines one endpoint feature selector, ENDPOINT_HALT
(0x00) which allows the host to stall and clear an endpoint. Only endpoints
other than the default endpoint is recommended to have this functionality.
o A Synch Frame request is used to report an endpoint synchronisation
frame.
Interfacing the Standard Parallel Port
Table of Contents
Introduction to Parallel Ports
Hardware Properties
Centronics?
Port Addresses
Software Registers - Standard Parallel Port (SPP)
Bi-directional Ports
Using the Parallel Port to Input 8 Bits
Nibble Mode
Using the Parallel Port's IRQ
Parallel Port Modes in BIOS
Parallel Port Modes and the ECP's Extended Control Register
PDF Version

Introduction to Parallel Ports


The Parallel Port is the most commonly used port for interfacing home made
projects. This port will allow the input of up to 9 bits or the output of 12 bits at any
one given time, thus requiring minimal external circuitry to implement many
simpler tasks. The port is composed of 4 control lines, 5 status lines and 8 data
lines. It's found commonly on the back of your PC as a D-Type 25 Pin female
connector. There may also be a D-Type 25 pin male connector. This will be a serial
RS-232 port and thus, is a totally incompatible port.

For more information on Serial RS-232 Ports See http://www.beyondlogic.org/serial/serial.htm

Newer Parallel Port’s are standardized under the IEEE 1284 standard first released
in 1994. This standard defines 5 modes of operation which are as follows,

1. Compatibility Mode.
2. Nibble Mode. (Protocol not Described in this Document)
3. Byte Mode. (Protocol not Described in this Document)
4. EPP Mode (Enhanced Parallel Port).
5. ECP Mode (Extended Capabilities Mode).

The aim was to design new drivers and devices which were compatible with each
other and also backwards compatible with the Standard Parallel Port (SPP).
Compatibility, Nibble & Byte modes use just the standard hardware available on
the original Parallel Port cards while EPP & ECP modes require additional hardware
which can run at faster speeds, while still being downwards compatible with the
Standard Parallel Port.
Compatibility mode or "Centronics Mode" as it is commonly known, can only send
data in the forward direction at a typical speed of 50 kbytes per second but can be
as high as 150+ kbytes a second. In order to receive data, you must change the
mode to either Nibble or Byte mode. Nibble mode can input a nibble (4 bits) in the
reverse direction. E.g. from device to computer. Byte mode uses the Parallel's bi-
directional feature (found only on some cards) to input a byte (8 bits) of data in the
reverse direction.

Extended and Enhanced Parallel Ports use additional hardware to generate and manage handshaking. To
output a byte to a printer (or anything in that matter) using compatibility mode, the software must,
1. Write the byte to the Data Port.
2. Check to see is the printer is busy. If the printer is busy, it will not accept any data, thus any
data which is written will be lost.
3. Take the Strobe (Pin 1) low. This tells the printer that there is the correct data on the data
lines. (Pins 2-9)
4. Put the strobe high again after waiting approximately 5 microseconds after putting the strobe
low. (Step 3)

This limits the speed at which the port can run at. The EPP & ECP ports get around
this by letting the hardware check to see if the printer is busy and generate a
strobe and /or appropriate handshaking. This means only one I/O instruction need
to be performed, thus increasing the speed. These ports can output at around 1-2
megabytes per second. The ECP port also has the advantage of using DMA
channels and FIFO buffers, thus data can be shifted around without using I/O
instructions.

Hardware Properties
Below is a table of the "Pin Outs" of the D-Type 25 Pin connector and the
Centronics 34 Pin connector. The D-Type 25 pin connector is the most common
connector found on the Parallel Port of the computer, while the Centronics
Connector is commonly found on printers. The IEEE 1284 standard however
specifies 3 different connectors for use with the Parallel Port. The first one, 1284
Type A is the D-Type 25 connector found on the back of most computers. The 2nd
is the 1284 Type B which is the 36 pin Centronics Connector found on most
printers.

IEEE 1284 Type C however, is a 36 conductor connector like the Centronics, but
smaller. This connector is claimed to have a better clip latch, better electrical
properties and is easier to assemble. It also contains two more pins for signals
which can be used to see whether the other device connected, has power. 1284
Type C connectors are recommended for new designs, so we can look forward on
seeing these new connectors in the near future.

Pin No (D- Pin No Direction Hardware


SPP Signal Register
Type 25) (Centronics) In/out Inverted
1 1 nStrobe In/Out Control Yes
2 2 Data 0 Out Data
3 3 Data 1 Out Data
4 4 Data 2 Out Data
5 5 Data 3 Out Data
6 6 Data 4 Out Data
7 7 Data 5 Out Data
8 8 Data 6 Out Data
9 9 Data 7 Out Data
10 10 nAck In Status
11 11 Busy In Status Yes
Paper-Out / Paper-
12 12 In Status
End
13 13 Select In Status
14 14 nAuto-Linefeed In/Out Control Yes
15 32 nError / nFault In Status
16 31 nInitialize In/Out Control
nSelect-Printer /
17 36 In/Out Control Yes
nSelect-In
18 - 25 19-30 Ground Gnd
Table 1. Pin Assignments of the D-Type 25 pin Parallel Port Connector.

The above table uses "n" in front of the signal name to denote that the signal is
active low. e.g. nError. If the printer has occurred an error then this line is low. This
line normally is high, should the printer be functioning correctly. The "Hardware
Inverted" means the signal is inverted by the Parallel card's hardware. Such an
example is the Busy line. If +5v (Logic 1) was applied to this pin and the status
register read, it would return back a 0 in Bit 7 of the Status Register.

The output of the Parallel Port is normally TTL logic levels. The voltage levels are
the easy part. The current you can sink and source varies from port to port. Most
Parallel Ports implemented in ASIC, can sink and source around 12mA. However
these are just some of the figures taken from Data sheets, Sink/Source 6mA,
Source 12mA/Sink 20mA, Sink 16mA/Source 4mA, Sink/Source 12mA. As you can
see they vary quite a bit. The best bet is to use a buffer, so the least current is
drawn from the Parallel Port.

Centronics?
Centronics is an early standard for transferring data from a host to the printer. The
majority of printers use this handshake. This handshake is normally implemented
using a Standard Parallel Port under software control. Below is a simplified diagram
of the `Centronics' Protocol.
Data is first applied on the Parallel Port pins 2 to 7. The host then checks to see if
the printer is busy. i.e. the busy line should be low. The program then asserts the
strobe, waits a minimum of 1uS, and then de-asserts the strobe. Data is normally
read by the printer/peripheral on the rising edge of the strobe. The printer will
indicate that it is busy processing data via the Busy line. Once the printer has
accepted data, it will acknowledge the byte by a negative pulse about 5uS on the
nAck line.

Quite often the host will ignore the nAck line to save time. Latter in the Extended
Capabilities Port, you will see a Fast Centronics Mode, which lets the hardware do
all the handshaking for you. All the programmer must do is write the byte of data
to the I/O port. The hardware will check to see if the printer is busy, generate the
strobe. Note that this mode commonly doesn't check the nAck either.

Port Addresses
The Parallel Port has three commonly used base addresses. These are listed in
table 2, below. The 3BCh base address was originally introduced used for Parallel
Ports on early Video Cards. This address then disappeared for a while, when
Parallel Ports were later removed from Video Cards. They has now reappeared as
an option for Parallel Ports integrated onto motherboards, upon which their
configuration can be changed using BIOS.

LPT1 is normally assigned base address 378h, while LPT2 is assigned 278h.
However this may not always be the case as explained later. 378h & 278h have
always been commonly used for Parallel Ports. The lower case h denotes that it is
in hexadecimal. These addresses may change from machine to machine.

Address Notes:
3BCh - 3BFh Used for Parallel Ports which were incorporated on to Video
Cards - Doesn't support ECP addresses
378h - 37Fh Usual Address For LPT 1
278h - 27Fh Usual Address For LPT 2
Table 2 Port Addresses

When the computer is first turned on, BIOS (Basic Input/Output System) will
determine the number of ports you have and assign device labels LPT1, LPT2 &
LPT3 to them. BIOS first looks at address 3BCh. If a Parallel Port is found here, it is
assigned as LPT1, then it searches at location 378h. If a Parallel card is found
there, it is assigned the next free device label. This would be LPT1 if a card wasn't
found at 3BCh or LPT2 if a card was found at 3BCh. The last port of call, is 278h
and follows the same procedure than the other two ports. Therefore it is possible
to have a LPT2 which is at 378h and not at the expected address 278h.

What can make this even confusing, is that some manufacturers of Parallel Port
Cards, have jumpers which allow you to set your Port to LPT1, LPT2, LPT3. Now
what address is LPT1? - On the majority of cards LPT1 is 378h, and LPT2, 278h, but
some will use 3BCh as LPT1, 378h as LPT1 and 278h as LPT2. Life wasn't meant to
be easy.

The assigned devices LPT1, LPT2 & LPT3 should not be a worry to people wishing
to interface devices to their PC's. Most of the time the base address is used to
interface the port rather than LPT1 etc. However should you want to find the
address of LPT1 or any of the Line PrinTer Devices, you can use a lookup table
provided by BIOS. When BIOS assigns addresses to your printer devices, it stores
the address at specific locations in memory, so we can find them.

Start Address Function


0000:0408 LPT1's Base Address
0000:040A LPT2's Base Address
0000:040C LPT3's Base Address
0000:040E LPT4's Base Address (Note 1)
Table 3 - LPT Addresses in the BIOS Data Area;

Note 1 : Address 0000:040E in the BIOS Data Area may be used as the Extended Bios Data Area in
PS/2 and newer Bioses.

The above table, table 3, shows the address at which we can find the Printer Port's
addresses in the BIOS Data Area. Each address will take up 2 bytes. The following
sample program in C, shows how you can read these locations to obtain the
addresses of your printer ports.

#include <stdio.h>
#include <dos.h>

void main(void)
{
unsigned int far *ptraddr; /* Pointer to location of Port Addresses */
unsigned int address; /* Address of Port */
int a;

ptraddr=(unsigned int far *)0x00000408;


for (a = 0; a < 3; a++)
{
address = *ptraddr;
if (address == 0)
printf("No port found for LPT%d \n",a+1);
else
printf("Address assigned to LPT%d is %Xh\n",a+1,address);
*ptraddr++;
}
}
Software Registers - Standard Parallel Port (SPP)
Offset Name Read/Write Bit No. Properties
Base + 0 Data Port Write (Note- Bit 7 Data 7
1) Bit 6 Data 6
Bit 5 Data 5
Bit 4 Data 4
Bit 3 Data 3
Bit 2 Data 2
Bit 1 Data 1
Bit 0 Data 0
Table 4 Data Port

Note 1 : If the Port is Bi-Directional then Read and Write Operations can be performed on the
Data Register.
The base address, usually called the Data Port or Data Register is simply used for outputting
data on the Parallel Port's data lines (Pins 2-9). This register is normally a write only port. If
you read from the port, you should get the last byte sent. However if your port is bi-directional,
you can receive data on this address. See Bi-directional Ports for more detail.
Offset Name Read/Write Bit No. Properties
Base + 1 Status Port Read Only Bit 7 Busy
Bit 6 Ack
Bit 5 Paper Out
Bit 4 Select In
Bit 3 Error
Bit 2 IRQ (Not)
Bit 1 Reserved
Bit 0 Reserved
Table 5 Status Port
The Status Port (base address + 1) is a read only port. Any data written to this port will be
ignored. The Status Port is made up of 5 input lines (Pins 10,11,12,13 & 15), a IRQ status
register and two reserved bits. Please note that Bit 7 (Busy) is a active low input. E.g. If bit 7
happens to show a logic 0, this means that there is +5v at pin 11. Likewise with Bit 2. (nIRQ) If
this bit shows a '1' then an interrupt has not occurred.
Offset Name Read/Write Bit No. Properties
Base + 2 Control Port Read/Write Bit 7 Unused
Bit 6 Unused
Bit 5 Enable Bi-Directional Port
Bit 4 Enable IRQ Via Ack Line
Bit 3 Select Printer
Bit 2 Initialize Printer (Reset)
Bit 1 Auto Linefeed
Bit 0 Strobe
Table 6 Control Port
The Control Port (base address + 2) was intended as a write only port. When a printer is
attached to the Parallel Port, four "controls" are used. These are Strobe, Auto Linefeed,
Initialize and Select Printer, all of which are inverted except Initialize.
The printer would not send a signal to initialize the computer, nor would it tell the computer to
use auto linefeed. However these four outputs can also be used for inputs. If the computer has
placed a pin high (e.g. +5v) and your device wanted to take it low, you would effectively short
out the port, causing a conflict on that pin. Therefore these lines are "open collector" outputs
(or open drain for CMOS devices). This means that it has two states. A low state (0v) and a
high impedance state (open circuit).
Normally the Printer Card will have internal pull-up resistors, but as you would expect, not all
will. Some may just have open collector outputs, while others may even have normal totem
pole outputs. In order to make your device work correctly on as many Printer Ports as possible,
you can use an external resistor as well. Should you already have an internal resistor, then it
will act in Parallel with it, or if you have Totem pole outputs, the resistor will act as a load.
An external 4.7k resistor can be used to pull the pin high. I wouldn't use anything lower, just in
case you do have an internal pull up resistor, as the external resistor would act in parallel
giving effectively, a lower value pull up resistor. When in high impedance state the pin on the
Parallel Port is high (+5v). When in this state, your external device can pull the pin low and
have the control port change read a different value. This way the 4 pins of the Control Port can
be used for bi-directional data transfer. However the Control Port must be set to xxxx0100 to
be able to read data, that is all pins to be +5v at the port so that you can pull it down to GND
(logic 0).
Bits 4 & 5 are internal controls. Bit four will enable the IRQ (See Using the Parallel Ports IRQ)
and Bit 5 will enable the bi-directional port meaning that you can input 8 bits using (DATA0-7).
This mode is only possible if your card supports it. Bits 6 & 7 are reserved. Any writes to these
two bits will be ignored.
Bi-directional Ports
The schematic diagram below, shows a simplified view of the Parallel Port's Data Register.
The original Parallel Port card's implemented 74LS logic. These days all this is crammed into
one ASIC, but the theory of operation is still the same.
The non bi-directional ports were manufactured with the 74LS374's output enable tied
permanent low, thus the data port is always output only. When you read the Parallel Port's
data register, the data comes from the 74LS374 which is also connected to the data pins. Now
if you can overdrive the '374 you can effectively have a Bi-directional Port. (or a input only port,
once you blow up the latches output!)
What is very concerning is that people have actually done this. I've seen one circuit, a scope
connected to the Parallel Port distributed on the Internet. The author uses an ADC of some
type, but finds the ADC requires transistors on each data line, to make it work! No wonder why.
Others have had similar trouble, the 68HC11 cannot sink enough current (30 to 40mA!)
Bi-directional ports use Control Bit 5 connected to the 374's Output Enable so that it's output
drivers can be turned off. This way you can read data present on the Parallel Port's Data Pins,
without having bus conflicts and excessive current drains.
Bit 5 of the Control Port enables or disables the bi-directional function of the Parallel Port. This
is only available on true bi-directional ports. When this bit is set to one, pins 2 to 9 go into high
impedance state. Once in this state you can enter data on these lines and retrieve it from the
Data Port (base address). Any data which is written to the data port will be stored but will not
be available at the data pins. To turn off bi-directional mode, set bit 5 of the Control Port to '0'.
However not all ports behave in the same way. Other ports may require setting bit 6 of the
Control Port to enable Bi-directional mode and setting of Bit 5 to dis-enable Bi-directional
mode, Different manufacturers implement their bi-directional ports in different ways. If you wish
to use your Bi-directional port to input data, test it with a logic probe or multimeter first to make
sure it is in bi-directional mode.
Using The Parallel Port to Input 8 Bits.
If your Parallel Port doesn't support bi-directional mode, don't despair. You can input a
maximum of 9 bits at any one given time. To do this you can use the 5 input lines of the Status
Port and the 4 inputs (open collector) lines of the Control Port.
The inputs to the Parallel Port has be chosen as such, to make life easier for us. Busy just
happens to be the MSB (Bit 7) of the Status Port, then in ascending order comes Ack, Paper
Out and Select, making up the most significant nibble of the Control Port. The Bars are used to
represent which inputs are Hardware inverted, i.e. +5v will read 0 from the register, while GND
will read 1. The Status Port only has one inverted input.
The Control port is used to read the least significant nibble. As described before, the control
port has open collector outputs, i.e. two possible states, high impedance and GND. If we
connect our inputs directly to the port (For example an ADC0804 with totem pole outputs), a
conflict will result if the input is high and the port is trying to pull it down. Therefore we use
open collector inverters.
However this is not always entirely necessary. If we were connecting single pole switches to
the port with a pull up resistor, then there is no need to bother with this protection. Also if your
software initializes the control port with xxxx0100 so that all the pins on the control port are
high, then it may be unnecessary. If however you don't bother and your device is connected to
the Parallel Port before your software has a chance to initialize then you may encounter
problems.
Another problem to be aware of is the pull up resistors on the control port. The average pull-up
resistor is 4.7k. In order to pull the line low, your device will need to sink 1mA, which some low
powered devices may struggle to do. Now what happens if I suggest that some ports have 1K
pull up resistors? Yes, there are such cards. Your device now has to sink 5mA. More reason to
use the open collector inverters.
Open collector inverters were chosen over open collector buffers as they are more popular,
and thus easier to obtain. There is no reason, however why you can't use them. Another
possibility is to use transistors.
The input, D3 is connected via the inverter to Select Printer. Select Printer just happens to be
bit 3 of the control port. D2, D1 & D0 are connected to Init, Auto linefeed and strobe,
respectively to make up the lower nibble. Now this is done, all we have to do is assemble the
byte using software. The first thing we must do is to write xxxx0100 to the Control Port. This
places all the control port lines high, so they can be pulled down to input data.
outportb(CONTROL, inportb(CONTROL) & 0xF0 | 0x04);
Now that this is done, we can read the most significant nibble. This just happens to be the
most significant nibble of the status port. As we are only interested in the MSnibble we will
AND the results with 0xF0, so that the LSnibble is clear. Busy is hardware inverted, but we
won't worry about it now. Once the two bytes are constructed, we can kill two birds with one
stone by toggling Busy and Init at the same time.
a = (inportb(STATUS) & 0xF0); /* Read MSnibble */
We can now read the LSnibble. This just happens to be LSnibble of the control port - How
convenient! This time we are not interested with the MSnibble of the port, thus we AND the
result with 0x0F to clear the MSnibble. Once this is done, it is time to combine the two bytes
together. This is done by OR'ing the two bytes. This now leaves us with one byte, however we
are not finished yet. Bits 2 and 7 are inverted. This is overcome by XOR'ing the byte with 0x84,
which toggles the two bits.
a = a |(inportb(CONTROL) & 0x0F); /* Read LSnibble */
a = a ^ 0x84; /* Toggle Bit 2 & 7 */
Note: Some control ports are not open collector, but have totem pole outputs. This is
also the case with EPP and ECP Ports. Normally when you place a Parallel Port in ECP
or EPP mode, the control port becomes totem pole outputs only. Now what happens if
you connect your device to the Parallel Port in this mode? Therefore, in the interest of
portability I recommend using the next circuit, reading a nibble at a time.
Nibble Mode.
Nibble mode is the preferred way of reading 8 bits of data without placing the port in reverse
mode and using the data lines. Nibble mode uses a Quad 2 line to 1 line multiplexer to read a
nibble of data at a time. Then it "switches" to the other nibble and reads its. Software can then
be used to construct the two nibbles into a byte. The only disadvantage of this technique is that
it is slower. It now requires a few I/O instructions to read the one byte, and it requires the use
of an external IC.

The operation of the 74LS157, Quad 2 line to 1 line multiplexer is quite simple. It simply acts
as four switches. When the A/B input is low, the A inputs are selected. E.g. 1A passes through
to 1Y, 2A passes through to 2Y etc. When the A/B is high, the B inputs are selected. The Y
outputs are connected up to the Parallel Port's status port, in such a manner that it represents
the MSnibble of the status register. While this is not necessary, it makes the software easier.
To use this circuit, first we must initialize the multiplexer to switch either inputs A or B. We will
read the LSnibble first, thus we must place A/B low. The strobe is hardware inverted, thus we
must set Bit 0 of the control port to get a low on Pin 1.
outportb(CONTROL, inportb(CONTROL) | 0x01); /* Select Low Nibble (A)*/
Once the low nibble is selected, we can read the LSnibble from the Status Port. Take note that
the Busy Line is inverted, however we won't tackle it just yet. We are only interested in the
MSnibble of the result, thus we AND the result with 0xF0, to clear the LSnibble.
a = (inportb(STATUS) & 0xF0); /* Read Low Nibble */
Now it's time to shift the nibble we have just read to the LSnibble of variable a,
a = a >> 4; /* Shift Right 4 Bits */
We are now half way there. It's time to get the MSnibble, thus we must switch the multiplexer
to select inputs B. Then we can read the MSnibble and put the two nibbles together to make a
byte,
outportb(CONTROL, inportb(CONTROL) & 0xFE); /* Select High Nibble (B)*/
a = a |(inportb(STATUS) & 0xF0); /* Read High Nibble */
byte = byte ^ 0x88;
The last line toggles two inverted bits which were read in on the Busy line. It may be necessary
to add delays in the process, if the incorrect results are being returned.
Using the Parallel Port's IRQ
The Parallel Port's interrupt request is not used for printing under DOS or Windows. Early
versions of OS-2 used them, but don't anymore. Interrupts are good when interfacing
monitoring devices such as high temp alarms etc, where you don't know when it is going to be
activated. It's more efficient to have an interrupt request rather than have the software poll the
ports regularly to see if something has changed. This is even more noticeable if you are using
your computer for other tasks, such as with a multitasking operating system.
The Parallel Port's interrupt request is normally IRQ5 or IRQ7 but may be something else if
these are in use. It may also be possible that the interrupts are totally disabled on the card, if
the card was only used for printing. The Parallel Port interrupt can be disabled and enabled
using bit 4 of the control register, Enable IRQ Via Ack Line. Once enabled, an interrupt will
occur upon a low to high transition (rising edge) of the nACK. However like always, some
cards may trigger the interrupt on the high to low transition.
The following code is an Interrupt Polarity Tester, which serves as two things. It will determine
which polarity your Parallel Port interrupt is, while also giving you an example for how to use
the Parallel Port's Interrupt. It checks if your interrupt is generated on the rising or falling edge
of the nACK line. To use the program simply wire one of the Data lines (Pins 2 to 9) to the Ack
Pin (Pin 10). The easiest way to do this is to bridge some solder from DATA7 (Pin 9) to ACK
(Pin 10) on a male DB25 connector.

/* Parallel Port Interrupt Polarity Tester */


/* 2nd February 1998 */
/* Copyright 1997 Craig Peacock */
/* WWW - http://www.beyondlogic.org */
/* Email - cpeacock@senet.com.au */

#include <dos.h>

#define PORTADDRESS 0x378 /* Enter Your Port Address Here */


#define IRQ 7 /* IRQ Here */

#define DATA PORTADDRESS+0


#define STATUS PORTADDRESS+1
#define CONTROL PORTADDRESS+2

#define PIC1 0x20


#define PIC2 0xA0

int interflag; /* Interrupt Flag */


int picaddr; /* Programmable Interrupt Controller (PIC) Base Address */

void interrupt (*oldhandler)();

void interrupt parisr() /* Interrupt Service Routine (ISR) */


{
interflag = 1;
outportb(picaddr,0x20); /* End of Interrupt (EOI) */
}

void main(void)
{
int c;
int intno; /* Interrupt Vector Number */
int picmask; /* PIC's Mask */

/* Calculate Interrupt Vector, PIC Addr & Mask. */

if (IRQ >= 2 && IRQ <= 7) {


intno = IRQ + 0x08;
picaddr = PIC1;
picmask = 1;
picmask = picmask << IRQ;
}
if (IRQ >= 8 && IRQ <= 15) {
intno = IRQ + 0x68;
picaddr = PIC2;
picmask = 1;
picmask = picmask << (IRQ-8);
}
if (IRQ < 2 || IRQ > 15)
{
printf("IRQ Out of Range\n");
exit();
}

outportb(CONTROL, inportb(CONTROL) & 0xDF); /* Make sure port is in Forward Direction */


outportb(DATA,0xFF);
oldhandler = getvect(intno); /* Save Old Interrupt Vector */
setvect(intno, parisr); /* Set New Interrupt Vector Entry */
outportb(picaddr+1,inportb(picaddr+1) & (0xFF - picmask)); /* Un-Mask Pic */
outportb(CONTROL, inportb(CONTROL) | 0x10); /* Enable Parallel Port IRQ's */

clrscr();
printf("Parallel Port Interrupt Polarity Tester\n");
printf("IRQ %d : INTNO %02X : PIC Addr 0x%X : Mask 0x
%02X\n",IRQ,intno,picaddr,picmask);
interflag = 0; /* Reset Interrupt Flag */
delay(10);
outportb(DATA,0x00); /* High to Low Transition */
delay(10); /* Wait */
if (interflag == 1) printf("Interrupts Occur on High to Low Transition of ACK.\n");
else
{
outportb(DATA,0xFF); /* Low to High Transition */
delay(10); /* wait */
if (interflag == 1) printf("Interrupts Occur on Low to High Transition of ACK.\n");
else printf("No Interrupt Activity Occurred. \nCheck IRQ Number, Port Address and
Wiring.");
}

outportb(CONTROL, inportb(CONTROL) & 0xEF); /* Disable Parallel Port IRQ's */


outportb(picaddr+1,inportb(picaddr+1) | picmask); /* Mask Pic */
setvect(intno, oldhandler); /* Restore old Interrupt Vector Before Exit */
}
At compile time, the above source may generate a few warnings, condition always true,
condition always false, unreachable code etc. These are perfectly O.K. They are generated as
some of the condition structures test which IRQ you are using, and as the IRQ is defined as a
constant some outcomes will never change. While they would of been better implemented as a
preprocessor directive, I've done this so you can cut and paste the source code in your own
programs which may use command line arguments, user input etc instead of a defined IRQ.
To understand how this example works, the reader must have an assumed knowledge and
understanding of Interrupts and Interrupt Service Routines (ISR). If not, See Interfacing the PC
: Using Interrupts for a quick introduction.
The first part of the mainline routine calculates the Interrupt Vector, PIC Addr & Mask in order
to use the Parallel Port's Interrupt Facility. After the Interrupt Service Routine (ISR) has been
set up and the Programmable Interrupt Controller (PIC) set, we must enable the interrupt on
the Parallel Port. This is done by setting bit 4 of the Parallel Port's Control Register using
outportb(CONTROL,inportb(CONTROL) | 0x10);
Before enabling the interrupts, we wrote 0xFF to the Parallel Port to enable the 8 data lines
into a known state. At this point of the program, all the data lines should be high. The interrupt
service routine simply sets a flag (interflag), thus we can determine when an IRQ occurs. We
are now in a position to write 0x00 to the data port, which causes a high to low transition on the
Parallel Port's Acknowledge line as it's connected to one of the data lines.
If the interrupt occurs on the high to low transition, the interrupt flag (interflag) should be set.
We now test this, and if this is so the program informs the user. However if it is not set, then an
interrupt has not yet occurred. We now write 0xFF to the data port, which will cause a low to
high transition on the nAck line and check the interrupt flag again. If set, then the interrupt
occurs on the low to high transition.
However if the interrupt flag is still reset, then this would suggest that the interrupts are not
working. Make sure your IRQ and Base Address is correct and also check the wiring of the
plug.
Parallel Port Modes in BIOS
Today, most Parallel Ports are mulimode ports. They are normally software configurable to one
of many modes from BIOS. The typical modes are,
Printer Mode (Sometimes called Default or Normal Modes)
Standard & Bi-directional (SPP) Mode
EPP1.7 and SPP Mode
EPP1.9 and SPP Mode
ECP Mode
ECP and EPP1.7 Mode
ECP and EPP1.9 Mode
Printer Mode is the most basic mode. It is a Standard Parallel Port in forward mode only. It has
no bi-directional feature, thus Bit 5 of the Control Port will not respond. Standard & Bi-
directional (SPP) Mode is the bi-directional mode. Using this mode, bit 5 of the Control Port will
reverse the direction of the port, so you can read back a value on the data lines.
EPP1.7 and SPP Mode is a combination of EPP 1.7 (Enhanced Parallel Port) and SPP Modes.
In this mode of operation you will have access to the SPP registers (Data, Status and Control)
and access to the EPP Registers. In this mode you should be able to reverse the direction of
the port using bit 5 of the control register. EPP 1.7 is the earlier version of EPP. This version,
version 1.7, may not have the time-out bit. See Interfacing the Enhanced Parallel Port for more
information.
EPP1.9 and SPP Mode is just like the previous mode, only it uses EPP Version 1.9 this time.
As in the other mode, you will have access to the SPP registers, including Bit 5 of the control
port. However this differs from EPP1.7 and SPP Mode as you should have access to the EPP
Timeout bit.
ECP Mode will give you an Extended Capabilities Port. The mode of this port can then be set
using the ECP's Extended Control Register (ECR). However in this mode from BIOS the EPP
Mode (100) will not be available. We will further discuss the ECP's Extended Control Register
in this document, but if you want further information on the ECP port, consult Interfacing the
Extended Capabilities Port.
ECP and EPP1.7 Mode and ECP and EPP1.9 Mode will give you an Extended Capabilities
Port, just like the previous mode. However the EPP Mode in the ECP's ECR will now be
available. Should you be in ECP and EPP1.7 Mode you will get an EPP1.7 Port, or if you are in
ECP and EPP1.9 Mode, an EPP1.9 Port will be at your disposal.
The above modes are configurable via BIOS. You can reconfigure them by using your own
software, but this is not recommended. These software registers, typically found at 0x2FA,
0x3F0, 0x3F1 etc are only intended to be accessed by BIOS. There is no set standard for
these configuration registers, thus if you were to use these registers, your software would not
be very portable. With today's multitasking operating systems, its also not a good idea to
change them when it suits you.
A better option is to select ECP and EPP1.7 Mode or ECP and EPP1.9 Mode from BIOS and
then use the ECP's Extended Control Register to select your Parallel Port's Mode. The EPP1.7
mode had a few problems in regards to the Data and Address Strobes being asserted to start
a cycle regardless of the wait state, thus this mode if not typically used now. Best set your
Parallel Port to ECP and EPP1.9 Mode.
Parallel Port Modes and the ECP's Extended Control Register
As we have just discussed, it is better to set the Parallel Port to ECP and EPP1.9 Mode and
use the ECP's Extended Control Register to select different modes of operation. The ECP
Registers are standardized under Microsoft's Extended Capabilities Port Protocol and ISA
Interface Standard, thus we don't have that problem of every vendor having their own register
set.
When set to ECP Mode, a new set of registers become available at Base + 0x400h. A
discussion of these registers are available in Interfacing the Extended Capabilities Port. Here
we are only interested in the Extended Control Register (ECR) which is mapped at Base +
0x402h. It should be stated that the ECP's registers are not available for port's with a base
address of 0x3BCh.
Bit Function
7:5 Selects Current Mode of Operation
000 Standard Mode
001 Byte Mode
010 Parallel Port FIFO Mode
011 ECP FIFO Mode
100 EPP Mode
101 Reserved
110 FIFO Test Mode
111 Configuration Mode
4 ECP Interrupt Bit
3 DMA Enable Bit
2 ECP Service Bit
1 FIFO Full
0 FIFO Empty
Table 7 - Extended Control Register (ECR)
The table above is of the Extended Control Register. We are only interested in the three MSB
of the Extended Control Register which selects the mode of operation. There are 7 possible
modes of operation, but not all ports will support all modes. The EPP mode is one such
example, not being available on some ports.
Modes of Operation
Standard Mode Selecting this mode will cause the ECP port to behave as a Standard Parallel Port,
without bi-directional functionality.
Byte Mode / Behaves as a SPP in bi-directional mode. Bit 5 will place the port in reverse mode.
PS/2 Mode
Parallel Port In this mode, any data written to the Data FIFO will be sent to the peripheral using
FIFO Mode the SPP Handshake. The hardware will generate the handshaking required. Useful
with non-ECP devices such as Printers. You can have some of the features of ECP
like FIFO buffers and hardware generation of handshaking but with the existing
SPP handshake instead of the ECP Handshake.
ECP FIFO Standard mode for ECP use. This mode uses the ECP Handshake described in
Mode Interfacing the Extended Capabilities Port. - When in ECP Mode though BIOS, and
the ECR register is set to ECP FIFO Mode (011), the SPP registers may disappear.
EPP This will enable EPP Mode, if available. Under BIOS, if ECP mode is set then it's
Mode/Reserved more than likely, this mode is not an option. However if BIOS is set to ECP and
EPP1.x Mode, then EPP 1.x will be enabled. - Under Microsoft's Extended
Capabilities Port Protocol and ISA Interface Standard this mode is Vendor
Specified.
Reserved Currently Reserved. - Under Microsoft's Extended Capabilities Port Protocol and
ISA Interface Standard this mode is Vendor Specified.
FIFO Test While in this mode, any data written to the Test FIFO Register will be placed into
Mode the FIFO and any data read from the Test FIFO register will be read from the
FIFO buffer. The FIFO Full/Empty Status Bits will reflect their true value, thus
FIFO depth, among other things can be determined in this mode.
Configuration In this mode, the two configuration registers, cnfgA & cnfgB become available at
Mode their designated register addresses.
If you are in ECP Mode under BIOS, or if your card is jumpered to use ECP then it is a good
idea to initialize the mode of your ECP port to a pre-defined state before use. If you are using
SPP, then set the port to Standard Mode as the first thing you do. Don't assume that the port
will already be in Standard (SPP) mode.
Under some of the modes, the SPP registers may disappear or not work correctly. If you are
using SPP, then set the ECR to Standard Mode. This is one of the most common mistakes that
people make.

Interfacing the Enhanced Parallel Port


Table of Contents
Enhanced Parallel Port
EPP Hardware Properties
The EPP Handshake
EPP Data Write Cycle
EPP Address Write Cycle
EPP Data Read Cycle
EPP Address Read Cycle
The EPP's Software Registers
EPP Programming Considerations

EPP - Enhanced Parallel Port


The Enhanced Parallel Port (EPP) was designed in a joint venture between Intel,
Xircom & Zenith Data Systems. EPP Ports were first specified in the EPP 1.7
standard, and then later included in the IEEE 1284 Standard released in 1994. EPP
has two standards, EPP 1.7 and EPP 1.9. There are differences between the two
standards which may affect the operation of devices. This is further discussed
latter. EPP has a typical transfer rate in the order of 500KB/S to 2MB/S. This is
achieved by allowing the hardware contained in the port to generate handshaking,
strobing etc, rather that have the software do it, which was the case with
Centronics.

For the hobbyist, EPP is more commonly used than ECP. EPP differs from ECP by
the fact that the EPP Port generates and controls all the transfers to and from the
peripheral. ECP on the other hand requires the peripheral to negotiate a reverse
channel and control the handshaking. This is harder to achieve with common glue
logic, thus really requires a dedicated controller or ECP Peripheral Chip.

EPP Hardware Properties


When using EPP mode, a different set of tasks and labels are assigned to each line.
These are listed below in Table 4. It's very common to see both the SPP and EPP
names interchanged in Parallel Port Data Sheets and Literature. This can make it
very hard to focus on what is exactly happening. Therefore all the documentation
here will use the EPP names.

Pin SPP Signal EPP Signal IN/OUT Function


A low on this line indicates a Write, High
1 Strobe Write Out
indicates a Read
2-9 Data 0-7 Data 0-7 In-Out Data Bus. Bi-directional
Interrupt Line. Interrupt occurs on Positive
10 Ack Interrupt In
(Rising) Edge.
Used for handshaking. A EPP cycle can be
11 Busy Wait In
started when low, and finished when high.
Paper Out /
12 Spare In Spare - Not Used in EPP Handshake
End
13 Select Spare In Spare - Not Used in EPP Handshake
Auto
14 Data Strobe Out When Low, indicates Data transfer
Linefeed
15 Error / Fault Spare In Spare - Not used in EPP Handshake
16 Initialize Reset Out Reset - Active Low
Select Address
17 Out When low, indicates Address transfer
Printer Strobe
18-
Ground Ground GND Ground
25
Table 1. Pin Assignments For Enhanced Parallel Port Connector.

Paper Out, Select and Error are not defined in the EPP handshake. These lines can
be utilised in any way by the user. The status of these lines can be determined at
anytime by viewing the SPP Status Register. Unfortunately there are no spare
output's. This can become a hassle regularly.
The EPP Handshake
In order to perform a valid exchange of data using EPP we must follow the EPP
handshake. As the hardware does all the work, this handshake only requires to be
used for your hardware and not for software as the case with SPP. To initiate an
EPP cycle your software needs to perform only one I/O operation to the relevant
EPP Register. Details on this, latter.

EPP Data Write Cycle

1. Program writes to EPP Data Register.


(Base + 4)
2. nWrite is placed low. (Low indicates write
operation)
3. Data is placed on Data Lines 0-7.
4. nData Strobe is asserted if Wait is Low
(O.K. to start cycle)
5. Host waits for Acknowledgment by
nWait going high (O.K. to end cycle)
6. nData Strobe is de-asserted.
7. EPP Data Write Cycle Ends.

Figure 1. Enhanced Parallel Port Data Write Cycle.

EPP Address Write Cycle

1. Program writes address to EPP's


Address Register (Base + 3)
2. Write is placed low. (Low indicates write
operation)
3. Address is placed on Data Lines 0-7.
4. Address Strobe is asserted if Wait is Low
(O.K. to start cycle)
5. Host waits for Acknowledgment by wait
going high (O.K. to end cycle)
6. nAddress Strobe is De-asserted.
7. EPP Address Write Cycle Ends.

Figure 2. Enhanced Parallel Port Address Write Cycle.

EPP Data Read Cycle


1. Program reads EPP Data Register.
(Base + 4)
2. nData Strobe is asserted if Wait is Low
(O.K. to start cycle)
3. Host waits for Acknowledgment by
nWait going high
4. Data is read from Parallel Port Pins.
5. nData Strobe is de-asserted.
6. EPP Data Read Cycle Ends.

Figure 3. Enhanced Parallel Port Data Read Cycle.

EPP Address Read Cycle

1. Program reads EPP Address Register.


(Base + 3)
2. nAddr Strobe is asserted if Wait is Low
(O.K. to start cycle)
3. Host waits for Acknowledgment by
nWait going high
4. Data is read from Parallel Port Pins.
5. nAddr Strobe is de-asserted.
6. EPP Address Read Cycle Ends.

Figure 4. Enhanced Parallel Port Address Read Cycle.

Note If implementing EPP 1.7 Handshake (Pre IEEE 1284) the Data and Address Strobes can be asserted
to start a cycle regardless of the wait state. EPP 1.9 will only start a cycle once wait is low. Both
EPP 1.7 and EPP 1.9 require the wait to be high to finish a cycle.
The EPP's Software Registers.
The EPP Port also has a new set of registers. However 3 of them have been
inherited from the Standard Parallel Port. Below is a table showing the new and
existing registers.

Address Port Name Read/Write


Base + 0 Data Port (SPP) Write
Base + 1 Status Port (SPP) Read
Base + 2 Control Port (SPP) Write
Base + 3 Address Port (EPP) Read/Write
Base + 4 Data Port (EPP) Read/Write
Base + 5 Undefined (16/32bit Transfers) -
Base + 6 Undefined (32bit Transfers) -
Base + 7 Undefined (32bit Transfers) -
Table 2 EPP Registers

As you can see, the first 3 addresses are exactly the same than the Standard
Parallel Port Register and behave in exactly the same way. Therefore if you used a
Enhanced Parallel Port, you can output data to Base + 0 in exactly the same
fashion than you would if it was a Standard Parallel Port (SPP). If you were to
connect a printer, and use compatibility mode then you would have to check to see
if the port is busy and then assert & de-assert the strobe using the Control and
Status Port, then wait for the Ack.

If you wish to communicate with a EPP compatible device then all you have to do,
is place any data you wish to send in the EPP Data Register at Base + 4 and the
card will generate all the necessary handshaking required. Likewise if you wish to
send an address to your device, then you use the EPP Address Register at offset
+3.

Both the EPP Address Register and the EPP Data Register are read / write, thus to
read data from your device, you can use the same registers. However the EPP
Printer Card has to initiate a read Cycle as both the nData Strobe and nAddress
Strobe are outputs. Your device can signal a read request via the use of the
interrupt and have your ISR perform the Read Operation.

The Status Port has one little modification. Bit 0, which was reserved in the SPP
register set, now becomes the EPP Time-out Bit. This bit will be set when an EPP
time-out occurs. This happens when the nWait line is not deasserted within
approximately 10uS (depending upon the port) of the IOW or IOR line being
asserted. The IOW and IOR are the I/O Read and Write lines present on the ISA Bus.

The EPP mode is very depended of the ISA bus timing. When a read cycle is
performed, the port must undertake the appropriate Read/Write handshake and
return the data in that ISA cycle. Of course this doesn't occur within one ISA cycle,
thus the port uses the IOCHRDY (I/O Channel Ready) on the ISA bus to introduce
wait states, until the cycle completes. Now imagine if a EPP Read or Write is
started with no peripheral connected? The port never gets an acknowledgment
(nWait), thus keeps sending requests for wait states, and your computer locks up.
Therefore the EPP implements a type of watchdog, which times out after
approximately 10uS.

The three registers, Base + 5, Base + 6 and Base + 7 can be used for 16 and 32
bit read/write operations if your port supports it. This can further reduce your I/O
operations. The Parallel Port can only transport 8 bits at a time, thus any 32 or 16
bit word written to the Parallel Port will be split into byte size blocks and sent via
the Parallel Port's 8 data lines.
EPP Programming Considerations.
EPP only has two main registers and a Time-out Status Flag, What could there
possibly be to set up?

Before you can start any EPP cycles by reading and writing to the EPP Data and
Address Ports, the port must be configured correctly. In the idle state, an EPP port
should have it's nAddress Strobe, nData Strobe, nWrite and nReset lines inactive,
high. Some ports require you to set this up before starting any EPP Cycle.
Therefore our first task is to manually initialise these lines using the SPP Registers.
Writing XXXX0100 to the control port will do this.

On some cards, if the Parallel Port is placed in reverse mode, a EPP Write cycle
cannot be performed. Therefore it is also wise to place the Parallel Port in forward
mode before using EPP. Clearing Bit 5 of the Control Register should result in an
more enjoyable programming session, without tearing your hair out.

The EPP Timeout bit we have already discussed. When this bit is set, the EPP port
may not function correctly. A common scenario is always reading 0xFF from either
the Address or Data Cycles. This bit should be cleared for reliable operation, and
constantly checked.

Interfacing the Extended Capabilities Port


Table of Contents
Introduction to the Extended Capabilities Port
ECP Hardware Properties
The ECP Handshake
ECP Forward Data Cycle
ECP Forward Command Cycle
ECP Reverse Data Cycle
ECP Reverse Command Cycle
ECP Handshake vs SPP Handshake
RLE - Run Length Encoding
ECP Software Registers
ECP's Extended Control Register (ECR)
ECP's Configuration Register A (cnfgA)
ECP's Configuration Register B (cnfgB)

Introduction to the Extended Capabilities Port


The Extended Capabilities Mode was designed by Hewlett Packard and Microsoft to
be implemented as the Extended Capabilities Port Protocol and ISA Interface
Standard. This protocol uses additional hardware to generate handshaking signals
etc just like the EPP mode, thus runs at very much the same speed than the EPP
mode. This mode, however may work better under Windows as it can use DMA
channels to move it's data about. It also uses a FIFO buffer for the sending and/or
receiving of data.

Another feature of ECP is a real time data compression. It uses Run Length
Encoding (RLE) to achieve data compression ratio's up to 64:1. This comes is
useful with devices such as Scanners and Printers where a good part of the data is
long strings which are repetitive.

The Extended Capabilities Port supports a method of channel addressing. This is


not intended to be used to daisy chain devices up but rather to address multiple
devices within one device. Such an example is many fax machines on the market
today which may contain a Parallel Port to interface it to your computer. The fax
machine can be split up into separate devices such as the scanner, modem/Fax
and printer, where each part can be addresses separately, even if the other
devices cannot accept data due to full buffers.

ECP Hardware Properties


While Extended Capabilities Printer Ports use exactly the same D25 connector as
your SPP, ECP assigns different tasks to each of the pins, just like EPP. This means
that there is also a different handshake method when using a ECP interface.

The ECP is backwards compatible to the SPP and EPP. When operating in SPP
mode, the individual lines operate in exactly the same fashion than the SPP and
thus are labeled Strobe, Auto Linefeed, Init, Busy etc. When operating in EPP
mode, the pins function according to the method described in the EPP protocol and
have a different method of Handshaking. When the port is operating in ECP mode,
then the following labels are assigned to each pin.

SPP
Pin ECP Signal IN/OUT Function
Signal
A low on this line indicates, that there is valid data at the
1 Strobe HostCLK Out host. When this pin is de-asserted, the +ve clock edge
should be used to shift the data into the device.
2-9 Data 0-7 Data 0-7 In/Out Data Bus. Bi-directional
A low on this line indicates, that there is valid data at the
10 Ack PeriphCLK In Device. When this pin is de-asserted, the +ve clock edge
should be used to shift the data into the Host.
When in reverse direction a HIGH indicates Data, while
11 Busy PeriphAck In a LOW indicates a Command Cycle.
In forward direction, functions as PeriphAck.
Paper Out /
12 nAckReverse In When Low, Device acknowledges Reverse Request.
End
13 Select X-Flag In Extensibility Flag
When in forward direction a HIGH indicates Data, while
Auto
14 Host Ack Out a LOW indicates a Command Cycle.
Linefeed
In reverse direction, functions as HostAck.
15 Error / PeriphRequest In A LOW set by the device indicates reverse data is
Fault available
16 Initialize nReverseRequest Out A LOW indicates data is in reverse direction
Select A HIGH indicates Host is in 1284 Transfer Mode. Taken
17 1284 Active Out
Printer low to terminate.
18-
Ground Ground GND Ground
25
Table 1. Pin Assignments For Extended Capabilities Parallel Port Connector.

The HostAck and PeriphAck lines indicate whether the signals on the data line are
data or a command. If these lines are high then data is placed on the data lines
(Pins 2-7). If a command cycle is taking place then the appropriate line will be low,
ie if the host is sending a command, then HostAck will be low or if the
device/peripheral is sending a command the PeriphAck line will be low.

A command cycle can be one of two things, either a RLE count or an address. This
is determined by the bit 7 (MSB) of the data lines, ie Pin 9. If bit 7 is a 0, then the
rest of the data (bits 0-6) is a run length count which is used with the data
compression scheme. However if bit 7 is a 1, then the data present on bits 0 to 6 is
a channel address. With one bit missing this can only be a value from 0 to
127(DEC).

The ECP Handshake


The ECP handshake is different to the SPP handshake. The most obvious difference
is that ECP has the ability at anytime to transmit data in any direction, thus
additional signaling is required. Below is the ECP handshake for both the Forward
and Reverse Directions.

ECP Forward Data Cycle

1. Data is placed on Data lines by Host.


2. Host then indicates a Data Cycle will
proceed by asserting HostAck.
3. Host indicates valid data by asserting
HostClk low.
4. Peripheral sends its acknowledgment of
valid data by asserting PeriphAck.
5. Host de-asserts HostClk high. +ve edge
used to shift data into the Peripheral.
6. Peripheral sends it's acknowledgment of
the byte via de-asserting PeriphAck.
Figure 1. Enhanced Capabilities Port Forward Data Cycle.

ECP Forward Command Cycle


1. Data is placed on Data lines by Host.
2. Host then indicates a Command cycle
will proceed by de-asserting HostAck.
3. Host indicates valid data by asserting
HostClk low.
4. Peripheral sends its acknowledgment of
valid data by asserting PeriphAck.
5. Host de-asserts HostClk high. +ve edge
used to shift data into the Peripheral.
6. Peripheral sends it's acknowledgment of
the byte via de-asserting PeriphAck.
Figure 2. Enhanced Capabilities Port Forward Command Cycle.

ECP Reverse Data Cycle


1. Host sets
nReverseRequest Low to
request a reverse channel.
2. Peripheral acknowledges
reverse channel request via
asserting nAckReverse low.
3. Data is placed on data
lines by Peripheral.
4. Data cycle is then selected
by Peripheral via PeriphAck
going high.
5. Valid data is indicated by
the Peripheral setting
PeriphClk low.
6. Host sends its
acknowledgment of valid data
via HostAck going high.
7. Device/Peripheral sets
PeriphClk high. +ve edge
used to shift data into the
Host.
Figure 3. Enhanced Capabilities Port Reverse Data Cycle. 8. Host sends it's
acknowledgment of the byte
by de-asserting HostAck low.

ECP Reverse Command Cycle


1. Host sets nReverseRequest Low
to request a reverse channel.
2. Peripheral acknowledges reverse
channel request via asserting
nAckReverse low.
3. Data is placed on data lines by
Peripheral.
4. Command cycle is then selected by
Peripheral via PeriphAck going low.
5. Valid data is indicated by the
Peripheral setting PeriphClk low.
6. Host sends its acknowledgment of
valid data via HostAck going high.
7. Device/Peripheral sets PeriphClk
high. +ve edge used to shift data
into the Host.
8. Host sends it's acknowledgment of
the byte by de-asserting HostAck low.
Figure 4. Enhanced Capabilities Port Reverse Command Cycle.

ECP Handshake vs SPP Handshake


If we look back at the SPP Handshake you will realize it only has 5 steps,

1. Write the byte to the Data Port.


2. Check to see is the printer is busy. If the printer is busy, it will not accept any data, thus any
data which is written will be lost.
3. Take the Strobe (Pin 1) low. This tells the printer that there is the correct data on the data
lines. (Pins 2-9)
4. Put the strobe high again after waiting approximately 5 microseconds after putting the strobe
low. (Step 3)
5. Check for Ack from Peripheral.

and that the ECP handshake has many more steps. This would suggest that ECP
would be slower that SPP. However this is not the case as all of these steps above
are controlled by the hardware on your I/O control. If this handshake was
implemented via software control then it would be a lot slower that it's SPP
counterpart.

RLE - Run Length Encoding


As briefly discussed earlier, the ECP Protocol includes a Simple Compression
Scheme called Run Length Encoding. It can support a maximum compression ratio
of 64:1 and works by sending repetitive single bytes as a run count and one copy
of the byte. The run count determines how many times the following byte is to be
repeated.

For example, if a string of 25 'A's were to be sent, then a run count byte equal to
24 would be sent first, followed by the byte 'A'. The receiving peripheral on receipt
of the Run Length Count, would expand (Repeat) the next byte a number of times
determined via the run count.

The Run Length Byte has to be distinguished from other bytes in the Data Path. It
is sent as a Command to the ECP's Address FIFO Port. Bytes sent to this register
can be of two things, a Run Length Count or an Address. These are distinguished
by the MSB, Bit 7. If Bit 7 is Set (1), then the other 7 bits, bits 0 to 6 is a channel
address. If Bit 7 is Reset (0), then the lower 7 bits is a run length count. By using
the MSB, this limits channel Addresses and Run Length Counts to 7 Bits (0 - 127).

ECP Software Registers


The table below shows the registers of the Extended Capabilities Port. The first 3
registers are exactly the same than with the Standard Parallel Port registers. Note
should be taken, however, of the Enable Bi-Directional Port bit (bit 5 of the Control
Port.) This bit reflects the direction that the ECP port is currently in, and will effect
the FIFO Full and FIFO Empty bits of the ECR Register, which will be explained
later.

Address Port Name Read/Write


Base + 0 Data Port (SPP) Write
ECP Address FIFO (ECP MODE) Read/Write
Base + 1 Status Port (All Modes) Read/Write
Base + 2 Control Port (All Modes) Read/Write
Base + 400h Data FIFO (Parallel Port FIFO Mode) Read/Write
Data FIFO (ECP Mode) Read/Write
Test FIFO (Test Mode) Read/Write
Configuration Register A (Configuration Mode) Read/Write
Base + 401h Configuration Register B (Configuration Mode) Read/Write
Base + 402h Extended Control Register (Used by all modes) Read/Write
Table 2 : ECP Registers
ECP's Extended Control Register (ECR)
The most important register with a Extended Capabilities Parallel Port is the
Extended Control Register (ECR) thus we will target it's operation first. This
register sets up the mode in which the ECP will run, plus gives status of the
ECP's FIFO among other things. You will find the contents of this register
below, in more detail.

Bit Function
7:5 Selects Current Mode of Operation
000 Standard Mode
001 Byte Mode
010 Parallel Port FIFO Mode
011 ECP FIFO Mode
100 EPP Mode
101 Reserved
110 FIFO Test Mode
111 Configuration Mode
4 ECP Interrupt Bit
3 DMA Enable Bit
2 ECP Service Bit
1 FIFO Full
0 FIFO Empty
Table 3 ECR - Extended Control Register

The three MSB of the Extended Control Register selects the mode of
operation. There are 7 possible modes of operation, but not all ports will
support all modes. The EPP mode is one such example, not being available
on some ports. Below is a table of Modes of Operation.

Modes of Operation
Standard Mode Selecting this mode will cause the ECP port to behave as a Standard Parallel Port,
without Bi-directional functionality.
Byte Mode /
Behaves as a SPP in Bi-directional (Reverse) mode.
PS/2 Mode
Parallel Port In this mode, any data written to the Data FIFO will be sent to the peripheral using
FIFO Mode the SPP Handshake. The hardware will generate the handshaking required. Useful
with non-ECP devices such as Printers. You can have some of the features of ECP
like FIFO buffers and hardware generation of handshaking but with the existing
SPP handshake instead of the ECP Handshake.
ECP FIFO Standard Mode for ECP Use. This mode uses the ECP Handshake, already
Mode described.
EPP On some chipsets, this mode will enable EPP to be used. While on others, this mode
Mode/Reserved is still reserved.
Reserved Currently Reserved
FIFO Test While in this mode, any data written to the Test FIFO Register will be placed into
Mode the FIFO and any data read from the Test FIFO register will be read from the
FIFO buffer. The FIFO Full/Empty Status Bits will reflect their true value, thus
FIFO depth, among other things can be determined in this mode.
Configuration In this mode, the two configuration registers, cnfgA & cnfgB become available at
Mode their designated Register Addresses.

As outlined above, when the port is set to operate in Standard Mode, it will
behave just like a Standard Parallel Port (SPP) with no bi-directional data
transfer. If you require bi-directional transfer, then set the mode to Byte
Mode. The Parallel Port FIFO mode and ECP FIFO mode both use hardware to
generate the necessary handshaking signals. The only difference between
each mode is that The Parallel Port FIFO Mode uses SPP handshaking, thus
can be used with your SPP printer. ECP FIFO mode uses ECP handshaking.
The FIFO test mode can be used to test the capacity of the FIFO Buffers as
well as to make sure they function correctly. When in FIFO test mode, any
byte which is written to the TEST FIFO (Base + 400h) is placed into the FIFO
buffer and any byte which is read from this register is taken from the FIFO
Buffer. You can use this along with the FIFO Full and FIFO Empty bits of the
Extended Control Register to determine the capacity of the FIFO Buffer. This
should normally be about 16 Bytes deep.

The other Bits of the ECR also play an important role in the operation of the
ECP Port. The ECP Interrupt Bit, (Bit 4) enables the use of Interrupts, while
the DMA Enable Bit (Bit 3) enables the use of Direct Memory Access. The
ECP Service Bit (Bit 2) shows if an interrupt request has been initiated. If so,
this bit will be set. Resetting this bit is different with different chips. Some
require you to Reset the Bit, E.g. Write a Zero to it. Others will reset once the
Register has been read.

The FIFO Full (Bit 1) and FIFO Empty (Bit 0) show the status of the FIFO
Buffer. These bits are direction dependent, thus note should be taken of the
Control Register's Bit 5. If bit 0 (FIFO Empty) is set, then the FIFO buffer is
completely empty. If Bit 1 is set then the FIFO buffer is Full. Thus, if neither
bit 0 or 1 is set, then there is data in FIFO, but is not yet full. These bits can
be used in FIFO Test Mode, to determine the capacity of the FIFO Buffer.

ECP's Configuration Register A (cnfgA)


Configuration Register A is one of two configuration registers which the ECP
Port has. These Configuration Registers are only accessible when the ECP
Port is in Configuration Mode. (See Extended Control Register) CnfgA can be
accessed at Base + 400h.

Bit Function
7 1 Interrupts are level triggered
0 Interrupts are edge triggered (Pulses)
6:4 00h Accepts Max. 16 Bit wide words
01h Accepts Max. 8 Bit wide words
02h Accepts Max. 32 Bit wide words
03h:07h Reserved for future expansion
3 Reserved
2 Host Recovery : Pipeline/Transmitter Byte included in FIFO?
In forward direction, the 1 byte in the transmitter pipeline doesn't affect
0
FIFO Full.
In forward direction, the 1 byte in the transmitter pipeline is include as
1
part of FIFO Full.
1:0 Host Recovery : Unsent byte(s) left in FIFO
00 Complete Pword
01 1 Valid Byte
10 2 Valid Bytes
11 3 Valid Bytes
Table 4 - Configuration Register A

Configuration Register A can be read to find out a little more about the ECP
Port. The MSB, shows if the card generates level interrupts or edge triggered
interrupts. This will depend upon the type of bus your card is using. Bits 4 to
6, show the buses width within the card. Some cards only have a 8 bit data
path, while others may have a 32 or 16 bit width. To get maximum efficiency
from your card, the software can read the status of these bits to determine
the Maximum Word Size to output to the port.

The 3 LSB's are used for Host Recovery. In order to recover from an error,
the software must know how many bytes were sent, by determining if there
are any bytes left in the FIFO. Some implementations may include the byte
sitting in the transmitter register, waiting to be sent as part of the FIFO's Full
Status, while others may not. Bit 2 determines weather or not this is the
case.

The other problem is that the Parallel Ports output is only 8 bits wide, and
that you many be using 16 bit or 32 bit I/O Instructions. If this is the case,
then part of your Port Word (Word you sent to port) may be sent. Therefore
Bits 0 and 1 give an indication of the number of valid bytes still left in the
FIFO, so that you can retransmit these.

ECP's Configuration Register B (cnfgB)


Configuration Register B, like Configuration Register A is only available when
the ECP Port is in Configuration Mode. When in this mode, cnfgB resides at
Base + 401h. Below is the make-up of the cnfgB Register.

Bit(s) Function
7 1 Compress outgoing Data Using RLE
0 Do Not compress Data
6 Interrupt Status - Shows the Current Status of the IRQ Pin
5:3 Selects or Displays Status of Interrupt Request Line.
000 Interrupt Selected Via Jumper
001 IRQ 7
010 IRQ 9
011 IRQ 10
100 IRQ 11
101 IRQ 14
110 IRQ 15
111 IRQ 5
2:0 Selects or Displays Status of the DMA Channel the Printer Card
Uses
000 Uses a Jumpered 8 Bit DMA Channel
001 DMA Channel 1
010 DMA Channel 2
011 DMA Channel 3
100 Uses a Jumpered 16 Bit DMA Channel
101 DMA Channel 5
110 DMA Channel 6
111 DMA Channel 7
Table 5 - Configuration B Register

The Configuration Register B (cnfgB) can be a combination of read/write


access. Some ports may be software configurable, where you can set the IRQ
and DMA resources from the register. Others may be set via BIOS or by using
jumpers on the Card, thus are read only.

Bit 7 of the cnfgB Register selects whether to compress outgoing data using
RLE (Run Length Encoding.) When Set, the host will compress the data
before sending. When reset, data will be sent to the peripheral raw
(Uncompressed). Bit 6 returns the status of the IRQ pin. This can be used to
diagnose conflicts as it will not only reflect the status of the Parallel Ports
IRQ, but and other device using this IRQ.

Bits 5 to 3 give status of about the Port's IRQ assignment. Likewise for bits 2
to 0 which give status of DMA Channel assignment. As mentioned above
these fields may be read/write. The disappearing species of Parallel Cards
which have Jumpers may simply show it's resources as "Jumpered" or it may
show the correct Line Numbers. However these of course will be read only.

Interfacing Example - 16 Character x 2 Line LCD

Description.

This is the first interfacing example for the Parallel Port. We will start with
something simple. This example doesn't use the Bi-directional feature
found on newer ports, thus it should work with most, if no all Parallel
Ports. It however doesn't show the use of the Status Port as an input. So
what are we interfacing? A 16 Character x 2 Line LCD Module to the
Parallel Port. These LCD Modules are very common these days, and are
quite simple to work with, as all the logic required to run them is on
board.

Schematic
Circuit Description

Above is the quite simple schematic. The LCD panel's Enable and
Register Select is connected to the Control Port. The Control Port is an
open collector / open drain output. While most Parallel Ports have
internal pull-up resistors, there are a few which don't. Therefore by
incorporating the two 10K external pull up resistors, the circuit is more
portable for a wider range of computers, some of which may have no
internal pull up resistors.

We make no effort to place the Data bus into reverse direction. Therefore
we hard wire the R/W line of the LCD panel, into write mode. This will
cause no bus conflicts on the data lines. As a result we cannot read back
the LCD's internal Busy Flag which tells us if the LCD has accepted and
finished processing the last instruction. This problem is overcome by
inserting known delays into our program.

The 10k Potentiometer controls the contrast of the LCD panel. Nothing
fancy here. As with all the examples, I've left the power supply out. You
can use a bench power supply set to 5v or use a onboard +5 regulator.
Remember a few de-coupling capacitors, especially if you have trouble
with the circuit working properly.

The 2 line x 16 character LCD modules are


available from a wide range of
manufacturers and should all be compatible
with the HD44780. The one I used to test
this circuit was a Powertip PC-1602F and
an old Philips LTN211F-10 which was
extracted from a Poker Machine! The
diagram to the right, shows the pin numbers
for these devices. When viewed from the
front, the left pin is pin 14 and the right pin is
pin 1.
Programming - Source Code
/* LCD Module Software */
/* 17th May 1997 */
/* Copyright 1997 Craig Peacock */
/* WWW - http://www.senet.com.au/~cpeacock */
/* Email - cpeacock@senet.com.au */
/* */
/* Register Select must be connected to Select Printer (PIN 17) */
/* Enable must be connected to Strobe (PIN1) */
/* DATA 0:7 Connected to DATA 0:7 */

#include <dos.h>
#include <string.h>

#define PORTADDRESS 0x378 /* Enter Your Port Address Here */

#define DATA PORTADDRESS+0


#define STATUS PORTADDRESS+1
#define CONTROL PORTADDRESS+2

void main(void)
{
char string[] = {"Testing 1,2,3 "
"It' Works ! "};
char init[10];
int count;
int len;
init[0] = 0x0F; /* Init Display */
init[1] = 0x01; /* Clear Display */
init[2] = 0x38; /* Dual Line / 8 Bits */

outportb(CONTROL, inportb(CONTROL) & 0xDF); /* Reset Control Port - Make sure


Forward Direction */

outportb(CONTROL, inportb(CONTROL) | 0x08); /* Set Select Printer (Register Select) */

for (count = 0; count <= 2; count++)


{
outportb(DATA, init[count]);
outportb(CONTROL,inportb(CONTROL) | 0x01); /* Set Strobe (Enable)*/
delay(20); /* Larger Delay for INIT */
outportb(CONTROL,inportb(CONTROL) & 0xFE); /* Reset Strobe (Enable)*/
delay(20); /* Larger Delay for INIT */
}

outportb(CONTROL, inportb(CONTROL) & 0xF7); /* Reset Select Printer (Register


Select) */

len = strlen(string);

for (count = 0; count < len; count++)


{
outportb(DATA, string[count]);
outportb(CONTROL,inportb(CONTROL) | 0x01); /* Set Strobe */
delay(2);
outportb(CONTROL,inportb(CONTROL) & 0xFE); /* Reset Strobe */
delay(2);
}
}
Above is the source code to get this example running. It's been written for Borland
C, so if you are using a Microsoft compiler, then you will have to change the
outportb() function to outp() and inportb() to inp().
The LCD panel requires a few instructions to be sent, to order to turn on the display
and initialise it. This is what the first for loop does. These instructions must be sent
to the LCD's Instruction Register which is controlled by the Register Select (Pin 4).
When pin 4 is low the instruction register is selected, thus when high the data
register must be selected. We connect this to the Parallel Port's Select Printer line
which happens to be hardware inverted. Therefore if we write a '1' to bit 3 of the
Control Register the Select Printer line goes low.
We want to first send instructions to the LCD module. Therefore the Register Select
line must be low. As it is hardware inverted, we will want to set bit 3 of the Control
Register to '1'. However we don't want to upset any other bits on the Control Port.
We achieve this by reading the Control Port and OR'ing 0x80 to it. e.g.
outportb(CONTROL, inportb(CONTROL) | 0x08); This will only set bit 3.
After we place a data byte on the data lines, we must then signal to the LCD
module to read the data. This is done using the Enable line. Data is clocked into the
LCD module on the high to low transition. The Strobe is hardware inverted, thus by
setting bit 0 of the Control Register we get a high to low transition on the Strobe
line. We then wait for a delay, and return the line to a high state ready for the next
byte.
After we initialize the LCD Module, we want to send text to it. Characters are sent to
the LCD's Data Port, thus we want to clear bit 3. Once again we must only change
the one bit, thus we use outportb(CONTROL, inportb(CONTROL) & 0xF7);. Then we set
up another for loop to read a byte from the string and send it to the LCD panel. This
is repeated for the length of the string.
The delays should be suitable for most machines. If the LCD panel is not initializing
properly, you can try increasing the delays. Likewise if the panel is skipping
characters, e.g. Tst ,2. On the other hand, If the LCD module is repeating
characters e.g. TTTeessttiinngg then you may have a faulting Enable connection.
Check your Enable to Strobe connection.

Interfacing the Serial / RS232 Port

The Serial Port is harder to interface than the Parallel Port. In most cases, any device you
connect to the serial port will need the serial transmission converted back to parallel so
that it can be used. This can be done using a UART. On the software side of things, there
are many more registers that you have to attend to than on a Standard Parallel Port.
(SPP)

So what are the advantages of using serial data transfer rather than parallel?

1. Serial Cables can be longer than Parallel cables. The serial port transmits a '1' as -3 to
-25 volts and a '0' as +3 to +25 volts where as a parallel port transmits a '0' as 0v and
a '1' as 5v. Therefore the serial port can have a maximum swing of 50V compared to
the parallel port which has a maximum swing of 5 Volts. Therefore cable loss is not
going to be as much of a problem for serial cables than they are for parallel.
2. You don't need as many wires than parallel transmission. If your device needs to be
mounted a far distance away from the computer then 3 core cable (Null Modem
Configuration) is going to be a lot cheaper that running 19 or 25 core cable. However
you must take into account the cost of the interfacing at each end.
3. Infra Red devices have proven quite popular recently. You may of seen many electronic
diaries and palmtop computers which have infra red capabilities build in. However
could you imagine transmitting 8 bits of data at the one time across the room and
being able to (from the devices point of view) decipher which bits are which? Therefore
serial transmission is used where one bit is sent at a time. IrDA-1 (The first infra red
specifications) was capable of 115.2k baud and was interfaced into a UART. The pulse
length however was cut down to 3/16th of a RS232 bit length to conserve power
considering these devices are mainly used on diaries, laptops and palmtops.
4. Microcontroller's have also proven to be quite popular recently. Many of these have in
built SCI (Serial Communications Interfaces) which can be used to talk to the outside
world. Serial Communication reduces the pin count of these MPU's. Only two pins are
commonly used, Transmit Data (TXD) and Receive Data (RXD) compared with at least 8
pins if you use a 8 bit Parallel method (You may also require a Strobe).
Table of Contents
Part 1 : Hardware (PC's)
Hardware Properties
Serial Pinouts (D25 and D9 connectors)
Pin Functions
Null Modems
Loopback Plugs
DTE/DCE Speeds
Flow Control
The UART (8250's and Compatibles)
Type of UARTS (For PC's)
Part 2 : Serial Ports' Registers (PC's)
Port Addresses and IRQ's
Table of Registers
DLAB ?
Interrupt Enable Register (IER)
Interrupt Identification Register (IIR)
First In / First Out Control Register (FCR)
Line Control Register (LCR)
Modem Control Register (MCR)
Line Status Register (LSR)
Modem Status Register (MSR)
Scratch Register
Part 3 : Programming (PC's)
Polling or Interrupt Driven?
Source Code - Termpoll.c (Polling Version)
Source Code - Buff1024.c (ISR Version)
Interrupt Vectors
Interrupt Service Routine
UART Configuration
Main Routine (Loop)
Determining the type of UART via Software
Part 4 : External Hardware - Interfacing Methods
RS-232 Waveforms
RS-232 Level Converters
Making use of the Serial Format
8250 and compatable UART's
CDP6402, AY-5-1015 / D36402R-9 etc UARTs
Microcontrollers
Part One : Hardware (PC's)
Hardware Properties

Devices which use serial cables for their communication are split into two
categories. These are DCE (Data Communications Equipment) and DTE (Data
Terminal Equipment.) Data Communications Equipment are devices such as your
modem, TA adapter, plotter etc while Data Terminal Equipment is your Computer
or Terminal.

The electrical specifications of the serial port is contained in the EIA (Electronics
Industry Association) RS232C standard. It states many parameters such as -

1. A "Space" (logic 0) will be between +3 and +25 Volts.


2. A "Mark" (Logic 1) will be between -3 and -25 Volts.
3. The region between +3 and -3 volts is undefined.
4. An open circuit voltage should never exceed 25 volts. (In Reference to GND)
5. A short circuit current should not exceed 500mA. The driver should be able to handle
this without damage. (Take note of this one!)

Above is no where near a complete list of the EIA standard. Line Capacitance,
Maximum Baud Rates etc are also included. For more information please consult
the EIA RS232-C standard. It is interesting to note however, that the RS232C
standard specifies a maximum baud rate of 20,000 BPS!, which is rather slow by
today's standards. A new standard, RS-232D has been recently released.

Serial Ports come in two "sizes", There are the D-Type 25 pin connector and the D-
Type 9 pin connector both of which are male on the back of the PC, thus you will
require a female connector on your device. Below is a table of pin connections for
the 9 pin and 25 pin D-Type connectors.

Serial Pinouts (D25 and D9 Connectors)

D-Type-25 Pin No. D-Type-9 Pin No. Abbreviation Full Name


Pin 2 Pin 3 TD Transmit Data
Pin 3 Pin 2 RD Receive Data
Pin 4 Pin 7 RTS Request To Send
Pin 5 Pin 8 CTS Clear To Send
Pin 6 Pin 6 DSR Data Set Ready
Pin 7 Pin 5 SG Signal Ground
Pin 8 Pin 1 CD Carrier Detect
Pin 20 Pin 4 DTR Data Terminal Ready
Pin 22 Pin 9 RI Ring Indicator
Table 1 : D Type 9 Pin and D Type 25 Pin Connectors
Pin Functions

Abbreviation Full Name Function


TD Transmit Data Serial Data Output (TXD)
RD Receive Data Serial Data Input (RXD)
CTS Clear to Send This line indicates that the Modem is ready to exchange data.
DCD Data Carrier When the modem detects a "Carrier" from the modem at the other end
Detect of the phone line, this Line becomes active.
DSR Data Set Ready This tells the UART that the modem is ready to establish a link.
DTR Data Terminal This is the opposite to DSR. This tells the Modem that the UART is
Ready ready to link.
RTS Request To
This line informs the Modem that the UART is ready to exchange data.
Send
RI Ring Indicator Goes active when modem detects a ringing signal from the PSTN.

Null Modems

A Null Modem is used to connect two DTE's together. This is commonly used as a
cheap way to network games or to transfer files between computers using
Zmodem Protocol, Xmodem Protocol etc. This can also be used with many
Microprocessor Development Systems.

Figure 1 : Null Modem Wiring Diagram

Above is my preferred method of wiring a Null Modem. It only requires 3 wires (TD,
RD & SG) to be wired straight through thus is more cost effective to use with long
cable runs. The theory of operation is reasonably easy. The aim is to make to
computer think it is talking to a modem rather than another computer. Any data
transmitted from the first computer must be received by the second thus TD is
connected to RD. The second computer must have the same set-up thus RD is
connected to TD. Signal Ground (SG) must also be connected so both grounds are
common to each computer.

The Data Terminal Ready is looped back to Data Set Ready and Carrier Detect on
both computers. When the Data Terminal Ready is asserted active, then the Data
Set Ready and Carrier Detect immediately become active. At this point the
computer thinks the Virtual Modem to which it is connected is ready and has
detected the carrier of the other modem.

All left to worry about now is the Request to Send and Clear To Send. As both
computers communicate together at the same speed, flow control is not needed
thus these two lines are also linked together on each computer. When the
computer wishes to send data, it asserts the Request to Send high and as it's
hooked together with the Clear to Send, It immediately gets a reply that it is ok to
send and does so.

Notice that the ring indicator is not connected to anything of each end. This line is
only used to tell the computer that there is a ringing signal on the phone line. As
we don't have a modem connected to the phone line this is left disconnected.

LoopBack Plug

This loopback plug can come in extremely handy when writing Serial /
RS232 Communications Programs. It has the receive and transmit lines
connected together, so that anything transmitted out of the Serial Port is
immediately received by the same port. If you connect this to a Serial Port an
load a Terminal Program, anything you type will be immediately displayed
on the screen. This can be used with the examples later in this tutorial.

Please note that this is not intended for use with Diagnostic Programs and
thus will probably not work. For these programs you require a differently
Figure 2 : Loopback Plug wired Loop Back plug which may vary from program to program.
Wiring Diagram

DTE / DCE Speeds

We have already talked briefly about DTE & DCE. A typical Data Terminal Device is
a computer and a typical Data Communications Device is a Modem. Often people
will talk about DTE to DCE or DCE to DCE speeds. DTE to DCE is the speed between
your modem and computer, sometimes referred to as your terminal speed. This
should run at faster speeds than the DCE to DCE speed. DCE to DCE is the link
between modems, sometimes called the line speed.

Most people today will have 28.8K or 33.6K modems. Therefore we should expect
the DCE to DCE speed to be either 28.8K or 33.6K. Considering the high speed of
the modem we should expect the DTE to DCE speed to be about 115,200 BPS.
(Maximum Speed of the 16550a UART) This is where some people often fall into a
trap. The communications program which they use have settings for DCE to DTE
speeds. However they see 9.6 KBPS, 14.4 KBPS etc and think it is your modem
speed.

Today's Modems should have Data Compression build into them. This is very much
like PK-ZIP but the software in your modem compresses and decompresses the
data. When set up correctly you can expect compression ratios of 1:4 or even
higher. 1 to 4 compression would be typical of a text file. If we were transferring
that text file at 28.8K (DCE-DCE), then when the modem compresses it you are
actually transferring 115.2 KBPS between computers and thus have a DCE-DTE
speed of 115.2 KBPS. Thus this is why the DCE-DTE should be much higher than
your modem's connection speed.
Some modem manufacturers quote a maximum compression ratio as 1:8. Lets say
for example its on a new 33.6 KBPS modem then we may get a maximum 268,800
BPS transfer between modem and UART. If you only have a 16550a which can do
115,200 BPS tops, then you would be missing out on a extra bit of performance.
Buying a 16C650 should fix your problem with a maximum transfer rate of 230,400
BPS.

However don't abuse your modem if you don't get these rates. These are
MAXIMUM compression ratios. In some instances if you try to send a already
compressed file, your modem can spend more time trying the compress it, thus
you get a transmission speed less than your modem's connection speed. If this
occurs try turning off your data compression. This should be fixed on newer
modems. Some files compress easier than others thus any file which compresses
easier is naturally going to have a higher compression ratio.

Flow Control

So if our DTE to DCE speed is several times faster than our DCE to DCE speed the
PC can send data to your modem at 115,200 BPS. Sooner or later data is going to
get lost as buffers overflow, thus flow control is used. Flow control has two basic
varieties, Hardware or Software.

Software flow control, sometimes expressed as Xon/Xoff uses two characters Xon
and Xoff. Xon is normally indicated by the ASCII 17 character where as the ASCII
19 character is used for Xoff. The modem will only have a small buffer so when the
computer fills it up the modem sends a Xoff character to tell the computer to stop
sending data. Once the modem has room for more data it then sends a Xon
character and the computer sends more data. This type of flow control has the
advantage that it doesn't require any more wires as the characters are sent via the
TD/RD lines. However on slow links each character requires 10 bits which can slow
communications down.

Hardware flow control is also known as RTS/CTS flow control. It uses two wires in
your serial cable rather than extra characters transmitted in your data lines. Thus
hardware flow control will not slow down transmission times like Xon-Xoff does.
When the computer wishes to send data it takes active the Request to Send line. If
the modem has room for this data, then the modem will reply by taking active the
Clear to Send line and the computer starts sending data. If the modem does not
have the room then it will not send a Clear to Send.

The UART (8250 and Compatibles)

UART stands for Universal Asynchronous Receiver / Transmitter. Its the little box of
tricks found on your serial card which plays the little games with your modem or
other connected devices. Most cards will have the UART's integrated into other
chips which may also control your parallel port, games port, floppy or hard disk
drives and are typically surface mount devices. The 8250 series, which includes
the 16450, 16550, 16650, & 16750 UARTS are the most commonly found type in
your PC. Later we will look at other types which can be used in your homemade
devices and projects.

Figure 3 : Pin Diagrams for 16550, 16450 & 8250 UARTs

The 16550 is chip compatible with the 8250 & 16450. The only two differences are
pins 24 & 29. On the 8250 Pin 24 was chip select out which functioned only as a
indicator to if the chip was active or not. Pin 29 was not connected on the
8250/16450 UARTs. The 16550 introduced two new pins in their place. These are
Transmit Ready and Receive Ready which can be implemented with DMA (Direct
Memory Access). These Pins have two different modes of operation. Mode 0
supports single transfer DMA where as Mode 1 supports Multi-transfer DMA.

Mode 0 is also called the 16450 mode. This mode is selected when the FIFO buffers
are disabled via Bit 0 of the FIFO Control Register or When the FIFO buffers are
enabled but DMA Mode Select = 0. (Bit 3 of FCR) In this mode RXRDY is active low
when at least one character (Byte) is present in the Receiver Buffer. RXRDY will go
inactive high when no more characters are left in the Receiver Buffer. TXRDY will
be active low when there are no characters in the Transmit Buffer. It will go
inactive high after the first character / byte is loaded into the Transmit Buffer.

Mode 1 is when the FIFO buffers are active and the DMA Mode Select = 1. In Mode
1, RXRDY will go active low when the trigger level is reached or when 16550 Time
Out occurs and will return to inactive state when no more characters are left in the
FIFO. TXRDY will be active when no characters are present in the Transmit Buffer
and will go inactive when the FIFO Transmit Buffer is completely Full.

All the UARTs pins are TTL compatible. That includes TD, RD, RI, DCD, DSR, CTS,
DTR and RTS which all interface into your serial plug, typically a D-type connector.
Therefore RS232 Level Converters (which we talk about in detail later) are used.
These are commonly the DS1489 Receiver and the DS1488 as the PC has +12 and
-12 volt rails which can be used by these devices. The RS232 Converters will
convert the TTL signal into RS232 Logic Levels.

Pin No. Name Notes


Pin 1:8 D0:D7 Data Bus
Receiver Clock Input. The frequency of this input should equal the
Pin 9 RCLK
receivers baud rate * 16
Pin 10 RD Receive Data
Pin 11 TD Transmit Data
Pin 12 CS0 Chip Select 0 - Active High
Pin 13 CS1 Chip Select 1 - Active High
Pin 14 nCS2 Chip Select 2 - Active Low
Baud Output - Output from Programmable Baud Rate Generator.
Pin 15 nBAUDOUT
Frequency = (Baud Rate x 16)
Pin 16 XIN External Crystal Input - Used for Baud Rate Generator Oscillator
Pin 17 XOUT External Crystal Output
Pin 18 nWR Write Line - Inverted
Pin 19 WR Write Line - Not Inverted
Pin 20 VSS Connected to Common Ground
Pin 21 RD Read Line - Inverted
Pin 22 nRD Read Line - Not Inverted
Driver Disable. This pin goes low when CPU is reading from
Pin 23 DDIS UART. Can be connected to Bus Transceiver in case of high
capacity data bus.
Pin 24 nTXRDY Transmit Ready
Address Strobe. Used if signals are not stable during read or write
Pin 25 nADS
cycle
Pin 26 A2 Address Bit 2
Pin 27 A1 Address Bit 1
Pin 28 A0 Address Bit 0
Pin 29 nRXRDY Receive Ready
Pin 30 INTR Interrupt Output
Pin 31 nOUT2 User Output 2
Pin 32 nRTS Request to Send
Pin 33 nDTR Data Terminal Ready
Pin 34 nOUT1 User Output 1
Pin 35 MR Master Reset
Pin 36 nCTS Clear To Send
Pin 37 nDSR Data Set Ready
Pin 38 nDCD Data Carrier Detect
Pin 39 nRI Ring Indicator
Pin 40 VDD + 5 Volts
Table 2 : Pin Assignments for 16550A UART

The UART requires a Clock to run. If you look at your serial card a common crystal
found is either a 1.8432 MHZ or a 18.432 MHZ Crystal. The crystal in connected to
the XIN-XOUT pins of the UART using a few extra components which help the
crystal to start oscillating. This clock will be used for the Programmable Baud Rate
Generator which directly interfaces into the transmit timing circuits but not directly
into the receiver timing circuits. For this an external connection mast be made
from pin 15 (BaudOut) to pin 9 (Receiver clock in.) Note that the clock signal will
be at Baudrate * 16.

If you are serious about pursuing the 16550 UART used in your PC further, then
would suggest downloading a copy of the PC16550D data sheet from National
Semiconductors Site. Data sheets are available in .PDF format so you will need
Adobe Acrobat Reader to read these. Texas Instruments has released the 16750
UART which has 64 Byte FIFO's. Data Sheets for the TL16C750 are available from
the Texas Instruments Site.

Types of UARTS (For PC's)

8250 First UART in this series. It contains no scratch register. The 8250A was an improved
version of the 8250 which operates faster on the bus side.
8250A This UART is faster than the 8250 on the bus side. Looks exactly the same to software
than 16450.
8250B Very similar to that of the 8250 UART.
16450 Used in AT's (Improved bus speed over 8250's). Operates comfortably at 38.4KBPS.
Still quite common today.
16550 This was the first generation of buffered UART. It has a 16 byte buffer, however it
doesn't work and is replaced with the 16550A.
16550A Is the most common UART use for high speed communications eg 14.4K & 28.8K
Modems. They made sure the FIFO buffers worked on this UART.
16650 Very recent breed of UART. Contains a 32 byte FIFO, Programmable X-On / X-Off
characters and supports power management.
16750 Produced by Texas Instruments. Contains a 64 byte FIFO.

Part Two : Serial Port's Registers (PC's)


Port Addresses & IRQ's

Name Address IRQ


COM 1 3F8 4
COM 2 2F8 3
COM 3 3E8 4
COM 4 2E8 3
Table 3 : Standard Port Addresses

Above is the standard port addresses. These should work for most P.C's. If you just
happen to be lucky enough to own a IBM P/S2 which has a micro-channel bus, then
expect a different set of addresses and IRQ's. Just like the LPT ports, the base
addresses for the COM ports can be read from the BIOS Data Area.
Start Address Function
0000:0400 COM1's Base Address
0000:0402 COM2's Base Address
0000:0404 COM3's Base Address
0000:0406 COM4's Base Address
Table 4 - COM Port Addresses in the BIOS Data Area;

The above table shows the address at which we can find the Communications
(COM) ports addresses in the BIOS Data Area. Each address will take up 2 bytes.
The following sample program in C, shows how you can read these locations to
obtain the addresses of your communications ports.

#include <stdio.h>
#include <dos.h>

void main(void)
{
unsigned int far *ptraddr; /* Pointer to location of Port Addresses */
unsigned int address; /* Address of Port */
int a;

ptraddr=(unsigned int far *)0x00000400;

for (a = 0; a < 4; a++)


{
address = *ptraddr;
if (address == 0)
printf("No port found for COM%d \n",a+1);
else
printf("Address assigned to COM%d is %Xh\n",a+1,address);
*ptraddr++;
}
}
Table of Registers
Base Address DLAB Read/Write Abr. Register Name
=0 Write - Transmitter Holding Buffer
+0 =0 Read - Receiver Buffer
=1 Read/Write - Divisor Latch Low Byte
=0 Read/Write IER Interrupt Enable Register
+1
=1 Read/Write - Divisor Latch High Byte
- Read IIR Interrupt Identification Register
+2
- Write FCR FIFO Control Register
+3 - Read/Write LCR Line Control Register
+4 - Read/Write MCR Modem Control Register
+5 - Read LSR Line Status Register
+6 - Read MSR Modem Status Register
+7 - Read/Write - Scratch Register
Table 5 : Table of Registers
DLAB ?
You will have noticed in the table of registers that there is a DLAB column. When
DLAB is set to '0' or '1' some of the registers change. This is how the UART is able
to have 12 registers (including the scratch register) through only 8 port addresses.
DLAB stands for Divisor Latch Access Bit. When DLAB is set to '1' via the line
control register, two registers become available from which you can set your speed
of communications measured in bits per second.
The UART will have a crystal which should oscillate around 1.8432 MHZ. The UART
incorporates a divide by 16 counter which simply divides the incoming clock signal
by 16. Assuming we had the 1.8432 MHZ clock signal, that would leave us with a
maximum, 115,200 hertz signal making the UART capable of transmitting and
receiving at 115,200 Bits Per Second (BPS). That would be fine for some of the
faster modems and devices which can handle that speed, but others just wouldn't
communicate at all. Therefore the UART is fitted with a Programmable Baud Rate
Generator which is controlled by two registers.
Lets say for example we only wanted to communicate at 2400 BPS. We worked out
that we would have to divide 115,200 by 48 to get a workable 2400 Hertz Clock.
The "Divisor", in this case 48, is stored in the two registers controlled by the
"Divisor Latch Access Bit". This divisor can be any number which can be stored in
16 bits (ie 0 to 65535). The UART only has a 8 bit data bus, thus this is where the
two registers are used. The first register (Base + 0) when DLAB = 1 stores the
"Divisor latch low byte" where as the second register (base + 1 when DLAB = 1)
stores the "Divisor latch high byte."
Below is a table of some more common speeds and their divisor latch high bytes &
low bytes. Note that all the divisors are shown in Hexadecimal.
Speed (BPS) Divisor (Dec) Divisor Latch High Byte Divisor Latch Low Byte
50 2304 09h 00h
300 384 01h 80h
600 192 00h C0h
2400 48 00h 30h
4800 24 00h 18h
9600 12 00h 0Ch
19200 6 00h 06h
38400 3 00h 03h
57600 2 00h 02h
115200 1 00h 01h
Table 6 : Table of Commonly Used Baudrate Divisors
Interrupt Enable Register (IER)
Bit Notes
Bit 7 Reserved
Bit 6 Reserved
Bit 5 Enables Low Power Mode (16750)
Bit 4 Enables Sleep Mode (16750)
Bit 3 Enable Modem Status Interrupt
Bit 2 Enable Receiver Line Status Interrupt
Bit 1 Enable Transmitter Holding Register Empty Interrupt
Bit 0 Enable Received Data Available Interrupt
Table 7 : Interrupt Enable Register
The Interrupt Enable Register could possibly be one of the easiest registers on a
UART to understand. Setting Bit 0 high enables the Received Data Available
Interrupt which generates an interrupt when the receiving register/FIFO contains
data to be read by the CPU.
Bit 1 enables Transmit Holding Register Empty Interrupt. This interrupts the CPU
when the transmitter buffer is empty. Bit 2 enables the receiver line status
interrupt. The UART will interrupt when the receiver line status changes. Likewise
for bit 3 which enables the modem status interrupt. Bits 4 to 7 are the easy ones.
They are simply reserved. (If only everything was that easy!)
Interrupt Identification Register (IIR)
Bit Notes
Bits 6 and 7 Bit 6 Bit 7
0 0 No FIFO
0 1 FIFO Enabled but Unusable
1 1 FIFO Enabled
Bit 5 64 Byte Fifo Enabled (16750 only)
Bit 4 Reserved
Bit 3 0 Reserved on 8250, 16450
1 16550 Time-out Interrupt Pending
Bits 1 and 2 Bit 2 Bit 1
0 0 Modem Status Interrupt
0 1 Transmitter Holding Register Empty Interrupt
1 0 Received Data Available Interrupt
1 1 Receiver Line Status Interrupt
Bit 0 0 Interrupt Pending
1 No Interrupt Pending
Table 8 : Interrupt Identification Register
The interrupt identification register is a read only register. Bits 6 and 7 give status
on the FIFO Buffer. When both bits are '0' no FIFO buffers are active. This should be
the only result you will get from a 8250 or 16450. If bit 7 is active but bit 6 is not
active then the UART has it's buffers enabled but are unusable. This occurs on the
16550 UART where a bug in the FIFO buffer made the FIFO's unusable. If both bits
are '1' then the FIFO buffers are enabled and fully operational.
Bits 4 and 5 are reserved. Bit 3 shows the status of the time-out interrupt on a
16550 or higher.
Lets jump to Bit 0 which shows whether an interrupt has occurred. If an interrupt
has occurred it's status will shown by bits 1 and 2. These interrupts work on a
priority status. The Line Status Interrupt has the highest Priority, followed by the
Data Available Interrupt, then the Transmit Register Empty Interrupt and then the
Modem Status Interrupt which has the lowest priority.
First In / First Out Control Register (FCR)
Bit Notes
Bits 6 and Bit 7 Bit 6 Interrupt Trigger Level
7 0 0 1 Byte
0 1 4 Bytes
1 0 8 Bytes
1 1 14 Bytes
Bit 5 Enable 64 Byte FIFO (16750 only)
Bit 4 Reserved
DMA Mode Select. Change status of RXRDY & TXRDY pins from mode 1 to
Bit 3
mode 2.
Bit 2 Clear Transmit FIFO
Bit 1 Clear Receive FIFO
Bit 0 Enable FIFO's
Table 9 : FIFO Control Register
The FIFO register is a write only register. This register is used to control the FIFO
(First In / First Out) buffers which are found on 16550's and higher.
Bit 0 enables the operation of the receive and transmit FIFO's. Writing a '0' to this
bit will disable the operation of transmit and receive FIFO's, thus you will loose all
data stored in these FIFO buffers.
Bit's 1 and 2 control the clearing of the transmit or receive FIFO's. Bit 1 is
responsible for the receive buffer while bit 2 is responsible for the transmit buffer.
Setting these bits to 1 will only clear the contents of the FIFO and will not affect the
shift registers. These two bits are self resetting, thus you don't need to set the bits
to '0' when finished.
Bit 3 enables the DMA mode select which is found on 16550 UARTs and higher. More on this later.
Bits 4 and 5 are those easy type again, Reserved.
Bits 6 and 7 are used to set the triggering level on the Receive FIFO. For example if
bit 7 was set to '1' and bit 6 was set to '0' then the trigger level is set to 8 bytes.
When there is 8 bytes of data in the receive FIFO then the Received Data Available
interrupt is set. See (IIR)
Line Control Register (LCR)
Bit 7 1 Divisor Latch Access Bit
Access to Receiver buffer, Transmitter buffer & Interrupt Enable
0
Register
Bit 6 Set Break Enable
Bits 3, 4 Bit 5 Bit 4 Bit 3 Parity Select
And 5 X X 0 No Parity
0 0 1 Odd Parity
0 1 1 Even Parity
1 0 1 High Parity (Sticky)
1 1 1 Low Parity (Sticky)
Bit 2 Length of Stop Bit
0 One Stop Bit
2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word
1
lengths of 5 bits.
Bits 0 And 1 Bit 1 Bit 0 Word Length
0 0 5 Bits
0 1 6 Bits
1 0 7 Bits
1 1 8 Bits
Table 10 : Line Control Register
The Line Control register sets the basic parameters for communication. Bit 7 is the
Divisor Latch Access Bit or DLAB for short. We have already talked about what it
does. (See DLAB?) Bit 6 Sets break enable. When active, the TD line goes into
"Spacing" state which causes a break in the receiving UART. Setting this bit to '0'
Disables the Break.
Bits 3,4 and 5 select parity. If you study the 3 bits, you will find that bit 3 controls
parity. That is, if it is set to '0' then no parity is used, but if it is set to '1' then
parity is used. Jumping to bit 5, we can see that it controls sticky parity. Sticky
parity is simply when the parity bit is always transmitted and checked as a '1' or
'0'. This has very little success in checking for errors as if the first 4 bits contain
errors but the sticky parity bit contains the appropriately set bit, then a parity error
will not result. Sticky high parity is the use of a '1' for the parity bit, while the
opposite, sticky low parity is the use of a '0' for the parity bit.
If bit 5 controls sticky parity, then turning this bit off must produce normal parity
provided bit 3 is still set to '1'. Odd parity is when the parity bit is transmitted as a
'1' or '0' so that there is a odd number of 1's. Even parity must then be the parity
bit produces and even number of 1's. This provides better error checking but still is
not perfect, thus CRC-32 is often used for software error correction. If one bit
happens to be inverted with even or odd parity set, then a parity error will occur,
however if two bits are flipped in such a way that it produces the correct parity bit
then an parity error will no occur.
Bit 2 sets the length of the stop bits. Setting this bit to '0' will produce one stop bit,
however setting it to '1' will produce either 1.5 or 2 stop bits depending upon the
word length. Note that the receiver only checks the first stop bit.
Bits 0 and 1 set the word length. This should be pretty straight forward. A word
length of 8 bits is most commonly used today.
Modem Control Register (MCR)
Bit Notes
Bit 7 Reserved
Bit 6 Reserved
Bit 5 Autoflow Control Enabled (16750 only)
Bit 4 LoopBack Mode
Bit 3 Aux Output 2
Bit 2 Aux Output 1
Bit 1 Force Request to Send
Bit 0 Force Data Terminal Ready
Table 11 : Modem Control Register
The Modem Control Register is a Read/Write Register. Bits 5,6 and 7 are reserved.
Bit 4 activates the loopback mode. In Loopback mode the transmitter serial output
is placed into marking state. The receiver serial input is disconnected. The
transmitter out is looped back to the receiver in. DSR, CTS, RI & DCD are
disconnected. DTR, RTS, OUT1 & OUT2 are connected to the modem control
inputs. The modem control output pins are then place in an inactive state. In this
mode any data which is placed in the transmitter registers for output is received
by the receiver circuitry on the same chip and is available at the receiver buffer.
This can be used to test the UARTs operation.
Aux Output 2 maybe connected to external circuitry which controls the UART-CPU
interrupt process. Aux Output 1 is normally disconnected, but on some cards is
used to switch between a 1.8432MHZ crystal to a 4MHZ crystal which is used for
MIDI. Bits 0 and 1 simply control their relevant data lines. For example setting bit 1
to '1' makes the request to send line active.
Line Status Register (LSR)
Bit Notes
Bit 7 Error in Received FIFO
Bit 6 Empty Data Holding Registers
Bit 5 Empty Transmitter Holding Register
Bit 4 Break Interrupt
Bit 3 Framing Error
Bit 2 Parity Error
Bit 1 Overrun Error
Bit 0 Data Ready
Table 12 : Line Status Register
The line status register is a read only register. Bit 7 is the error in received FIFO bit.
This bit is high when at least one break, parity or framing error has occurred on a
byte which is contained in the FIFO.
When bit 6 is set, both the transmitter holding register and the shift register are empty. The UART's
holding register holds the next byte of data to be sent in parallel fashion. The shift register is used to
convert the byte to serial, so that it can be transmitted over one line. When bit 5 is set, only the
transmitter holding register is empty. So what's the difference between the two? When bit 6, the
transmitter holding and shift registers are empty, no serial conversions are taking place so there should
be no activity on the transmit data line. When bit 5 is set, the transmitter holding register is empty, thus
another byte can be sent to the data port, but a serial conversion using the shift register may be taking
place.
The break interrupt (Bit 4) occurs when the received data line is held in a logic
state '0' (Space) for more than the time it takes to send a full word. That includes
the time for the start bit, data bits, parity bits and stop bits.
A framing error (Bit 3) occurs when the last bit is not a stop bit. This may occur due
to a timing error. You will most commonly encounter a framing error when using a
null modem linking two computers or a protocol analyzer when the speed at which
the data is being sent is different to that of what you have the UART set to receive
it at.
A overrun error normally occurs when your program can't read from the port fast
enough. If you don't get an incoming byte out of the register fast enough, and
another byte just happens to be received, then the last byte will be lost and a
overrun error will result.
Bit 0 shows data ready, which means that a byte has been received by the UART
and is at the receiver buffer ready to be read.
Modem Status Register (MSR)
Bit Notes
Bit 7 Carrier Detect
Bit 6 Ring Indicator
Bit 5 Data Set Ready
Bit 4 Clear To Send
Bit 3 Delta Data Carrier Detect
Bit 2 Trailing Edge Ring Indicator
Bit 1 Delta Data Set Ready
Bit 0 Delta Clear to Send
Table 13 : Modem Status Register
Bit 0 of the modem status register shows delta clear to send, delta meaning a
change in, thus delta clear to send means that there was a change in the clear to
send line, since the last read of this register. This is the same for bits 1 and 3. Bit 1
shows a change in the Data Set Ready line where as Bit 3 shows a change in the
Data Carrier Detect line. Bit 2 is the Trailing Edge Ring Indicator which indicates
that there was a transformation from low to high state on the Ring Indicator line.
Bits 4 to 7 show the current state of the data lines when read. Bit 7 shows Carrier
Detect, Bit 6 shows Ring Indicator, Bit 5 shows Data Set Ready & Bit 4 shows the
status of the Clear To Send line.
Scratch Register
The scratch register is not used for communications but rather used as a place to
leave a byte of data. The only real use it has is to determine whether the UART is a
8250/8250B or a 8250A/16450 and even that is not very practical today as the
8250/8250B was never designed for AT's and can't hack the bus speed.

Part 3 : Programming (PC's)


Polling or Interrupt Driven?
Source Code - Termpoll.c (Polling Version)
Source Code - Buff1024.c (ISR Version)
Interrupt Vectors
Interrupt Service Routine
UART Configuration
Main Routine (Loop)
Determining the type of UART via Software
Part 4 : External Hardware - Interfacing Methods
RS-232 Waveforms
RS-232 Level Converters
Making use of the Serial Format
8250 and compatable UART's
CDP6402, AY-5-1015 / D36402R-9 etc UARTs
Microcontrollers
Part 3 : Programming (PC's)
Polling or Interrupt Driven?

When writing a communications program you have two methods available to you.
You can poll the UART, to see if any new data is available or you can set up an
interrupt handler to remove the data from the UART when it generates a interrupt.
Polling the UART is a lot slower method, which is very CPU intensive thus can only
have a maximum speed of around 34.8 KBPS before you start losing data. Some
newer Pentium Pro's may be able to achieve better rates that this. The other
option is using a Interrupt handler, and that's what we have used here. It will very
easily support 115.2K BPS, even on low end computers.

Termpoll.c - Simple Terminal Program using the Polling Method.

Polling the UART should not be dismissed totally. It's a good method for
diagnostics. If you have no idea of what address your card is at or what IRQ you
are using you can poll the UART at several different addresses to firstly find which
port your card is at and which one your modem is attached to. Once you know this
information, then you can set up the Interrupt routines for the common IRQs and
by enabling one IRQ at a time using the Programmable Interrupt Controller you can
find out your IRQ, You don't even need a screw driver!

Buff1024.c - Simple Terminal Program using Interrupt Requests.


Note: The source code above is not a really good example on how to program but is rather cut down
to size giving quick results, and making it easier to understand. Upon executing your
communications program, it would be wise to store the status of the UART registers, so that
they all can be restored before you quit the program. This is to cause the least upset to other
programs which may also be trying to use the communications ports.

The first step to using interrupts is to work out which interrupt services your serial
card. Table 13 shows the base addresses and IRQ's of some standard ports. IRQ's 3
and 4 are the two most commonly used. IRQ 5 and 7 are sometimes used.

Interrupt Vectors

Once we know the IRQ the next step is to find it's interrupt vector or software
interrupt as some people may call it. Basically any 8086 processor has a set of 256
interrupt vectors numbered 0 to 255. Each of these vectors contains a 4 byte code
which is an address of the Interrupt Service Routine (ISR). Fortunately C being a
high level language, takes care of the addresses for us. All we have to know is the
actual interrupt vector.

INT (Hex) IRQ Common Uses


08 0 System Timer
09 1 Keyboard
0A 2 Redirected
0B 3 Serial Comms. COM2/COM4
0C 4 Serial Comms. COM1/COM3
0D 5 Reserved/Sound Card
0E 6 Floppy Disk Controller
0F 7 Parallel Comms.
70 8 Real Time Clock
71 9 Reserved
72 10 Reserved
73 11 Reserved
74 12 PS/2 Mouse
75 13 Maths Co-Processor
76 14 Hard Disk Drive
77 15 Reserved
Table 14 : Interrupt Vectors (Hardware Only)

The above table shows only the interrupts which are associated with IRQ's. The
other 240 are of no interest to us when programming RS-232 type
communications.

For example if we were using COM3 which has a IRQ of 4, then the interrupt vector
would be 0C in hex. Using C we would set up the vector using the instruction
setvect(0x0C, PORT1INT); where PORT1INT would lead us to a set of instructions which
would service the interrupt.

However before we proceed with that I should say that it is wise to record the old
vectors address and then restore that address once the program is finished. This is
done using oldport1isr = getvect(INTVECT); where oldport1isr is defined using void
interrupt (*oldport1isr)();

Not only should you store the old vector addresses, but also the configuration the
UART was in. Why you Ask? Well it's simple, I wrote a communications program
which was fully featured in the chat side of things. It had line buffering, so no body
could see my spelling mistakes or how slowly I typed. It included anti-bombing
routines and the list goes on. However I couldn't be bothered to program any file
transfer protocols such as Zmodem etc into my communications program.
Therefore I either had to run my communications program in the background of
Telemate using my communications program for chat and everything else it was
designed for and using Telemate to download files. Another method was to run,
say Smodem as a external protocol to my communications program.

Doing this however would mean that my communications program would override
the original speed, parity etc and then when I returned to the original
communications program, everything stopped. Therefore by saving the old
configuration, you can revert back to it before you hand the UART back over to the
other program. Makes sense? However if you don't have any of these programs
you can save yourself a few lines of code. This is what we have done here.

Interrupt Service Routine (ISR)

Now, could we be off track just a little? Yes that's right, PORT1INT is the label to
our interrupt handler called a Interrupt Service Routine (ISR). You can put just
about anything in here you want. However calling some DOS routines can be a
problem.

void interrupt PORT1INT()


{
int c;
do { c = inportb(PORT1 + 5);
if (c & 1) {
buffer[bufferin] = inportb(PORT1);
bufferin++;
if (bufferin == 1024) bufferin = 0;
}
} while (c & 1);
outportb(0x20,0x20);
}
From the example above we check to see if there is a character to receive and if
their is we remove it from the UART and place it in a buffer contained in memory.
We keep on checking the UART, in case FIFO's are enabled, so we can get all data
available at the time of interrupt.
The last line contains the instruction outportb(0x20,0x20); which tells the
Programmable Interrupt Controller that the interrupt has finished. The
Programmable Interrupt Controller (PIC) is what we must go into now. All of the
routines above, we have assumed that everything is set up ready to go. That is all
the UART's registers are set correctly and that the Programmable Interrupt
Controller is set.
The Programmable Interrupt Controller handles hardware interrupts. Most PC's will
have two of them located at different addresses. One handles IRQ's 0 to 7 and the
other IRQ's 8 to 15. Mainly Serial communications interrupts reside on IRQ's under
7, thus PIC1 is used, which is located at 0020 Hex.
Bit Disable IRQ Function
7 IRQ7 Parallel Port
6 IRQ6 Floppy Disk Controller
5 IRQ5 Reserved/Sound Card
4 IRQ4 Serial Port
3 IRQ3 Serial Port
2 IRQ2 PIC2
1 IRQ1 Keyboard
0 IRQ0 System Timer
Table 15 : PIC1 Control Word (0x21)
Multi-Comm ports are getting quite common, thus table 16 includes data for PIC2
which is located at 0xA0. PIC2 is responsible for IRQ's 8 to 15. It operates in exactly
the same way than PIC1 except that EOI's (End of Interrupt) goes to port 0xA0
while the disabling (Masking) of IRQ's are done using port 0xA1.
Bit Disable IRQ Function
7 IRQ15 Reserved
6 IRQ14 Hard Disk Drive
5 IRQ13 Maths Co-Processor
4 IRQ12 PS/2 Mouse
3 IRQ11 Reserved
2 IRQ10 Reserved
1 IRQ9 IRQ2
0 IRQ8 Real Time Clock
Table 16 : PIC2 Control Word (0xA1)
Most of the PIC's initiation is done by BIOS. All we have to worry about is two
instructions. The first one is outportb(0x21,(inportb(0x21) & 0xEF); which selects which
interrupts we want to Disable (Mask). So if we want to enable IRQ4 we would have
to take 0x10 (16) from 0xFF (255) to come up with 0xEF (239). That means we
want to disable IRQ's 7,6,5,3,2,1 and 0, thus enabling IRQ 4.
But what happens if one of these IRQs are already enabled and then we come
along and disable it? Therefore we input the value of the register and using the &
function output the byte back to the register with our changes using the instruction
outportb(0x21,(inportb(0x21) & 0xEF);. For example if IRQ5 is already enabled before we
come along, it will enable both IRQ4 and IRQ5 so we don't make any changes
which may affect other programs or TSR's.
The other instruction is outportb(0x20,0x20); which signals an end of interrupt to the
PIC. You use this command at the end of your interrupt service routine, so that
interrupts of a lower priority will be accepted.
UART Configuration
Now we get to the UART settings (Finally)
It's a good idea to turn off the interrupt generation on the UART as the first
instruction. Therefore your initialization can't get interrupted by the UART. I've
then chosen to set up our interrupt vectors at this point. The next step is to set the
speed at which you wish to communicate at. If you remember the process, we
have to set bit 7 (The DLAB) of the LCR so we can access the Divisor Latch High
and Low Bytes. We have decided to set the speed to 38,400 Bits per second which
should be find for 16450's and 16550's. This requires a divisor of 3, thus our divisor
latch high byte will be 0x00 and a divisor latch low byte, 0x03.
In today's standards the divisor low latch byte is rarely used but it still pays us to
write 0x00 to the register just in case the program before us just happened to set
the UART at a very very low speed. BIOS will normally set UARTs at 2400 BPS when
the computer is first booted up which still doesn't require the Divisor Latch Low
byte.
The next step would be to turn off the Divisor latch access bit so we can get to the
Interrupt Enable Register and the receiver/transmitter buffers. What we could do is
just write a 0x00 to the register clearing it all, but considering we have to set up
our word length, parity as so forth in the line control register we can do this at the
same time. We have decided to set up 8 bits, no parity and 1 stop bit which is
normally used today. Therefore we write 0x03 to the line control register which will
also turn off the DLAB for us saving one more I/O instruction.
The next line of code turns on the FIFO buffers. We have made the trigger level at
14 bytes, thus bits 6 and 7 are on. We have also enabled the FIFO's (bit 0). It's also
good practice to clear out the FIFO buffers on initialization. This will remove any
rubbish which the last program may of left in the FIFO buffers. Due to the fact that
these two bits are self resetting, we don't have to go any further and turn off these
bits. If my arithmetic is correct all these bits add up to 0xC7 or 199 for those
people which still work in decimal.
Then DTR, RTS and OUT 2 is taken active by the instruction outportb(PORT1 + 4,0x0B);.
Some cards (Both of Mine) require OUT2 active for interrupt requests thus I'm
normally always take it high. All that is left now is to set up our interrupts which
has be deliberately left to last as to not interrupt our initialization. Our interrupt
handler is only interested in new data being available so we have only set the
UART to interrupt when data is received.
Main Routine (Loop)
Now we are left with,
do {
if (bufferin != bufferout){
ch = buffer[bufferout];
bufferout++;
if (bufferout == 1024) bufferout = 0;
printf("%c",ch);
}
if (kbhit()){
c = getch();
outportb(PORT1, c);
}
} while (c !=27);
which keeps repeating until c = 27. This occurs when the ESC key is hit.
The next if statement checks to see if a key has been hit. (kbhit()) If so, it gets the
character using the getch() statement and outputs it to the receiver buffer. The
UART then transmits the character to the modem. What we have assumed here, is
that the person using the Communications Program can't type as fast as the UART
can send. However if the program wishes to send something, then a check should
be made to see if BIT 5 of the Line Status Register is set before attempting to send
a byte to the transmitter register.
For more information on Interrupts, try "Interfacing the PC : Using Interrupts"
Determining the type of UART via software
The type of UART you have installed in your system can be determined without
even needing a screwdriver in most cases. As you can see from Types of UART's
each UART has minor differences, all we have to do it test these.
The first procedure we do is to set bit 0 to '1' in the FIFO control register. This tries
to enable the FIFO buffers. Then we read bits 6 and 7 from the interrupt
identification register. If both bits are '1' then the FIFO buffers are enabled. This
would mean the UART is a 16550a. If the FIFO's were enabled but not usable then
it would be a 16550. If there is no FIFO buffer enabled it is most likely to be a
16450 UART, but could be a 8250, 8250A or 8250B on very old systems.
AT's have a fast bus speed which the 8250 series of UART can't handle to well thus
it is very unlikely to be found in any AT. However if you wish to test for them as
well you can follow the same test as above to distinguish 16550's or 16550A's from
the rest. If no FIFOs are enabled then a possible UART is the 16450, 8250, 8250A
or 8250B. Once it is established the it could be one of these four chips, try writing
a byte to the scratch register and then read it back and compare the results. If the
results match then you must have a scratch register, if they don't you either don't
have a scratch register, or it doesn't work to well.
From the descriptions of the UART above if you read back your byte from the
scratch register then the UART must be a 16450 or 8250A. (Both have scratch
registers) If you don't read back your byte then it's either a 8250 or 8250B.
The 16750 has 64 byte FIFO's, thus the easiest way to test for it's presence is to
enable the 64 byte buffer using the FIFO Control Register and then read back the
status of the Interrupt Identification Register. However I have never tested this.
Part 4 : Interfacing Devices to RS-232 Ports
RS-232 Waveforms
So far we have introduced RS-232 Communications in relation to the PC. RS-232
communication is asynchronous. That is a clock signal is not sent with the data.
Each word is synchronized using it's start bit, and an internal clock on each side,
keeps tabs on the timing.
Figure 4 : TTL/CMOS Serial Logic Waveform
The diagram above, shows the expected waveform from the UART when using the
common 8N1 format. 8N1 signifies 8 Data bits, No Parity and 1 Stop Bit. The RS-
232 line, when idle is in the Mark State (Logic 1). A transmission starts with a start
bit which is (Logic 0). Then each bit is sent down the line, one at a time. The LSB
(Least Significant Bit) is sent first. A Stop Bit (Logic 1) is then appended to the
signal to make up the transmission.
The diagram, shows the next bit after the Stop Bit to be Logic 0. This must mean
another word is following, and this is it's Start Bit. If there is no more data coming
then the receive line will stay in it's idle state(logic 1). We have encountered
something called a "Break" Signal. This is when the data line is held in a Logic 0
state for a time long enough to send an entire word. Therefore if you don't put the
line back into an idle state, then the receiving end will interpret this as a break
signal.
The data sent using this method, is said to be framed. That is the data is framed
between a Start and Stop Bit. Should the Stop Bit be received as a Logic 0, then a
framing error will occur. This is common, when both sides are communicating at
different speeds.
The above diagram is only relevant for the signal immediately at the UART. RS-232
logic levels uses +3 to +25 volts to signify a "Space" (Logic 0) and -3 to -25 volts
for a "Mark" (logic 1). Any voltage in between these regions (ie between +3 and -3
Volts) is undefined. Therefore this signal is put through a "RS-232 Level
Converter". This is the signal present on the RS-232 Port of your computer, shown
below.

Figure 5 : RS-232 Logic Waveform


The above waveform applies to the Transmit and Receive lines on the RS-232 port.
These lines carry serial data, hence the name Serial Port. There are other lines on
the RS-232 port which, in essence are Parallel lines. These lines (RTS, CTS, DCD,
DSR, DTR, RTS and RI) are also at RS-232 Logic Levels.
RS-232 Level Converters
Almost all digital devices which we use require either TTL or CMOS logic levels.
Therefore the first step to connecting a device to the RS-232 port is to transform
the RS-232 levels back into 0 and 5 Volts. As we have already covered, this is done
by RS-232 Level Converters.
Two common RS-232 Level Converters are the 1488 RS-232 Driver and the 1489
RS-232 Receiver. Each package contains 4 inverters of the one type, either Drivers
or Receivers. The driver requires two supply rails, +7.5 to +15v and -7.5 to -15v.
As you could imagine this may pose a problem in many instances where only a
single supply of +5V is present. However the advantages of these I.C's are they
are cheap.
Above: (Figure 6) Pinouts for the MAX-232,
RS-232 Driver/Receiver.

Right: (Figure 7) Typical MAX-232 Circuit.

Another device is the MAX-232. It includes a Charge Pump, which generates +10V
and -10V from a single 5v supply. This I.C. also includes two receivers and two
transmitters in the same package. This is handy in many cases when you only
want to use the Transmit and Receive data Lines. You don't need to use two chips,
one for the receive line and one for the transmit. However all this convenience
comes at a price, but compared with the price of designing a new power supply it
is very cheap.
There are also many variations of these devices. The large value of capacitors are
not only bulky, but also expensive. Therefore other devices are available which use
smaller capacitors and even some with inbuilt capacitors. (Note : Some MAX-232's
can use 1 micro farad Capacitors). However the MAX-232 is the most common, and
thus we will use this RS-232 Level Converter in our examples.
Making use of the Serial Format
In order to do anything useful with our Serially transmitted data, we must convert
it back to Parallel. (You could connect an LED to the serial port and watch it flash if
you really want too, but it's not extremely useful). This in the past has been done
with the use of UART's. However with the popularity of cheap Microcontroller's,
these can be more suited to many applications. We will look into the advantages
and disadvantages of each method.
8250 and Compatible UARTs
We have already looked at one type of UART, the 8250 and compatibles found in
your PC. These devices have configuration registers accessible via the data and
address buses which have to be initialized before use. This is not a problem if your
device which you are building uses a Microprocessor. However if you are making a
stand alone device, how are you going to initialize it?
Most Microprocessors / Microcontrollers these days can be brought with build-in
Serial Communication Interfaces (SCI). Therefore there is little need to connect a
40 pin 16550 to, for example a 68HC11 when you can buy one built in. If you are
still in love with the Z-80 or 8086 then an 16550 may be option! (or if you are like
myself, the higher chip count the better. After all it looks more complicated and
impressive! - But a headache to debug!)
Figure 8 : Pin Diagrams for 16550, 16450 & 8250 UARTs
Pin No. Name Notes
Pin 1:8 D0:D7 Data Bus
Receiver Clock Input. The frequency of this input should equal the
Pin 9 RCLK
receivers baud rate x 16
Pin 10 RD Receive Data
Pin 11 TD Transmit Data
Pin 12 CS0 Chip Select 0 - Active High
Pin 13 CS1 Chip Select 1 - Active High
Pin 14 nCS2 Chip Select 2 - Active Low
Baud Output - Output from Programmable Baud Rate Generator.
Pin 15 nBAUDOUT
Frequency = (Baud Rate x 16)
Pin 16 XIN External Crystal Input - Used for Baud Rate Generator Oscillator
Pin 17 XOUT External Crystal Output
Pin 18 nWR Write Line - Inverted
Pin 19 WR Write Line - Not Inverted
Pin 20 VSS Connected to Common Ground
Pin 21 RD Read Line - Inverted
Pin 22 nRD Read Line - Not Inverted
Driver Disable. This pin goes low when CPU is reading from
Pin 23 DDIS UART. Can be connected to Bus Transceiver in case of high
capacity data bus.
Pin 24 nTXRDY Transmit Ready
Address Strobe. Used if signals are not stable during read or write
Pin 25 nADS
cycle
Pin 26 A2 Address Bit 2
Pin 27 A1 Address Bit 1
Pin 28 A0 Address Bit 0
Pin 29 nRXRDY Receive Ready
Pin 30 INTR Interrupt Output
Pin 31 nOUT2 User Output 2
Pin 32 nRTS Request to Send
Pin 33 nDTR Data Terminal Ready
Pin 34 nOUT1 User Output 1
Pin 35 MR Master Reset
Pin 36 nCTS Clear To Send
Pin 37 nDSR Data Set Ready
Pin 38 nDCD Data Carrier Detect
Pin 39 nRI Ring Indicator
Pin 40 VDD + 5 Volts
Table 17 : Pin Assignments for 16550A UART
For more information on the 16550 and compatible UART's see The UART (8250's
and Compatibles) in the first part of this tutorial.
CDP6402, AY-5-1015 / D36402R-9 etc UARTs

There are UARTs such as the CDP6402, AY-5-1015 /


D36402R-9 and compatibles. These differ from the 8250
and compatibles, by the fact that they have separate
Receive and Transmit data buses and can be configured by
connecting certain pins to various logic levels. These are
ideal for applications where you don't have a
Microprocessor available. Such an example is if you want
to connect a ADC0804 (Analog to Digital Converter) to the
UART, or want to connect a LCD Display to the Serial Line.
These common devices use a 8 bit parallel data bus.

The CDP6402's Control Register is made up of Parity Inhibit


(PI), Stop Bit Select (SBS), Character Length Select (CLS1
and 2) and Even Parity Enable (EPE). These inputs can be
latched using the Control Register Load (CRL) or if you tie
this pin high, changes made to these pins will immediately
Figure 9 : Pinout of CDP6402 take effect.
Pin
Abbr. Full Name Notes
Number
Pin 1 VDD + 5v Supply Rail
Pin 2 NC Not Connected
Pin 3 GND Ground
Receiver Register
Pin 4 RRD When driven high, outputs RBR8:RBR1 are High Impedance.
Disable
RBR8: Receiver Buffer
Pin 5:12 Receiver's Data Bus
RBR1 Register
Pin 13 PE Parity Error When High, A Parity Error Has Occurred.
When High, A Framing Error Has Occurred. i.e. The Stop Bit
Pin 14 FE Framing Error
was not a Logic 1.
When High, Data has been received but the nData Received
Pin 15 OE Overrun Error
Reset had not yet been activated.
When High, Status Flag Outputs (PE, FE, OE, DR and
Pin 16 SFD Status Flag Disable
TBRE) are High Impedance
Receiver Register
Pin 17 RRC x16 Clock input for the Receiver Register.
Clock
Data Received Active Low. When low, sets Data received Output Low (i.e.
Pin 18 nDRR
Reset Clears DR)
When High, Data has been received and placed on outputs
Pin 19 DR Data Received
RBR8:RBR1.
Receiver Register RXD - Serial Input. Connect to Serial Port, Via RS-232
Pin 20 RRI
In receiver.
Resets the UART. UART should be reset after applying
Pin 21 MR Master Reset
power.
Transmitter Buffer When High, indicates that Transmitter Buffer Register is
Pin 22 TBRE
Register Empty Empty, thus all bits including the stop bit have been sent.
Active Low. When low, data present on TBR8:TBR1 is
Transmitter Buffer
Pin 23 nTBRL placed in Transmitter Buffer Register. A Low to High
Load / Strobe
Transition on this pin, then sends the data.
Transmitter When High, Transmitter Register is Empty, thus can accept
Pin 24 TRE
Register Empty another byte of data to be sent.
Transmitter
TXD - Serial Output. Connect to Serial Port, Via RS-232
Pin 25 TRO Register Out
Transmitter.
(TXD)
TBR8: Transmitter Buffer Data Bus, for Transmitter. Place Data here which you want to
Pin 26:33
TBR1 Register send.
When High, Control Register (PI, SBS, CLS2, CLS1, EPE) is
Control Register
Pin 34 CRL Loaded. Can be tied high, so changes on these pins occur
Load
instantaneously.
When High, No Parity is Used for Both Transmit and
Pin 35 PI Parity Inhibit
Receive. When Low, Parity is Used.
A High selects 2 stop bits. (1.5 for 5 Character Word Lengths)
Pin 36 SBS Stop Bit Select
A Low selects one stop bit.
CLS2: Character Length Selects Word Length. 00 = 5 Bits, 01 = 6 Bits, 10 = 7 Bits and
Pin 37:38
CLS1 Select 11 = 8 Bits.
When High, Even Parity is Used, When Low, Odd Parity is
Pin 39 EPE Even Parity Enable
Used.
Transmitter
Pin 40 TRC 16x Clock input for Transmitter.
Register Clock
Table 18 : Pin Description for CDP6402, AY-5-1015 / D36402R-9 and compatible UART's
However one disadvantage of these chips over the 8250's is that these UART's
have no inbuilt Programmable Baud Rate Generator, and no facility to connect a
crystal directly to it. While there are Baud Rate Generator Chips such as the AY-5-
8116, a more cheaper (and common) alternative is the 74HC4060 14-bit Binary
Counter and Oscillator.
The 74HC4060, being a 14 bit binary counter/divider only has outputs for some of
it's stages. Only Q4 to Q14 is available for use as they have external connections.
This means higher Baud Rates are not obtainable from common crystals, such as
the 1.8432 Mhz and 2.4576 Mhz. The UART requires a clock rate 16 times higher
than the Baud Rate you will be using. eg A baud rate of 9600 BPS requires a input
clock frequency of 153.6 Khz.
Output 1.8432Mhz 2.4546Mhz
Out 2 115.2 KBPS 153.6 KBPS
Q4 7200 BPS 9600 BPS
Q5 3600 BPS 4800 BPS
Q6 1800 BPS 2400 BPS
Q7 900 BPS 1200 BPS
Q8 450 BPS 600 BPS
Q9 225 BPS 300 BPS
Figure 10 : Baud Rate Generator using a 74HC4060 Table 19 : Possible Baud Rates using a 74HC4060
The 1.8432 Mhz crystal gives some unfamiliar Baud Rates. While many of these
won't be accepted by terminal programs or some hardware, they are still
acceptable if you write your own serial programs. For example the PC's baud rate
divisor for 7200 BPS is 16, 3600 BPS is 32, 1800 BPS is 64 etc. If you require higher
speeds, then it is possible to connect the UART to the OUT2 pin. This connection
utilizes the oscillator, but has no frequency division applied. Using OUT2 with a
1.8432 Mhz crystal connected gives a baud rate of 115,200 BPS. The CMOS
CDP6402 UART can handle up to 200 KBPS at 5 volts, however your MAX-232 may
be limited to 120 KBPS, but is still within range.

Microcontrollers
It is also possible to use microcontrollers to transmit and receive Serial data. As we
have already covered, some of these MCU's (Micro Controller Units) have built in
UART's among other things. Take the application we have used above. We want to
monitor analog voltages using a ADC and then send them serially to the PC. If the
Microcontroller also has a ADC built in along with the UART or SCI, then we could
simply program the device and connect a RS-232 Line Driver. This would minimize
your chip count and make your PCB much smaller.
Take the second example, displaying the serial data to a common 16 character x 2
line LCD display. A common problem with the LCD modules, is they don't accept
cartridge returns, line-feeds, form-feeds etc. By using a microcontroller, not only
can you emulate the UART, but you can also program it to clear the screen, should
a form-feed be sent or advance to the next line should a Line-feed be sent.
The LCD example also required some additional logic (An Inverter) to reset the
data receive line on the UART, and provide a -ve edge on the enable of the LCD to
display the data present on the pins. This can all be done using the Microcontroller
and thus reducing the chip count and the cost of the project.
Talking of chip count, most Microcontrollers have internal oscillators thus you don't
require the 74HC4060 14 Bit Binary Counter and Oscillator. Many Microcontrollers
such as the 68HC05J1A and PIC16C84 have a smaller pin count, than the 40 Pin
UART. This not only makes the project smaller in size, it reduces complexity of the
PCB.
But there are also many disadvantages of the Microcontroller. The major one, is
that you have to program it. For the hobbyist, you may not have a development
system for a Microcontroller or a method of programming it. Then you have to
learn the micro's code and work out how to tackle the problem. At least with the
UART, all you did was plug it in, wire it up and it worked. You can't get much
simpler that that.
So far we have only discussed Full Duplex Transmission, that is that we can
transmit and receive at the same time. If our Microcontroller doesn't have a SCI
then we can Emulate a RS-232 port using a Parallel line under software control.
However Emulation has it's dis-advantages. It only supports slow transmission
speeds, commonly 2400, 9600 or maybe even 19,200 BPS if you are lucky. The
other disadvantage is that it's really only effective in half duplex mode. That is, it
can only communicate in one direction at any one given time. However in many
applications this is not a problem.
As there are many different types of Micro-Controllers all with their different
instruction sets, it is very hard to give examples here which will suit everyone. Just
be aware that you can use them for serial communications and hopefully at a later
date, I can give a limited number of examples with one micro.

Using Interrupts
Interrupts
What are Interrupts?
Interrupts and Intel Architecture
Hardware Interrupts
Programming
Implementing the Interrupt Service Routine (ISR)
Using your new Interrupt Service Routine
The Programmable Interrupt Controller's Operation
The Programmable Interrupt Controller
IRQ2/IRQ9 Redirection
The Programmable Interrupt Controller's Registers
Programmable Interrupt Controller's Addresses
Initialization Command Words (ICWs)
Operation Control Words (OCWs)
Feedback
Feedback

What are Interrupts?


When receiving data and change in status from I/O Ports, we have two methods
available to us. We can Poll the port, which involves reading the status of the port
at fixed intervals to determine whether any data has been received or a change of
status has occurred. If so, then we can branch to a routine to service the ports
requests.

As you could imagine, polling the port would consume quite some time. Time
which could be used doing other things such refreshing the screen, displaying the
time etc. A better alternative would be to use Interrupts. Here, the processor does
your tasks such as refreshing the screen, displaying the time etc, and when a I/O
Port/Device needs attention as a byte has been received or status has changed,
then it sends a Interrupt Request (IRQ) to the processor.

Once the processor receives an Interrupt Request, it finishes its current instruction,
places a few things on the stack, and executes the appropriate Interrupt Service
Routine (ISR) which can remove the byte from the port and place it in a buffer.
Once the ISR has finished, the processor returns to where it left off.

Using this method, the processor doesn't have to waste time, looking to see if your
I/O Device is in need of attention, but rather the device will interrupt the processor
when it needs attention.

Interrupts and Intel Architecture


Interrupts do not have to be entirely associated with I/O devices. The 8086 family
of microprocessors provides 256 interrupts, many of these are only for use as
software interrupts, which we do not attempt to explain in this document.

The 8086 series of microprocessors has an Interrupt Vector Table situated at


0000:0000 which extends for 1024 bytes. The Interrupt Vector table holds the
address of the Interrupt Service Routines (ISR), all four bytes in length. This gives
us room for the 256 Interrupt Vectors.

INT (Hex) IRQ Common Uses


00 - 01 Exception Handlers -
02 Non-Maskable IRQ Non-Maskable IRQ (Parity Errors)
03 - 07 Exception Handlers -
08 Hardware IRQ0 System Timer
09 Hardware IRQ1 Keyboard
0A Hardware IRQ2 Redirected
0B Hardware IRQ3 Serial Comms. COM2/COM4
0C Hardware IRQ4 Serial Comms. COM1/COM3
0D Hardware IRQ5 Reserved/Sound Card
0E Hardware IRQ6 Floppy Disk Controller
0F Hardware IRQ7 Parallel Comms.
10 - 6F Software Interrupts -
70 Hardware IRQ8 Real Time Clock
71 Hardware IRQ9 Redirected IRQ2
72 Hardware IRQ10 Reserved
73 Hardware IRQ11 Reserved
74 Hardware IRQ12 PS/2 Mouse
75 Hardware IRQ13 Math's Co-Processor
76 Hardware IRQ14 Hard Disk Drive
77 Hardware IRQ15 Reserved
78 - FF Software Interrupts -
Table 1 : x86 Interrupt Vectors

The average PC, only has 15 Hardware IRQ's plus one Non-Maskable IRQ. The rest
of the interrupt vectors are used for software interrupts and exception handlers.
Exception handlers are routines like ISR's which get called or interrupted when an
error results. Such an example is the first Interrupt Vector which holds the address
of the Divide By Zero, Exception handler. When a divide by zero occurs the
Microprocessor fetches the address at 0000:0000 and starts executing the code at
this Address.

Hardware Interrupts
The Programmable Interrupt Controller (PIC) handles hardware interrupts. Most
PC's will have two of them located at different addresses. One handles IRQ's 0 to 7
and the other, IRQ's 8 to 15, giving a total of 15 individual IRQ lines, as the second
PIC is cascaded into the first, using IRQ2.

Most of the PIC's initialization is done by BIOS, thus we only have to worry about
two instructions. The PIC has a facility available where we can mask individual
IRQ's so that these requests will not reach the Processor. Thus the first instruction
is to the Operation Control Word 1 (OCW1) to set which IRQ's to mask and which
IRQ's not too.

As there are two PIC's located at different addresses, we must first determine
which PIC we need to use. The first PIC, located at Base Address 0x20h controls
IRQ 0 to IRQ 7. The bit format of PIC1's Operation Control Word 1 is shown below in
table 2.

Bit Disable IRQ Function


7 IRQ7 Parallel Port
6 IRQ6 Floppy Disk Controller
5 IRQ5 Reserved/Sound Card
4 IRQ4 Serial Port
3 IRQ3 Serial Port
2 IRQ2 PIC2
1 IRQ1 Keyboard
0 IRQ0 System Timer
Table 2 : PIC1 Operation Control Word 1 (0x21)

Note that IRQ 2 is connected to PIC2, thus if you mask this IRQ, then you will be
disabling IRQ's 8 to 15.

The second PIC located at a base address of 0xA0h controls IRQs 8 to 15. Below is
the individual bits required to make up it's Operation Control Word.

Bit Disable IRQ Function


7 IRQ15 Reserved
6 IRQ14 Hard Disk Drive
5 IRQ13 Maths Co-Processor
4 IRQ12 PS/2 Mouse
3 IRQ11 Reserved
2 IRQ10 Reserved
1 IRQ9 Redirected IRQ2
0 IRQ8 Real Time Clock
Table 3 : PIC2 Operation Control Word 1 (0xA1)

As the above table shows the bits required to disable an IRQ, we must invert them
should we want to enable an IRQ. For example, if we want to enable IRQ 3 then we
would send the byte 0xF7 as OCW1 to PIC1. But what happens if one of these IRQs
are already enabled and then we come along and disable it?

Therefore we must first get the mask and use the AND function to output the byte
back to the register with our changes so to cause the least upset to the other IRQs.
Going back to our IRQ3 example, we could use outportb(0x21,(inportb(0x21) & 0xF7); to
enable IRQ3. Take note that the OCW1 goes to the register at Base + 1.

The same procedure must be used to mask (disable) an IRQ once we are finished
with it. However this time we must OR the byte 0x08 to the contents of OCW1.
Such and example of code is outportb(0x21,(inportb(0x21) | 0x08);

The other PIC instruction we have to worry about is the End of Interrupt (EOI). This
is sent to the PIC at the end of the Interrupt Service Routine so that the PIC can
reset the In Service Register. See The Programmable Interrupt Controller for more
information. An EOI can be sent using outportb(0x20,0x20); for PIC1 or
outportb(0xA0,0x20); for PIC2

Implementing the Interrupt Service Routine (ISR)


In C you can implement your ISR using void interrupt yourisr() where yourisr is a far
pointer, pointing to the address that your Interrupt Service Routine will reside in
memory. This is later placed in the Interrupt Vector Table so that, it will be called
when interrupted.

The following code is a basic implementation of an ISR.

void interrupt yourisr() /* Interrupt Service Routine (ISR) */


{
disable();

/* Body of ISR goes here */

oldhandler();

outportb(0x20,0x20); /* Send EOI to PIC1 */


enable();
}
defines this function as an Interrupt Service Routine. disable();
void interrupt yourisr()
clears the interrupt flag, so that no other hardware interrupts ,except a NMI (Non-
Maskable Interrupt) can occur. Otherwise, and interrupt with a higher priority that
this one can interrupt the execution of this ISR. However this is not really a
problem in many cases, thus is optional.
The body of your ISR will include code which you want to execute upon this
interrupt request being activated. Most Ports/UARTs may interrupt the processor
for a range of reasons, eg byte received, time-outs, FIFO buffer empty, overruns
etc, thus the nature of the interrupt has to be determined. This is normally
achieved by reading the status registers of the port you are using. Once it has
been established, you can service it's requests.
If you read any data from a port, it is normally common practice to place it in a
buffer, rather that immediately writing it to the screen, inhibiting further interrupts
to be processed. Most Ports these days will have FIFO buffers which can contain
more than one byte, thus repeat your read routine, until the FIFO is empty, then
exit your ISR.
In some cases it may be appropriate to chain the old ISR to this one. Such an
example would be the Clock Interrupt. Other TSR or resident programs may also be
using it, thus if you intercept the interrupt and keep it all for yourself, the other
ISR's can no longer function possibly causing some side effects. However for
Serial/Parallel Ports this is not a problem. To chain the old ISR to your new ISR, you
can call it using oldhandler(); where oldhandler points to your old ISR.
Before we can return from the interrupt, we must tell the Programmable Interrupt
Controller, that we are ending the interrupt by sending an EOI (End of Interrupt
0x10) to it. As there are two PIC's you must first establish which one to send it to.
Use outportb(0x20,0x20); for PIC 1 (IRQ 0 - 7) or outportb(0xA0,0x20); for PIC 2 (IRQ 8 -
15).
Note: If using PIC2, then an EOI has to be sent to both PIC1 and PIC2.
Using your new Interrupt Service Routine
Now that we have written our new interrupt service routine, we can start looking at
how to implement it. The following code segment shows the basic usage of your
new ISR. For this example we have chosen to use IRQ 3.
#include <dos.h>

#define INTNO 0x0B /* Interupt Number - See Table 1 */

void main(void)
{
oldhandler = getvect(INTNO); /* Save Old Interrupt Vector */
setvect(INTNO, yourisr); /* Set New Interrupt Vector Entry */
outportb(0x21,(inportb(0x21) & 0xF7)); /* Un-Mask (Enable) IRQ3 */

/* Set Card - Port to Generate Interrupts */

/* Body of Program Goes Here */

/* Reset Card - Port as to Stop Generating Interrupts */

outportb(0x21,(inportb(0x21) | 0x08)); /* Mask (Disable) IRQ3 */


setvect(INTNO, oldhandler); /* Restore old Interrupt Vector Before Exit */
}
Before you can place the address of your new ISR in the interrupt vector table, you
must first save the old interrupt vector, so that you can restore it once you exit
your program. This is done using oldhandler = getvect(INTNO); where INTNO is the
number of the interrupt vector you wish to return. Before oldhandler can be used,
you must first declare it using void interrupt ( *oldhandler)();
Once the old interrupt vector is stored, we can now install your new ISR into the
interrupt vector table. This is done using the line setvect(INTNO, yourisr); where yourisr
points to your interrupt service routine.
The IRQ which you are using must now be unmasked. We have already discussed
this earlier. See Hardware Interrupts.
Most Ports/UARTs will need some initialization to be able to generate interrupts.
For Example, The Standard Parallel Port (SPP) will require Bit 4 of the Control Port,
Enable IRQ Via ACK Line to be set at Base + 2. The Serial Port will require the
appropriate setting of Bits 0 to 4 of the Interrupt Enable Register (IER) located at
Base + 1.
Your body of the program normally consists of a few housekeeping tasks
depending upon your application. Here you look for new keys pressed, menus
being selected, updating clocks, checking for incoming data in buffers etc, knowing
that any data from your Ports will be automatically read and processed, by the ISR.
If you like implementing ISR's so much, you can attach your own ISR to the
Keyboard Interrupt, so any keys being pressed will be automatically handled by
another ISR, and even one to the clock. Upon every 18.2 ticks you can update the
seconds on your display! The possibilities of ISR's are endless.
Before you exit your program always restore the old interrupt vector, so that your
computer doesn't become unstable. This is done using setvect(INTNO, oldhandler); ,
where oldhandler points to the old interrupt service routine, which we stored using
oldhandler = getvect(INTNO);

The Programmable Interrupt Controller


As we have all ready discussed, the Interrupt ReQuests (IRQ's) of a PC is handled
by two 8259 Programmable Interrupt Controllers. On the old XT's/AT's these were
two 28 Pin DIP IC's, but as you can imagine, things have changed dramatically
since then. While the operational principal is still the same, the PIC is now
integrated somewhere into your chipset, along with many other devices.
The basic block diagram of the PIC is shown above. The 8 individual interrupt
request lines are first passed through the Interrupt Mask Register (IMR) to see if
they have been masked or not. If they are masked, then the request isn't
processed any further. However if they are not masked, they will register their
request with the Interrupt Request Register (IRR).
The Interrupt Request Register will hold all the requested IRQ's until they have
been dealt with appropriately. If required, this register can be read by setting
certain bits of the Operation Control Word 3. The Priority Resolver simply selects
the IRQ of highest priority. The higher priority interrupts are the lower numbered
ones. For Example IRQ 0 has the highest priority followed by IRQ 1 etc.
Now that the PIC has determined which IRQ to process, it is now time to tell the
processor, so that it can call your ISR for you. This process is done by sending a
INT to the processor, i.e. the INT line on the processor is asserted. The processor
will then finish the current instruction it's processing and acknowledge your INT
request with a INTA (Interrupt Acknowledge) pulse.
Upon receiving the processor's INTA, the IRQ which the PIC is processing at the
time is stored in the In Service Register (ISR) which as the name suggests, shows
which IRQ is currently in service. The IRQ's bit is also reset in the Interrupt Request
Register, as it is no longer requesting service but actually getting service.
Another INTA pulse will be sent by the processor, to tell the PIC to place a 8 bit
pointer on the data bus, corresponding to the IRQ number. If an IRQ serviced by
PIC2 is requesting the service, then PIC2 will send the pointer to the processor. The
Master (PIC1) at this stage, will select PIC2 to send the pointer, by placing PIC2's
Slave ID on the Cascade lines, which is a 3 wire bus between all the PIC's in a
system.
The 5 most significant bits of this pointer is set using the Initialization Command
Word 2 (ICW2). This will be 00001 for PIC1 and 01110 for PIC2. The three least
significant bits, will be sent due to which IRQ is being serviced. For example, if
IRQ3 is requesting service then the 8 bit pointer will be made up with 00001 for
the 5 most significant bits and 011 (IR3) for the least significant bits. Put this
together and you get 00001011 or 0x0B which just happens to be IRQ3's interrupt
vector.
For PIC2, the same principal is applied. If IRQ10 is requesting service, then
01110010 will be sent, which just happens to represent Interrupt 72h. IRQ10
happens to be connected to IR2 on the Second PIC, thus 010 is used as the least
significant bits.
Once your ISR has done everything it needs, it sends an End of Interrupt (EOI) to
the PIC, which resets the In-Service Register. If the request came from PIC2, then
EOI's are required to be sent to both PICs. The PIC will then determine the next
highest priority interrupt and repeat the same process. If no Interrupt Requests are
present, then the PIC waits for the next request before interrupting the processor.
IRQ2/IRQ9 Redirection
The redirection of IRQ2 causes quite some confusion, and thus is discussed here. In
the original XT's there were only one PIC, thus only eight IRQ's. However users
soon out grew these resources, thus an additional 7 IRQ's were added to the PC.
This involved attaching another PIC to the existing one already in the XT.
Compatibility always causes problems as the new configuration still had to be
compatible with old hardware and software. The "new" configuration is shown
below.
The CPU only has one interrupt line, thus the second controller had to be
connected to the first controller, in a master/slave configuration. IRQ2 was selected
for this. By using IRQ2 for the second controller, no other devices could use IRQ2,
so what happened to all these devices using IRQ2? Nothing, the interrupt request
line found on the bus, was simply diverted into the IRQ 9 input. As no devices yet
used the second PIC or IRQ9, this could be done.
The next problem was that a hardware device using IRQ2 would install it's ISR at
INT 0x0A. Therefore an ISR routine was used at INT 71h, which sent a EOI to PIC2
and then called the ISR at INT 0x0A. If you dis-assemble the ISR for IRQ9, it will go
a little like,
MOV AL,20
OUT A0,AL ; Send EOI to PIC2
INT 0A ; Call ISR for IRQ2
IRET
The routine only has to send a EOI to PIC2, as it is expected that a ISR routine
written for IRQ2 will send a EOI to PIC1. This example destroys the contents of
Register AL, thus this must be placed on the stack first (Not shown in example). As
PIC2 is initialized with a Slave on IRQ2, any request using PIC2 will not call the ISR
routine for IRQ2. The 8 bit pointer will come from PIC2.
Programmable Interrupt Controller's Addresses
The two PIC's found in an IBM compatible system are initialized via BIOS thus you
don't have to worry about all of their registers. However for some people, who
have inquisitive minds the following information may come in some use or maybe
you want to (re)program a BIOS? Below is a table of all the command words of the
8259 and compatible Programmable Interrupt Controller. The Top Table shows the
Addresses for the PIC1, while the bottom table shows addresses for PIC2.
Address Read/Write Function
Write Initialization Command Word 1 (ICW1)
Write Operation Command Word 2 (OCW2)
20h Write Operation Command Word 3 (OCW3)
Read Interrupt Request Register (IRR)
Read In-Service Register (ISR)
Write Initialization Command Word 2 (ICW2)
Write Initialization Command Word 3 (ICW3)
21h
Write Initialization Command Word 4 (ICW4)
Read/Write Interrupt Mask Register (IMR)
Table 4 : Addresses/Registers for PIC1
PIC2 Addresses . . .
Address Read/Write Function
Write Initialization Command Word 1 (ICW1)
Write Operation Command Word 2 (OCW2)
A0h Write Operation Command Word 3 (OCW3)
Read Interrupt Request Register (IRR)
Read In-Service Register (ISR)
Write Initialization Command Word 2 (ICW2)
Write Initialization Command Word 3 (ICW3)
A1h
Write Initialization Command Word 4 (ICW4)
Read/Write Interrupt Mask Register (IMR)
Table 5 : Addresses/Registers for PIC2

Initialization Command Word 1 (ICW1)


If the PIC has been reset, it must be initialized with 2 to 4 Initialization Command
Words (ICW) before it will accept and process Interrupt Requests. The following
selection outlines the four possible Initialization Command Words.
Bit(s) Function
7:5 Interrupt Vector Addresses for MCS-80/85 Mode.
4 Must be set to 1 for ICW1
1 Level Triggered Interrupts
3
0 Edge Triggered Interrupts
1 Call Address Interval of 4
2
0 Call Address Interval of 8
1 Single PIC
1
0 Cascaded PICs
1 Will be Sending ICW4
0
0 Don't need ICW4
Table 6 : Initialization Command Word 1 (ICW1)
The 8259 Programmable Interrupt Controller, offers many other features which are
not used in the PC. It also offers support for MCS-80/85 microprocessors. All we
have to be aware of being PC uses, is if the system is running in single mode (One
PIC) or if in Cascaded Mode (More than one PIC) and if the Initialization Command
Word 4 is needed. If no ICW4 is used, then all of it's bits will be set to 0. As we are
using it in 8086 mode, we must send a ICW4.
Initialization Command Word 2 (ICW2)
Bit 8086/8080 Mode MCS 80/85 Mode
7 I7 A15
6 I6 A14
5 I5 A13
4 I4 A12
3 I3 A11
2 - A10
1 - A9
0 - A8
Table 7 : Initialization Command Word 2 (ICW2)
Initialization Command Word 2 (ICW2) selects which vector information is released
onto the bus, during the 2nd INTA Pulse. Using the 8086 mode, only bits 7:3 need
to be used. This will be 00001000 (0x08) for PIC1 and 01110000 (0x70) for PIC2. If
you wish to relocate the IRQ Vector Table, then you can use this register.
Initialization Command Word 3 (ICW3)
There are two different Initialization Command Word 3's. One is used, if the PIC is a
master, while the other is used for slaves. The top table shows the ICW3 for the
master.
Bit Function
7 IR7 is connected to a Slave
6 IR6 is connected to a Slave
5 IR5 is connected to a Slave
4 IR4 is connected to a Slave
3 IR3 is connected to a Slave
2 IR2 is connected to a Slave
1 IR1 is connected to a Slave
0 IR0 is connected to a Slave
Table 8 : Initialization Command Word 3 for Master PIC (ICW3)
And for the slave device, the ICW3 below is used.
Bit(s) Function
7 Reserved. Set to 0
6 Reserved. Set to 0
5 Reserved. Set to 0
4 Reserved. Set to 0
3 Reserved. Set to 0
2:0 Slave ID
000 Slave 0
001 Slave 1
010 Slave 2
011 Slave 3
100 Slave 4
101 Slave 5
110 Slave 6
111 Slave 7
Table 9 : Initialization Command Word 3 for Slaves (ICW3)

Initialization Command Word 4 (ICW4)


Bit(s) Function
7 Reserved. Set to 0
6 Reserved. Set to 0
5 Reserved. Set to 0
1 Special Fully Nested Mode
4
0 Not Special Fully Nested Mode
3:2 0x Non - Buffered Mode
10 Buffered Mode - Slave
11 Buffered Mode - Master
1 Auto EOI
1
0 Normal EOI
1 8086/8080 Mode
0
0 MCS-80/85
Table 10 : Initialization Command Word 4 (ICW4)
Once again, many of these are special functions not used with the 8259 PIC in a
PC. We don't use, Special Fully Nested Mode, thus this bit is set to 0. Likewise we
use non-buffered mode and Normal EOI's thus all these corresponding bits are set
to 0. The only thing we must set is 8086/8080 Mode which is done using Bit 0.
Operation Control Word 1 (OCW1)
Once all the required Initialization Command Words have been sent to the PIC,
then you can send Operation Control Words, in any order and at any time during
the PIC's operation. The Operation Control Words are shown in the next sections.
Bit PIC 2 PIC 1
7 Mask IRQ15 Mask IRQ7
6 Mask IRQ14 Mask IRQ6
5 Mask IRQ13 Mask IRQ5
4 Mask IRQ12 Mask IRQ4
3 Mask IRQ11 Mask IRQ3
2 Mask IRQ10 Mask IRQ2
1 Mask IRQ9 Mask IRQ1
0 Mask IRQ8 Mask IRQ0
Table 11 : Operation Control Word 1 (OCW1)
Operation Control Word 1, shown above is used to mask the inputs of the PIC. This
has already been discussed, earlier in this article.
Operation Control Word 2 (OCW2)
Bit(s) Function
7:5 000 Rotate in Auto EOI Mode (Clear)
001 Non Specific EOI
010 Reserved
011 Specific EOI
100 Rotate in Auto EOI Mode (Set)
101 Rotate on Non-Specific EOI
110 Set Priority Command (Use Bits 2:0)
111 Rotate on Specific EOI (Use Bits 2:0)
4 Must be set to 0
3 Must be set to 0
2:0 000 Act on IRQ 0 or 8
001 Act on IRQ 1 or 9
010 Act on IRQ 2 or 10
011 Act on IRQ 3 or 11
100 Act on IRQ 4 or 12
101 Act on IRQ 5 or 13
110 Act on IRQ 6 or 14
111 Act on IRQ 7 or 15
Table 12 : Operation Control Word 2 (OCW2)
Operation Control Word 2 selects how the End of Interrupt (EOI) procedure works.
The only thing of interest to us in this register is the non-specific EOI command,
which we must send at the end of our ISR's.
Operation Control Word 3 (OCW3)
Bit(s) Function
7 Must be set to 0
6:5 00 Reserved
01 Reserved
10 Reset Special Mask
11 Set Special Mask
4 Must be set to 0
3 Must be set to 1
1 Poll Command
2
0 No Poll Command
1:0 00 Reserved
01 Reserved
10 Next Read Returns Interrupt Request Register
11 Next Read Returns In-Service Register
Table 13 : Operation Control Word 3 (OCW3)
Bits 0 and 1 are of the most significant to us, in Operation Control Word 3. These
two bits enable us to read the status of the Interrupt Request Register (IRR) and
the In-Service Register (ISR). This is done by setting the appropriate bits correctly
as above, and reading the register at the Base Address.
For example if we wanted to read the In-Service Register (ISR), then we would set
both bits 1 and 0 to 1. The next read to the base register, (0x20 for PIC1 or 0xA0
for PIC2) will return the status of the In-Service Register.
Interfacing Example - Analog Sampling Via the RS-232 Port
Have you ever wanted to sample analog voltages and log it to your PC for further
analysis? Maybe you have looked at circuits which did just this using the Parallel Por
but it was no use to you as you only have one Parallel Port and the Printer is connec
to it. Or maybe you are using a Palmtop computer which doesn't have a Parallel Port
so, then this is the circuit for you.

Schematic

Circuit Description

The above circuit when in a working state, will wait for a byte to be sent to it before it star
the analog conversion and sends data back to the computer using the 8N1 serial format a
9600 BPS. The circuit is based on a CDP6402C or equivalent UART. This, if you want to cal
is the brains of the operation and performs the conversion of Parallel data to a Serial form
for transmission. The Analog to Digital Conversion is done by the ADC0804, while the MAX
is used to convert TTL/CMOS voltage levels into RS-232 Voltage Levels. The 74HC4060 is a
Oscillator/Divider which is used to generate the UART's Clock.

The Analog to Digital Converter (ADC0804) starts it's conversion when the UART's Data
Received line becomes active. Many people at this stage will say that this circuit cannot w
- The Data Received (DR) output is Active High, while the nWrite (WR) input to the ADC is
Active Low. This circuit is quite correct. If we look at the ADC's operation, on a high to low
transition of the nWrite input the internal Successive Approximation and Shift Registers ar
reset. Provided the nWrite line remains in this state the ADC will remain reset. The conver
process will start when a low to high transition is made on the nWrite input.

Therefore getting back to this circuit, the Data Received output will remain low while there
no data to be received, thus the ADC will remain in the reset mode. When data is received
the UART, a low to high transition will result on the Data Received line and thus on the
connected nWrite pin of the ADC.

This low to high transition will cause the ADC to spring to life and make a digital conversio
the analog voltage on it's pins. Once the conversion is finished, it's nINTR (Interrupt) line w
become active low. This signal is then used to tell the UART to send the data residing on it
Transmitter Buffer Register inputs (TBR8:TBR1). nINTR is also connected to the UART's Da
Received Reset so that the Data Received line will be reset. The circuit is then ready to
repeat the entire process upon receiving the next byte.

Programming - Source Code


/* Name : Serial ADC Program */
/* Written By : Craig Peacock <Craig.Peacock@beyondlogic.org> */

#include <dos.h>
#include <stdio.h>
#include <conio.h>

#define PORT1 0x3E8 /* Port Address Goes Here */


#define INTVECT 0x0C /* Com Port's IRQ here (Must also change PIC setting) */

/* Defines Serial Ports Base Address */


/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */

int bufferin = 0;
int bufferout = 0;
unsigned char ch;
char buffer[1025];

void interrupt (*oldport1isr)();

void interrupt PORT1INT() /* Interrupt Service Routine (ISR) for PORT1 */


{
int c;
do { c = inportb(PORT1 + 5);
if (c & 1) {buffer[bufferin] = inportb(PORT1);
bufferin++;
if (bufferin == 1024) {bufferin = 0;}}
}while (c & 1);
outportb(0x20,0x20);
}

void main(void)
{
int c;
outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */

oldport1isr = getvect(INTVECT); /* Save old Interrupt Vector of later


recovery */

setvect(INTVECT, PORT1INT); /* Set Interrupt Vector Entry */


/* COM1 - 0x0C */
/* COM2 - 0x0B */
/* COM3 - 0x0C */
/* COM4 - 0x0B */

/* PORT 1 - Communication Settings */

outportb(PORT1 + 3 , 0x80); /* SET DLAB ON */


outportb(PORT1 + 0 , 0x0C); /* Set Baud rate - Divisor Latch Low Byte */
/* Default 0x03 = 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 57,600 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
outportb(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outportb(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
outportb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outportb(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */

outportb(0x21,(inportb(0x21) & 0xEF)); /* Set Programmable Interrupt Controller */


/* COM1 (IRQ4) - 0xEF */
/* COM2 (IRQ3) - 0xF7 */
/* COM3 (IRQ4) - 0xEF */
/* COM4 (IRQ3) - 0xF7 */

outportb(PORT1 + 1 , 0x01); /* Interrupt when data received */

printf("\nSample Serial ADC Program. Press ESC to quit \n");

do {

if (bufferin != bufferout){ch = buffer[bufferout];


bufferout++;
if (bufferout == 1024) {bufferout = 0;}
printf("%u\n",ch);
printf("%f volts\n",(float)ch/256 * 5);}

if (kbhit()){c = getch();
outportb(PORT1, c);}

} while (c !=27);

outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */


outportb(0x21,(inportb(0x21) | 0x10)); /* MASK IRQ using PIC */
/* COM1 (IRQ4) - 0x10 */
/* COM2 (IRQ3) - 0x08 */
/* COM3 (IRQ4) - 0x10 */
/* COM4 (IRQ3) - 0x08 */
setvect(INTVECT, oldport1isr); /* Restore old interrupt vector */

}
The above source code was copied from the example given in the Interfacing the Serial /
RS232 Port. The only modifications made was to change the Baud rate to 9600 BPS and
include an optional line, shown below, to calculate the voltage present at the ADC's pins.
printf("%f volts\n",(float)ch/256 * 5);}
The program can be modified to start the conversion at fixed intervals and to log the recei
result to file. This makes the above circuit more useful, than it is currently presented.
Data Sheets
Data Sheets for all the I.C's used in this example can sought on-line from the Internet. This
a list of where the Author obtained his data sheets from. Some semiconductors may be
available from other manufacturers. The URL for the manufacturer's homepage is given. Y
will need to search for the data sheet using their site's search engines.
I.C's Name Part Number Link to Manufacturers Homepa
CMOS Universal Asynchronous Receiver/Transmitter (UART) CDP6402 Harris Semiconductor
RS-232 Level Converter MAX232 Maxim Integrated Products
8 Bit Analog to Digital Converter ADC0804 Harris Semiconductor
14 Stage Binary Counter 74HC4060 National Semiconductor

You might also like