|
Serial
Data Transmission
Copyright 1995,
Jack G. Ganssle
Abstract
Here's a primer
about using UARTs in embedded systems.
Published in
Embedded Systems Programming, July, 1995
If you've been
foolish enough to read this column with any regularity then you probably know
my mantra: just as no hardware designer can
competently create new microprocessor designs without a thorough understanding
of software tradeoffs, we software folks must be
knowledgeable about what might appear to be strictly hardware details.
At least weekly
a frustrated manager calls with tales of employee woe. Either the hardware
and software folks are in opposite camps,
tossing verbal grenades at each other, or projects have ground to an expensive
halt because no one has the background to straddle the
hardware/software fence. The code seems fine; the logic is booleaning away,
but the system just doesn't work.
Who ya gonna
call?
The very cream
of the embedded crop, who bring home the big bucks and get the greatest respect,
the babes, and who get to trash hotel
rooms with Kiss and Softaid, are those engineers who can solve any problem,
regardless of source. Occasionally I cover serious hardware
issues here, since I feel it's so important every embedded designer is competent
with basic computer operation. It's your choice: be
master of any embedded system, or be content to plead ignorance when a problem
slips over some vague boundary. Serial Communications
Wires cost money
and are typically unreliable. It's fascinating to study the early development
of the telegraph, the first electric device
intended for long distance communication. The earliest versions used 5 wires,
each of which deflected a compass needle at the receiving
end. A crude binary code (the first four needles deflecting left and the last
going right means "z") let signalers send messages
over distances approaching a kilometer.
It wasn't long
before engineers compacted the code into a stream of data, recognizable on
a single compass needle. By 1838 von Steinheil
realized a single wire was enough; the earth itself could form a return circuit.
Samuel Morse
defined a standard set of codes for each character. He tried to minimize the
code's "cost"; the more frequently
used letters used shorter codes. This greatly predates our obsession with
compression, but was surely a step in the same direction.
Now, of course,
Morse's original code is no longer used. Modern code is properly called "International
Morse" to differentiate
it from it's dead predecessor.
Conceptually,
Morse took the first critical steps towards modern data transmission. A complex
message could be encoded onto a single wire
by defining a character as a string of components, each of which was transmitted
one after the other (hence the name "serial
transmission").
Long, long ago
in the pre-computer dark ages the military and weather services, later followed
by the news agencies, sent text data
between sites using various versions of a device called the teletype. These
beasts were purely mechanical - somewhat like early electric
typewriters - and converted keystrokes to series of ones and zeroes representing
each character.
Early models
encoded characters into Baudot, a 5 bit code... resulting in a maximum possible
32 characters. Needless to say, you can't
even encode all of the letters and numbers in a 32 bit space, so "shift
up" and "shift down" characters, which toggled
character sets were added, essentially doubling capacity to 64 characters
(minus the two allocated to shifting).
The 5 bit code
used by these early teletypes was quite a bit different than Morse code. Each
bit could be only a one or a zero, and always
occupied the same amound of time. Morse, of course, uses a different number
of elements (dots and dashes) in each character, and
differentiates the two possible elements by length (a dash is three times
as long as a dot).
In the early
70s, as a broke college kid, I built a 12 bit machine out of TTL components.
A scrounged model 15 teletype did console duty.
Each character started a quarter horsepower motor that sequenced an breathtaking
number of levers and shafts to select the correct print
hammer. The entire room vibrated in sync to its rhythm. Visitors stared aghast
at the beast; the neighbors downstairs pounded in
syncopated rhythm.
Clearly, a 5
bit code just won't cut it for real computing applications. Baudot was eventually
replaced by ASCII, though not until other
variations (EBCDIC, FIELDDATA, etc.) were tried. Thankfully the CRT terminal
came along, it's screen acting as a digital version of the
hideously mechanical teletype. RS-232
RS-232, as has
been extended for microcomputer communications, defines signal levels, transfer
parameters, and cabling for serial
communications over short (under about 50 feet) distances. Of course, different
vendors implement various aspects of the standard in
different ways, so devices hardly ever work together without some frantic
wire swapping.
RS-232 communications
is always serial, taking place one bit at a time. Each of the 8 bits of a
byte are sequentially sent out over a
single pair of wires in a specific order: the least significant bit goes first,
followed by bit 1, etc.
So, this oh-so-modern
communication method is no more than a slight upgrade over the von Steinheil's
ancient compass needles. There's
little new under the sun.
All RS-232 communications
takes place at a baud rate agreed on by both the driver and receiver. 9600
baud means that each bit of the
character stream takes 1/9600 second to transmit - about 100 microseconds.
Since the transmitter sends neither clock nor other timing
information, it's up to the receiver to figure out when to sample the stream
to determine if an individual bit is a one or a zero.
Clearly, if the transmitter and receiver each are set to different baud rates,
the receiver will never sample the input stream at the
correct time.
RS-232 data embeds
the 8 bit character within at least two other bits as shown in Figure 1. Every
character starts with a
"start" bit - the transmitter drives the line to a logic
one state for exactly one bit period (e.g., at 9600 baud, 1/9600
second). Bit zero follows. To prevent one character running into another a
stop bit - a logic zero - follows the character.
These two bits
"frame" the character. You may have run into a "framing
error" from time to time. This simply indicates
that the start/stop sequence was not properly detected by the receiver.
Suppose the transmitter
sends the character "A", which is hex 41. Figure two shows
the data on the line. When the link is idle
(no data being sent) it is in the Marking state (the line is more negative
than -3 volts). The Start bit, which puts the line into the
Spacing state (more positive than +3 volts) for one bit period, is sent first
and serves to announce that a character is on the way. The
receiver senses the start bit and sets itself up to read the incoming serialized
byte.
Data bits follow
Start. The least significant (data bit 0) goes first - in this case, a logic
1. One at a time, the other bits follow,
each being given exactly one bit time on the link. The "A"
has only two data bits, plus the stop bit, set to a one, as you can
clearly see in the figure.
After the entire
character has been transferred the line goes to the marking state for the
length of the stop bit - one or two bit times
depending on the protocol agreed to by the communicating devices. Stop bits
look like an idle line and give the receiver time to recover
before the next character starts.
Note that if
a parity bit is defined, it is inserted immediately after data bit 7.
The RS-232 standard
defines pinouts for Data Communications equipment (DCE) and Data Terminal
Equipment (DTE). Terminals are DTE.
Computers seem to be DTE or DCE depending on th` whim of the designer. The
IBM PC is DTE. The Wires
The RS-232 standard
defines 25 signals used to transmit data and control the communications channel.
Seldom do we see so many actually
used, which is one source of grief with connecting serial devices - it seems
each device requires a different number of the signals.
The following
table lists the most common signals in PC and embedded work, both for the
25 and 9 pin connector configurations.
Pin Number Direction
Pin Name
DB-9 DB-25
5 7 Ground
3 2 Transmitted data from DTE
2 3 Received data from DCE
8 5 Clear to send (DCE ready to receive data)
7 4 Request to send (DTE ready to send data)
6 6 Data set ready (transmit device power is on)
4 20 Data terminal ready (receive device power is on)
The Hardware
Many embedded
systems save wiring by using a simple three-wire connection: ground, transmit
data, and receive data. Software handshaking
uses the X-on and X-off characters, transmitted buy the receiver, to throttle
the transmitter. With some clever electronics you can even
feed both data directions onto a single phone line pair, permitting world-wide
communications over the existing phone system.
There's only
one problem.
Computer busses
are wide. Even a small computer moves data around in 8 bit chunks. Either
the processor must painstakingly convert each
character to and from a bit stream, or we have to add hardware to do the work
automatically.
In fact, virtually
every system does use hardware. The UART (Universal Asynchronous Receiver/Transmitter)
or USART (Universal
Synchronous/Asynchronous Receiver/Transmitter) is a chip that does this conversion
for you. Asynchronous transmission is exactly what
we've been looking at: the characters move at a pace determined by the baud
rate. Synchronous transmission is relative to an external
clocking source, one that is often transmitted on an additional wire.
UARTs were some
of the earliest integrated peripherals. They are complex devices - thousands
of transistors - not something you'd care to
create out of ordinary TTL components.
A single UART
handles both transmission and reception of data. Shift registers convert between
the parallel computer data and the serial
stream, with start, stop and parity bits. There's not much complexity to a
pair of shift registers. Most of the trouble lies in figuring
out exactly when to sample a received data stream. Suffice to say that the
silicon wizards work out the timing.
The hardware
engineer views a UART as a device connected to the serial level shifters by
receiver, transmit, and perhaps handshaking
wires. It goes to the computer via an 8 bit data bus, a chip select signal,
and some other control lines.
Software gurus
never really see the serial stream. Characters are magically assembled in
the receiver; the software simply sees that data
is available, and then reads an I/O port to get the received character. Transmission
is the reverse: a single bit on some port indicates
that the transmitter is not busy, so you write a byte to the UART data port
and let the code go to its next duty. The UART takes care of
shifting it out the serial port. Software Issues
Most UARTs can
only queue one, or at best a handful, of characters in its internal buffers.
With software running at warp speed you will
surely overrun the buffers when trying to send a long string of data. Though
the device does supply software handshake lines (transmit
buffer empty and receive data available), it makes little sense to leave the
CPU idly polling the UART, waiting for a slow transmission,
when the time could be better used elsewhere. Every UART has some provision
for interrupt-based operation to better optimize the code.
Handling transmit
interrupts can be just a bit tricky, though. Generally the UART interrupts
on the transmit buffer empty condition,
telling the program it's time to write out the next transmit character. The
code starts the Interrupt Service Routine (ISR), pulls a
character from a software buffer, writes it to the UART, and returns. All
is well.
What happens
if there are no more characters to send? The transmit buffer empty condition
will be set forever; if the ISR returns having
done nothing (cleverly recognize that there were no more queued characters
to send), the interrupt will immediately restart the ISR,
starting an interrupt loop that brings the system to its knees.
The ISR must
recognize that it's work is done, and disable the transmitter (or, at least
disable transmit interrupts) by writing the
appropriate bytes to the UART's control register. Then, the transmit routine
should recognize that the UART is off, restart it, and only
then start queuing data again. Testing UART Code
When I'm writing
UART drivers, I always start testing the system using the transmitter in a
polled mode. I either set the emulator up to
send a single character to the UART repeatedly, or write a bit of code to
do the same. This controlled condition is very easy to
troubleshoot with an oscilloscope - if you send the letter "A",
expect to see the waveform in figure 2.
Being a generally
hopeful sort, I'll connect the embedded system to a terminal, or a computer
running a terminal program (like Procomm or
Windows Terminal) and burn a little incense before checking to see what I
earnestly hope to be correct data. Very occasionally the stream
immediately shows up on the terminal's stream. Well, OK, that hasn't happened
yet, but maybe soon...
Life being what
it is, though, usually the screen stays frustratingly blank. I'll grab a scope
probe and start looking at waveforms out of
the UART itself. If the line is dead, then there's a good chance the code
is just not setting up the UART right, or the UART is not
properly connected to the computer's bus. Check the usual suspects (chip select,
address and data lines). Double check the way your code
sets up the UART.
Remember that
you can watch every bit of the program's actions on the UART by synching the
scope on the device's chip select and examining
the data and control busses. This is not rocket science!
Sooner or later
the code will be right, and data will start to flow. Scope the level shifter
(typically a MAX232 or similar chip that
converts the computer's 5 volt levels to RS-232's plus and minus levels),
and the wiring. Use your brain: if the system is running at 9600
baud, the total bit time is just the number of bits in the transmitted character,
including start, stop, and parity. Use the scope to
check the rate - it could be wrong. If the screen shows scrambled characters,
the baud rate is almost certainly wrong... or an inversion
in the UART's output flips the levels from those in figure 2.
Once the transmitter
works, test reception by writing a program that reads the input and echoes
it to the screen. Put a weight on a key on
your terminal to generate a constant, scopable, data scream, and look for
data into the circuit, through the level shifters, and thence
into the UART.
Once you are
sure the data makes it to the UART, well, just make the code work right! Resources
http://www.ics.uci.edu/~archive/documents
- looks for the file rs232.doc.Z, which has a good, though dated, description
of the signal
lines. The Art of Electronics, by Paul Horowitz and Winfield Hill, 1989, Press
Syndicate of the University of Cambridge, Cambridge
England. Besides being a pretty good reference to RS-232, it's a fairly complete
look at all aspects of electronics. It's a good book that
I turn to regularly.
|