145
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. Chapt er Name Description Page s 1 Introduction Includes the motivation and scope for USB. The most important piece of information in this chapter is to make reference to the Universal Serial Bus Device Class Specifications. No need reading this chapter. 2 2 Terms and Abbreviation s This chapter is self-explanatory and a necessary evil to any standard. 8 3 Background Specifies the goals of USB which are Plug’n’Play and simplicity to the end user (not developer). Introduces Low, Full and High Speed ranges with a feature list straight from marketing. No need reading this chapter either. 4 4 Architectura l Overview This is where you can start reading. This chapter provides a basic overview of a USB system including topology, data rates, data flow types, basic electrical specs etc. 10 5 USB Data Flow Model 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 on each of the data flow types (Control, Interrupt, Isochronous and Bulk). While it’s important to know 60

USB in a NutShell

Embed Size (px)

Citation preview

Page 1: USB in a NutShell

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

1 IntroductionIncludes the motivation and scope for USB. The most important piece of information in this chapter is to make reference to the Universal Serial Bus Device Class Specifications. No need reading this chapter.

2

2Terms and Abbreviations

This chapter is self-explanatory and a necessary evil to any standard. 8

3 BackgroundSpecifies the goals of USB which are Plug’n’Play and simplicity to the end user (not developer). Introduces Low, Full and High Speed ranges with a feature list straight from marketing. No need reading this chapter either.

4

4Architectural Overview

This is where you can start reading. This chapter provides a basic overview of a USB system including topology, data rates, data flow types, basic electrical specs etc.

10

5USB Data Flow Model

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 on each of the data flow types (Control, Interrupt, Isochronous and Bulk). While it’s important to know each transfer type and its properties it is a little heavy on for a first reader.

60

6 Mechanical

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, 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.

33

7 Electrical

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 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.

75

Page 2: USB in a NutShell

8 Protocol Layer

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 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.

45

9USB Device Frame Work

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 enumeration and request codes (set address, get descriptor etc) which make up the most common protocol layer USB programmers and designers will ever see. This chapter is a must read in detail.

36

10USB Host Hardware and Software

This chapter covers issues relating to the host. This includes frame and microframe generation, host controller requirements, software mechanisms and the universal serial bus driver model. Unless you are designing Hosts, you can skip this chapter.

23

11 Hub SpecificationDetails the workings of USB hubs including hub configuration, split transactions, standard descriptors for hub class etc. Unless you are designing Hubs, you can skip this chapter.

143

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 Overviewo 5 - USB Data Flow Modelo 9 - USB Device Frame Work, ando 10 - USB Host Hardware and Software.

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

o 4 - Architectural Overviewo 5 - USB Data Flow Modelo 6 - Mechanical, ando 7 - Electrical.

USB in a NutShell for Peripheral DesignersNow 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

Page 3: USB in a NutShell

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

Page 4: USB in a NutShell

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.

ConnectorsAll 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.

Page 5: USB in a NutShell

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 Function1 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.

ElectricalUnless 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

Page 6: USB in a NutShell

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 IdentificationA 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+

Page 7: USB in a NutShell

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.

Page 8: USB in a NutShell

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 CurrentSuspend 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

Page 9: USB in a NutShell

mobile phones and mobile organisers. The power consumption pulled from these devices will adversely effect the operating life of the battery.

Entering Suspend ModeA 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 RateAnother 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 ProtocolsUnlike 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), ano Optional Data Packet, (Containing the payload) and ao Status Packet (Used to acknowledge transactions and to provide a means of error correction)

Page 10: USB in a NutShell

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 FieldsData 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 Token1001 IN Token0101 SOF Token1101 SETUP Token

Data

0011 DATA01011 DATA10111 DATA21111 MDATA

Handshake

0010 ACK Handshake1010 NAK Handshake1110 STALL Handshake0110 NYET (No Response Yet)

Special

1100 PREamble1100 ERR1000 Split0100 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.

Page 11: USB in a NutShell

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 TypesUSB 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.

Page 12: USB in a NutShell

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.

Sync PIDFrame Number

