Serial Data Transmission
Here's a primer about using UARTs in embedded systems.
Published in Embedded Systems Programming, July, 1995
 |
For hints, tricks and ideas about better ways to build embedded systems, subscribe to The Embedded Muse, a free biweekly e-newsletter. No hype, just down to earth embedded talk. 23,000 other engineers subscribe. It takes just a few seconds (all we need is your email address, which is shared with absolutely no one) to subscribe to the Embedded Muse. |
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.
- http://www.ganssle.com/articles/auart.html - In really low end systems
you might not need a UART - this paper shows how to do one in
software.
|