Serial Data Transmission
Here's a primer about using UARTs in embedded systems.Published in Embedded Systems Programming, July, 1995
By Jack Ganssle
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.
Suggestion: Sign up for my free newsletter which discusses data communications of all sorts from time to time. |
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.
- https://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.