CRC5 EOP

USB FunctionsWhen 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

Page 13: USB in a NutShell

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.

EndpointsEndpoints 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.

PipesWhile 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

Page 14: USB in a NutShell

The Universal Serial Bus specification defines four transfer/endpoint types, o Control Transfers o Interrupt Transfers o Isochronous Transfers o Bulk Transfers

Control TransfersControl 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.

Page 15: USB in a NutShell

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.

Page 16: USB in a NutShell

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 pictureNow 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 EOPAddress & 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 EOPAddress & Endpoint Number

Page 17: USB in a NutShell

2. Data1 Packet Sync PID Data1 CRC16 EOPFirst 8 Bytes of Device Descriptor

3. Ack Handshake Sync PID EOP 

Host Acknowledges Packet

1. In Token Sync PID ADDR ENDP CRC5 EOPAddress & 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 EOPAddress & Endpoint Number

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

3. Ack Handshake Sync PID EOP  Device Ack. Entire

Transaction

Interrupt TransfersAny 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

Page 18: USB in a NutShell

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

Page 19: USB in a NutShell

if an error occurred with the endpoint consequently and its halt bit has been set, it returns a STALL.

Isochronous TransfersIsochronous 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 TransfersBulk 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

Page 20: USB in a NutShell

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

Page 21: USB in a NutShell

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 ManagementThe 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 DescriptorsAll 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

Page 22: USB in a NutShell

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.

Page 23: USB in a NutShell

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 DescriptorsAll 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 NumberSize of Descriptor in Bytes

1 bDescriptionType 1 ConstantDescriptorType

2 ... nStart of parameters for descriptor

Device DescriptorsThe 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.

Page 24: USB in a NutShell

Offset Field Size Value Description

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

1 bDescriptorType 1 ConstantDevice Descriptor (0x01)

2 bcdUSB 2 BCDUSB 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 SubClassSubclass Code (Assigned by USB Org)

6 bDeviceProtocol 1 ProtocolProtocol Code (Assigned by USB Org)

7 bMaxPacketSize 1 NumberMaximum Packet Size for Zero Endpoint. Valid Sizes are 8, 16, 32, 64

8 idVendor 2 IDVendor ID (Assigned by USB Org)

10 idProduct 2 IDProduct ID (Assigned by Manufacturer)

12 bcdDevice 2 BCDDevice Release Number

14 iManufacturer 1 IndexIndex of Manufacturer String Descriptor

15 iProduct 1 IndexIndex of Product String Descriptor

16 iSerialNumber 1 IndexIndex of Serial Number String Descriptor

17 bNumConfigurations 1 IntegerNumber 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

Page 25: USB in a NutShell

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 DescriptorsA 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 NumberSize of Descriptor in Bytes

1 bDescriptorType 1 ConstantConfiguration Descriptor (0x02)

2 wTotalLength 2 NumberTotal length in bytes of data returned

4 bNumInterfaces 1 NumberNumber of Interfaces

Page 26: USB in a NutShell

5 bConfigurationValue 1 NumberValue to use as an argument to select this configuration

6 iConfiguration 1 IndexIndex 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 mAMaximum 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.

Page 27: USB in a NutShell

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 DescriptorsThe 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 NumberSize of Descriptor in Bytes (9 Bytes)

1 bDescriptorType 1 ConstantInterface Descriptor (0x04)

2 bInterfaceNumber 1 NumberNumber of Interface

3 bAlternateSetting 1 NumberValue used to select alternative setting

4 bNumEndpoints 1 NumberNumber of Endpoints used for this interface

5 bInterfaceClass 1 ClassClass Code (Assigned by USB Org)

6 bInterfaceSubClass 1 SubClassSubclass Code (Assigned by USB Org)

7 bInterfaceProtocol 1 ProtocolProtocol Code (Assigned by USB Org)

8 iInterface 1 IndexIndex 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.

Page 28: USB in a NutShell

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 DescriptorsEndpoint 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 NumberSize of Descriptor in Bytes (7 bytes)

1 bDescriptorType 1 ConstantEndpoint Descriptor (0x05)

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

3 bmAttributes 1 Bitmap Bits 0..1 Transfer Type 00 = Control01 = Isochronous10 = Bulk11 = Interrupt

Bits 2..7 are reserved. If Isochronous endpoint, Bits 3..2 = Synchronisation Type (Iso Mode)

00 = No Synchonisation01 = Asynchronous10 = Adaptive11 = Synchronous

Bits 5..4 = Usage Type (Iso Mode) 00 = Data Endpoint01 = Feedback Endpoint10 = Explicit Feedback Data Endpoint11 = Reserved

4 wMaxPacketSize 2 NumberMaximum Packet Size this endpoint is capable of sending or receiving

Page 29: USB in a NutShell

6 bInterval 1 NumberInterval 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 DescriptorsString 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 NumberSize of Descriptor in Bytes

1 bDescriptorType 1 ConstantString Descriptor (0x03)

2 wLANGID[0] 2 numberSupported Language Code Zero(e.g. 0x0409 English - United States)

4 wLANGID[1] 2 numberSupported Language Code One(e.g. 0x0c09 English - Australian)

n wLANGID[x] 2 numberSupported 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,

Page 30: USB in a NutShell

Offset Field Size Value Description

0 bLength 1 NumberSize of Descriptor in Bytes

1 bDescriptorType 1 ConstantString Descriptor (0x03)

2 bString n UnicodeUnicode Encoded String

The Setup PacketEvery 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 Direction0 = Host to Device1 = Device to Host

Page 31: USB in a NutShell

D6..5 Type0 = Standard1 = Class2 = Vendor3 = ReservedD4..0 Recipient0 = Device1 = Interface2 = Endpoint3 = Other4..31 = Reserved

1 bRequest 1 ValueRequest

2 wValue 2 ValueValue

4 wIndex 2Index or Offset Index

6 wLength 2 Count Number of bytes to transfer if there is 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

Page 32: USB in a NutShell

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 RequestsThere 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

0000 0000bCLEAR_FEATURE (0x01)

Feature Selector

Zero Zero None

0000 0000b SET_FEATURE (0x03)Feature Selector

Zero Zero None

0000 0000b SET_ADDRESS (0x05)Device Address

Zero Zero None

1000 0000bGET_DESCRIPTOR (0x06)

Descriptor Type & Index

Zero or Language ID

Descriptor Length

Descriptor

0000 0000bSET_DESCRIPTOR (0x07)

Descriptor Type & Index

Zero or Language ID

Descriptor Length

Descriptor

1000 0000bGET_CONFIGURATION (0x08)

Zero Zero 1Configuration Value

0000 0000bSET_CONFIGURATION (0x09)

Configuration Value

Zero Zero None

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

ReservedRemote Wakeup

Self 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

Page 33: USB in a NutShell

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 RequestsThe 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

1000 0001bGET_STATUS (0x00)

Zero Interface TwoInterface Status

0000 0001b CLEAR_FEATURE (0x01)

Feature Selector

Interface Zero None

0000 0001bSET_FEATURE (0x03)

Feature Selector

Interface Zero None

1000 0001bGET_INTERFACE (0x0A)

Zero Interface OneAlternate Interface

0000 0001bSET_INTERFACE (0x11)

Alternative Setting

Interface Zero None

Page 34: USB in a NutShell

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 RequestsStandard Endpoint requests come in the four varieties listed below.

bmRequestType bRequest wValue Windex wLength Data

1000 0010bGET_STATUS (0x00)

Zero Endpoint TwoEndpoint Status

0000 0010bCLEAR_FEATURE (0x01)

Feature Selector

Endpoint Zero None

0000 0010bSET_FEATURE (0x03)

Feature Selector

Endpoint Zero None

1000 0010bSYNCH_FRAME (0x12)

Zero Endpoint Two FrameNumber

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.

Page 35: USB in a NutShell

o A Synch Frame request is used to report an endpoint synchronisation frame.

Interfacing the Standard Parallel Port

Table of Contents

Introduction to Parallel PortsHardware Properties

Centronics?Port Addresses

Software Registers - Standard Parallel Port (SPP)Bi-directional Ports

Using the Parallel Port to Input 8 BitsNibble Mode

Using the Parallel Port's IRQParallel Port Modes in BIOS

Parallel Port Modes and the ECP's Extended Control RegisterPDF 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)

Page 36: USB in a NutShell

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

Page 37: USB in a NutShell

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-Type 25)

Pin No (Centronics)

SPP SignalDirection

In/outRegister

Hardware 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

12 12Paper-Out / Paper-

EndIn Status  

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  

17 36nSelect-Printer /

nSelect-InIn/Out Control Yes

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?

Page 38: USB in a NutShell

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.

Page 39: USB in a NutShell

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 2Table 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){

Page 40: USB in a NutShell

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-1)

Bit 7 Data 7

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 0Table 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 ReservedTable 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

Page 41: USB in a NutShell

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 5Enable 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 StrobeTable 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.

Page 42: USB in a NutShell

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.

Page 43: USB in a NutShell

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

Page 44: USB in a NutShell

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,

Page 45: USB in a NutShell

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 - [email protected] */

#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)

Page 46: USB in a NutShell

{ 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.

Page 47: USB in a NutShell

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) ModeEPP1.7 and SPP ModeEPP1.9 and SPP ModeECP ModeECP and EPP1.7 ModeECP 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.

Page 48: USB in a NutShell

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 EmptyTable 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

Page 49: USB in a NutShell

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 / PS/2 Mode

Behaves as a SPP in bi-directional mode. Bit 5 will place the port in reverse mode.

Parallel Port FIFO Mode

In this mode, any data written to the Data FIFO will be sent to the peripheral using 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 Mode

Standard mode for ECP use. This mode uses the ECP Handshake described in 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 Mode/Reserved

This will enable EPP Mode, if available. Under BIOS, if ECP mode is set then it's 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 Mode

While in this mode, any data written to the Test FIFO Register will be placed into 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 Mode

In this mode, the two configuration registers, cnfgA & cnfgB become available at 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 PortEPP Hardware Properties

The EPP HandshakeEPP Data Write Cycle

EPP Address Write CycleEPP Data Read Cycle

EPP Address Read Cycle

Page 50: USB in a NutShell

The EPP's Software RegistersEPP 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

1 Strobe Write OutA low on this line indicates a Write, High indicates a Read

2-9 Data 0-7 Data 0-7 In-Out Data Bus. Bi-directional

10 Ack Interrupt InInterrupt Line. Interrupt occurs on Positive (Rising) Edge.

11 Busy Wait InUsed for handshaking. A EPP cycle can be started when low, and finished when high.

12Paper Out /

EndSpare In Spare - Not Used in EPP Handshake

13 Select Spare In Spare - Not Used in EPP Handshake

14Auto

LinefeedData Strobe Out When Low, indicates Data transfer

15 Error / Fault Spare In Spare - Not used in EPP Handshake

16 Initialize Reset Out Reset - Active Low

Page 51: USB in a NutShell

17Select Printer

Address Strobe

Out When low, indicates Address transfer

18-25

Ground Ground GND Ground

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

Figure 1. Enhanced Parallel Port 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.

EPP Address Write Cycle

Page 52: USB in a NutShell

Figure 2. Enhanced Parallel Port 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.

EPP Data Read Cycle

Figure 3. Enhanced Parallel Port 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 high4. Data is read from Parallel Port Pins.5. nData Strobe is de-asserted.6. EPP Data Read Cycle Ends.

EPP Address Read Cycle

Page 53: USB in a NutShell

Figure 4. Enhanced Parallel Port 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 high4. Data is read from Parallel Port Pins.5. nAddr Strobe is de-asserted.6. EPP Address Read Cycle Ends.

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.

Page 54: USB in a NutShell

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

Page 55: USB in a NutShell

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 PortECP Hardware Properties

The ECP HandshakeECP Forward Data Cycle

ECP Forward Command CycleECP Reverse Data Cycle

ECP Reverse Command CycleECP Handshake vs SPP Handshake

RLE - Run Length EncodingECP 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.

Page 56: USB in a NutShell

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.

PinSPP

SignalECP Signal IN/OUT Function

1 Strobe HostCLK OutA low on this line indicates, that there is valid data at the 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

10 Ack PeriphCLK InA low on this line indicates, that there is valid data at the Device. When this pin is de-asserted, the +ve clock edge should be used to shift the data into the Host.

11 Busy PeriphAck InWhen in reverse direction a HIGH indicates Data, while a LOW indicates a Command Cycle.In forward direction, functions as PeriphAck.

12Paper Out /

EndnAckReverse In When Low, Device acknowledges Reverse Request.

13 Select X-Flag In Extensibility Flag

14Auto

LinefeedHost Ack Out

When in forward direction a HIGH indicates Data, while a LOW indicates a Command Cycle.In reverse direction, functions as HostAck.

15Error / Fault

PeriphRequest InA LOW set by the device indicates reverse data is available

16 Initialize nReverseRequest Out A LOW indicates data is in reverse direction

17Select Printer

1284 Active OutA HIGH indicates Host is in 1284 Transfer Mode. Taken low to terminate.

18-25

Ground Ground GND Ground

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.

Page 57: USB in a NutShell

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

Figure 1. Enhanced Capabilities Port 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.

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.

Page 58: USB in a NutShell

Figure 2. Enhanced Capabilities Port Forward Command Cycle.

ECP Reverse Data Cycle

Figure 3. Enhanced Capabilities Port 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.8. Host sends it's acknowledgment of the byte by de-asserting HostAck low.

ECP Reverse Command Cycle

Page 59: USB in a NutShell

Figure 4. Enhanced Capabilities Port 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.

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.

Page 60: USB in a NutShell

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/WriteTable 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

Page 61: USB in a NutShell

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 EmptyTable 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 / PS/2 Mode

Behaves as a SPP in Bi-directional (Reverse) mode.

Parallel Port FIFO Mode

In this mode, any data written to the Data FIFO will be sent to the peripheral using 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 Mode

Standard Mode for ECP Use. This mode uses the ECP Handshake, already described.

EPP Mode/Reserved

On some chipsets, this mode will enable EPP to be used. While on others, this mode is still reserved.

Reserved Currently ReservedFIFO Test Mode

While in this mode, any data written to the Test FIFO Register will be placed into 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 Mode

In this mode, the two configuration registers, cnfgA & cnfgB become available at their designated Register Addresses.

Page 62: USB in a NutShell

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

Page 63: USB in a NutShell

2 Host Recovery : Pipeline/Transmitter Byte included in FIFO?

0In forward direction, the 1 byte in the transmitter pipeline doesn't affect FIFO Full.

1In forward direction, the 1 byte in the transmitter pipeline is include as 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 BytesTable 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

Page 64: USB in a NutShell

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 7Table 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

Page 65: USB in a NutShell

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

Page 66: USB in a NutShell

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 - [email protected] *//* *//* 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)*/

Page 67: USB in a NutShell

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

Page 68: USB in a NutShell

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 PlugsDTE/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'sTable of Registers

DLAB ? Interrupt Enable Register (IER)

Page 69: USB in a NutShell

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 RegisterPart 3 : Programming (PC's)

Polling or Interrupt Driven?Source Code - Termpoll.c (Polling Version)Source Code - Buff1024.c (ISR Version)

Interrupt VectorsInterrupt Service Routine

UART ConfigurationMain Routine (Loop)

Determining the type of UART via SoftwarePart 4 : External Hardware - Interfacing Methods

RS-232 WaveformsRS-232 Level Converters

Making use of the Serial Format8250 and compatable UART's

CDP6402, AY-5-1015 / D36402R-9 etc UARTsMicrocontrollers

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

Page 70: USB in a NutShell

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

DetectWhen the modem detects a "Carrier" from the modem at the other end 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

ReadyThis is the opposite to DSR. This tells the Modem that the UART is ready to link.

    RTS Request To

SendThis line informs the Modem that the UART is ready to exchange data.

     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.

Page 71: USB in a NutShell

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

Figure 2 : Loopback Plug Wiring Diagram

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 wired Loop Back plug which may vary from program to program.

Page 72: USB in a NutShell

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.

Page 73: USB in a NutShell

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

Page 74: USB in a NutShell

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

Pin 9 RCLKReceiver Clock Input. The frequency of this input should equal the 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

Pin 15 nBAUDOUTBaud Output - Output from Programmable Baud Rate Generator. 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

Pin 23 DDIS Driver Disable. This pin goes low when CPU is reading from

Page 75: USB in a NutShell

UART. Can be connected to Bus Transceiver in case of high capacity data bus.

Pin 24 nTXRDY Transmit Ready

Pin 25 nADSAddress Strobe. Used if signals are not stable during read or write 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 VoltsTable 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.

Page 76: USB in a NutShell

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 3Table 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 AddressTable 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 */

Page 77: USB in a NutShell

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

=0 Write - Transmitter Holding Buffer

=0 Read - Receiver Buffer

=1 Read/Write - Divisor Latch Low Byte

+ 1=0 Read/Write IER Interrupt Enable Register

=1 Read/Write - Divisor Latch High Byte

+ 2- Read IIR Interrupt Identification Register

- 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.

Page 78: USB in a NutShell

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 InterruptTable 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

Page 79: USB in a NutShell

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 PendingTable 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 7

Bit 7 Bit 6 Interrupt Trigger Level

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

Bit 3DMA Mode Select. Change status of RXRDY & TXRDY pins from mode 1 to mode 2.

Bit 2 Clear Transmit FIFO

Bit 1 Clear Receive FIFO

Page 80: USB in a NutShell

Bit 0 Enable FIFO'sTable 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

0Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register

Bit 6 Set Break Enable

Bits 3, 4 And 5

Bit 5 Bit 4 Bit 3 Parity Select

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

12 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word 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.

Page 81: USB in a NutShell

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 ReadyTable 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)

Page 82: USB in a NutShell

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 ReadyTable 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

Page 83: USB in a NutShell

Bit 1 Delta Data Set Ready

Bit 0 Delta Clear to SendTable 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 VectorsInterrupt Service Routine

UART ConfigurationMain Routine (Loop)

Determining the type of UART via SoftwarePart 4 : External Hardware - Interfacing Methods

RS-232 WaveformsRS-232 Level Converters

Making use of the Serial Format8250 and compatable UART's

CDP6402, AY-5-1015 / D36402R-9 etc UARTsMicrocontrollers

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.

Page 84: USB in a NutShell

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

Page 85: USB in a NutShell

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;

Page 86: USB in a NutShell

} } 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 TimerTable 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 ClockTable 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

Page 87: USB in a NutShell

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 {

Page 88: USB in a NutShell

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.

Page 89: USB in a NutShell

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

Page 90: USB in a NutShell

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!)

Page 91: USB in a NutShell

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

Pin No. Name Notes

Pin 1:8 D0:D7 Data Bus

Pin 9 RCLKReceiver Clock Input. The frequency of this input should equal the 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

Pin 15 nBAUDOUTBaud Output - Output from Programmable Baud Rate Generator. 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

Pin 23 DDISDriver Disable. This pin goes low when CPU is reading from UART. Can be connected to Bus Transceiver in case of high capacity data bus.

Pin 24 nTXRDY Transmit Ready

Pin 25 nADSAddress Strobe. Used if signals are not stable during read or write cycle

Pin 26 A2 Address Bit 2

Pin 27 A1 Address Bit 1

Pin 28 A0 Address Bit 0

Page 92: USB in a NutShell

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 VoltsTable 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

Figure 9 : Pinout of CDP6402

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 take effect.

Pin Number

Abbr. Full Name Notes

Pin 1 VDD + 5v Supply Rail  

Pin 2 NC Not Connected  

Pin 3 GND Ground  

Pin 4 RRDReceiver Register Disable

When driven high, outputs RBR8:RBR1 are High Impedance.

Pin 5:12RBR8:RBR1

Receiver Buffer Register

Receiver's Data Bus

Pin 13 PE Parity Error When High, A Parity Error Has Occurred.

Page 93: USB in a NutShell

Pin 14 FE Framing ErrorWhen High, A Framing Error Has Occurred. i.e. The Stop Bit was not a Logic 1.

Pin 15 OE Overrun ErrorWhen High, Data has been received but the nData Received Reset had not yet been activated.

Pin 16 SFD Status Flag DisableWhen High, Status Flag Outputs (PE, FE, OE, DR and TBRE) are High Impedance

Pin 17 RRCReceiver Register Clock

x16 Clock input for the Receiver Register.

Pin 18 nDRRData Received Reset

Active Low. When low, sets Data received Output Low (i.e. Clears DR)

Pin 19 DR Data ReceivedWhen High, Data has been received and placed on outputs RBR8:RBR1.

Pin 20 RRIReceiver Register In

RXD - Serial Input. Connect to Serial Port, Via RS-232 receiver.

Pin 21 MR Master ResetResets the UART. UART should be reset after applying power.

Pin 22 TBRETransmitter Buffer Register Empty

When High, indicates that Transmitter Buffer Register is Empty, thus all bits including the stop bit have been sent.

Pin 23 nTBRLTransmitter Buffer Load / Strobe

Active Low. When low, data present on TBR8:TBR1 is placed in Transmitter Buffer Register. A Low to High Transition on this pin, then sends the data.

Pin 24 TRETransmitter Register Empty

When High, Transmitter Register is Empty, thus can accept another byte of data to be sent.

Pin 25 TROTransmitter Register Out (TXD)

TXD - Serial Output. Connect to Serial Port, Via RS-232 Transmitter.

Pin 26:33TBR8:TBR1

Transmitter Buffer Register

Data Bus, for Transmitter. Place Data here which you want to send.

Pin 34 CRLControl Register Load

When High, Control Register (PI, SBS, CLS2, CLS1, EPE) is Loaded. Can be tied high, so changes on these pins occur instantaneously.

Pin 35 PI Parity InhibitWhen High, No Parity is Used for Both Transmit and Receive. When Low, Parity is Used.

Pin 36 SBS Stop Bit SelectA High selects 2 stop bits. (1.5 for 5 Character Word Lengths) A Low selects one stop bit.

Pin 37:38CLS2:CLS1

Character Length Select

Selects Word Length. 00 = 5 Bits, 01 = 6 Bits, 10 = 7 Bits and 11 = 8 Bits.

Pin 39 EPE Even Parity EnableWhen High, Even Parity is Used, When Low, Odd Parity is Used.

Pin 40 TRCTransmitter Register Clock

16x Clock input for Transmitter.

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-

Page 94: USB in a NutShell

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.

Figure 10 : Baud Rate Generator using a 74HC4060

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 BPSTable 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

Page 95: USB in a NutShell

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

InterruptsWhat are Interrupts?

Interrupts and Intel ArchitectureHardware Interrupts

ProgrammingImplementing the Interrupt Service Routine (ISR)

Using your new Interrupt Service RoutineThe Programmable Interrupt Controller's Operation

The Programmable Interrupt ControllerIRQ2/IRQ9 Redirection

The Programmable Interrupt Controller's RegistersProgrammable Interrupt Controller's Addresses

Initialization Command Words (ICWs)Operation Control Words (OCWs)

FeedbackFeedback

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.

Page 96: USB in a NutShell

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

Page 97: USB in a NutShell

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 TimerTable 2 : PIC1 Operation Control Word 1 (0x21)

Page 98: USB in a NutShell

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 ClockTable 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.

Page 99: USB in a NutShell

void interrupt yourisr() /* Interrupt Service Routine (ISR) */{ disable();

/* Body of ISR goes here */

oldhandler();

outportb(0x20,0x20); /* Send EOI to PIC1 */ enable();}void interrupt yourisr() defines this function as an Interrupt Service Routine. disable(); 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 */

Page 100: USB in a NutShell

/* 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.

Page 101: USB in a NutShell

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.

Page 102: USB in a NutShell

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.

Page 103: USB in a NutShell

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,20OUT A0,AL ; Send EOI to PIC2INT 0A ; Call ISR for IRQ2IRET

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

20h

Write Initialization Command Word 1 (ICW1)

Write Operation Command Word 2 (OCW2)

Write Operation Command Word 3 (OCW3)

Read Interrupt Request Register (IRR)

Read In-Service Register (ISR)

21h

Write Initialization Command Word 2 (ICW2)

Write Initialization Command Word 3 (ICW3)

Write Initialization Command Word 4 (ICW4)

Read/Write Interrupt Mask Register (IMR)Table 4 : Addresses/Registers for PIC1

PIC2 Addresses . . .

Address Read/Write Function

A0h

Write Initialization Command Word 1 (ICW1)

Write Operation Command Word 2 (OCW2)

Write Operation Command Word 3 (OCW3)

Read Interrupt Request Register (IRR)

Read In-Service Register (ISR)

A1h

Write Initialization Command Word 2 (ICW2)

Write Initialization Command Word 3 (ICW3)

Write Initialization Command Word 4 (ICW4)

Read/Write Interrupt Mask Register (IMR)Table 5 : Addresses/Registers for PIC2

Page 104: USB in a NutShell

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

31 Level Triggered Interrupts

0 Edge Triggered Interrupts

21 Call Address Interval of 4

0 Call Address Interval of 8

11 Single PIC

0 Cascaded PICs

01 Will be Sending ICW4

0 Don't need ICW4Table 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 - A8Table 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)

Page 105: USB in a NutShell

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 SlaveTable 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 7Table 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

41 Special Fully Nested Mode

0 Not Special Fully Nested Mode

3:2 0x Non - Buffered Mode

Page 106: USB in a NutShell

10 Buffered Mode - Slave

11 Buffered Mode - Master

11 Auto EOI

0 Normal EOI

01 8086/8080 Mode

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 IRQ0Table 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

Page 107: USB in a NutShell

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 15Table 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

21 Poll Command

0 No Poll Command

1:0 00 Reserved

01 Reserved

10 Next Read Returns Interrupt Request Register

11 Next Read Returns In-Service RegisterTable 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 Port, but it was no use to you as you only have

Page 108: USB in a NutShell

one Parallel Port and the Printer is connected to it. Or maybe you are using a Palmtop computer which doesn't have a Parallel Port. If 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 starts the analog conversion and sends data back to the computer using the 8N1 serial format at 9600 BPS. The circuit is based on a CDP6402C or equivalent UART. This, if you want to call it, is the brains of the operation and performs the conversion of Parallel data to a Serial format for transmission. The Analog to Digital Conversion is done by the ADC0804, while the MAX232 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 work! - The Data Received (DR) output is Active High, while the nWrite (WR) input to the ADC is a Active Low. This circuit is quite correct. If we look at the ADC's operation, on a high to low transition of the

Page 109: USB in a NutShell

nWrite input the internal Successive Approximation and Shift Registers are reset. Provided the nWrite line remains in this state the ADC will remain reset. The conversion 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 is no data to be received, thus the ADC will remain in the reset mode. When data is received by 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 conversion of the analog voltage on it's pins. Once the conversion is finished, it's nINTR (Interrupt) line will become active low. This signal is then used to tell the UART to send the data residing on it's Transmitter Buffer Register inputs (TBR8:TBR1). nINTR is also connected to the UART's Data 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 <[email protected]> */

#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){

Page 110: USB in a NutShell

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

Page 111: USB in a NutShell

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 received 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 is 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. You will need to search for the data sheet using their site's search engines. I.C's Name Part Number Link to Manufacturers HomepageCMOS Universal Asynchronous Receiver/Transmitter (UART) CDP6402 Harris SemiconductorRS-232 Level Converter MAX232 Maxim Integrated Products8 Bit Analog to Digital Converter ADC0804 Harris Semiconductor14 Stage Binary Counter 74HC4060 National Semiconductor