Serial Programming/Typical RS232 Hardware Configuration
This page lines out a typical serial RS232-hardware configuration as it can be found in computers and devices of all kinds. It should serve as a guideline and introduction to the direct programming of some serial hardware.
The provided information is generic in nature, since the actual serial hardware which can be found in some device or computer do vary. Often these days the hardware is integrated in one single chip, even with many other functions like a parallel port, unrelated to serial communications. Nevertheless this module provides a good overview about which logical or physical components do what, and where the programming of serial hardware actually happens.
The following figure provides an overview of the involved components, and how they are in principle connected:
RS232 +-----------+ +-----------+ +-----------+ +-----------+ Interface | Line | | | | Interface | | | -----------+ Driver / +---+ UART +---+ Logic +---+ CPU | | Receiver | | | | | | | +-----------+ +-----+-----+ +-----+-----+ +-----------+ | | | | +-----+-----+ | | Baud Rate | | | Generator +---------+ | | +-----------+
We will discuss the components and their purpose in turn.
Line Driver / Receiver
RS-232 communication uses voltage up to ±15V (some early specs even use ±25V). And it uses an inverse logic (high/true/1 is a negative voltage, low/false/0 is a positive voltage). These voltages are far too high for modern (and even older) computer logic. Therefore, RS-232 interfaces typically contain special hardware, a so-called line driver and a line receiver.
The line driver is responsible for converting typical computer voltage logic to the high voltages used on the RS-232 line, and for inverting the logic. This is used when the computer hardware transmits serial data. The voltage output is typically continuous short-circuit safe, as this is required by the RS-232 standard.
The line receiver is responsible for the inverse operation of the line driver. It converts incoming RS-232 signals to voltages safe for computer logic, and it of course also inverts the logic.
Actually, there is usually more than one line driver and receiver in use, because each incoming RS-232 signal in a RS-232 cable needs its own receiver, and each outgoing RS-232 signal needs its own driver. However, typically multiple drivers / receivers are combined in one chip. The chip as such also protects the computer logic from spikes which could happen on the serial wire. Some line driver / receiver chips even generate the necessary RS-232 voltage on-chip, while others need external power sources for these voltages.
Line drivers / receivers are typically not programmable. They are hard-wired into the logic and as such of no great concern for the programmer.
The UART (universal asynchronous receiver transmitter) is the heart of the serial hardware. It is a chip or part of a chip with the purpose to convert between parallel data and serial data. RS-232 UARTs also typically add the necessary start/stop and parity bits when transmitting, and decode this information when receiving.
A UART typically operates entirely on computer logic voltage. Its serial data input/output voltage is the computer logic voltage, not the serial line voltage. They leave the actual line interface to a particular line driver / receiver. This line driver / receiver not necessarily needs to be an RS-232 line driver / receiver, but could e.g. also be an RS-422 differential driver / receiver. This, and the fact that baud rate, parity, number of stop bits, number of data bits are programmable is the reason why UARTs are called universal. The distinction between UART and line driver / receiver blurs if they are both placed in the same chip. Such chips are typically also sold under the label 'UART'.
UARTs are called asynchronous, because they don't use a special clock signal to synchronize with the the remote side. Instead, they use the start/stop bits to identify the data bits in the serial stream.
Thanks to the UART the rest of the hardware, as well as the software application can deal with normal bytes to hold the communication data. It is the job of the UART to chop a byte into a series of serial bits when sending, and to assemble series of bits into a byte when receiving. UARTs typically contain eight bit wide receiver and transmission buffers. Of which not all bits might be used if e.g. a 7 bit transmission is used. Received serial data is provided in parallel in the receiver buffer, to-be-send data is written in parallel to the transmission buffer. Depending on the UART the buffers might just have a depth of one byte, or a few bytes (in the range of 15 or 16 bytes). The less deep the buffers are, the more precise the communication with the CPU needs to be. E.g. if the receiver buffer just has a depth of one byte, and the data is not fetched fast enough, the next received data can overwrite the previously received data in the buffer, and the previously received data is lost.
Because of the fact that the timing on the serial interface is important, UARTs are typically connected to a baud rate generator, either an internal one in the UART chip, or an external one.
A USART (universal synchronous/asynchronous receiver transmitter) is a version of an UART that can also communicate synchronously. Many modern ICs have this feature. However, as RS232 works asynchronously, the synchronous features of a USART aren't used for serial communication. Depending on the particular type of USART the synchronous features are either turned off at start or need to be turned off. Once they are off a USART is a UART when it comes to RS232 communication.
Baud Rate Generator
Principle of Operation
The baud rate generator is an oscillator. It provides a frequency signal which is used to control the timing on the serial interface. Since different line speeds need a different timing, the baud rate generation needs to be flexible.
There are two general ways to achieve a flexible baud rate generation. Either the baud rate generator itself is programmable and can produce the necessary different frequencies, or the UART has a programmable divider or multiplier, which converts the frequency from the baud rate generator into the required frequencies. Observers might have noticed that there are fixed relation between typical baud rates (300bps, 600bps, 1200bps, 2400bps, 9600bps (4 x 2400bps), etc.). This simplifies the usage of frequency dividers or multipliers to generate the necessary timing.
Depending on the actual UART, the baud rate generator either needs to be some external component, or it is directly integrated into the UART chip. From the outside, the programmatic change of the baud rate generation is the means to control the speed of the serial connection. Often when programming the baud rate one doesn't provide the desired baud rate in 'clear text', but needs to provide some divider or factor. Providing the right divider or factor requires to know the basic frequency of the used baud rate generator.
Oscillator & Magic Quartz Crystal Values
RS-232 communication is asynchronous. So there is no explicit means, no shared clock signal, how a transmitter and receiver synchronize. Instead they synchronize on the start bit and further assume a certain bit length. The bit length is a direct function of the baud rate. E.g. 9600 bauds on RS-232 means 9600 bits are transmitted in one second. Each bit therefore has a length of 1/9600 seconds. To ensure the transmitter and receiver assume the same bit length, they need to work with the same baud rate. And the baud rates on both sides of the communication need to be precise up to a certain level. Otherwise the data transmission fails. Typical errors when the baud rates on the two sides aren't well enough aligned are framing errors, wrongly received or missed bits.
The baud rate generator, as it generates the clock frequency for sending, as well as receiving data, is responsible for providing the required baud rate precision. As a rule of thumb, typically, the total difference in the baud rate should not exceed +/- 4% in the worst case. If one distributes this "error budget" equally to both sides it means the baud rate on each side needs to be kept within +/- 2% of the nominal value. In fact, one should avoid this worst case and keep the baud rate at least within +/- 1% on each side.
It is common to use a crystal oscillator to build a baud rate generator with an initial precision, temperature drift and long term drift suitable for RS-232. That is, the baud rate generator is based on an oscillator that in turn uses a quartz crystal as a reference. Simpler oscillators, like RC oscillators are usually not good enough.
Typically all the crystal oscillator electronics is build into the baud rate generator IC. If a UART with an integrated baud rate generator is used, that UART typically contains the electronics. The only thing that typically needs to be added is the quartz crystal.
To achieve the common baud rates, the frequency value of the quartz crystal needs to have one of certain "magic" values. These values at first glance seem rather odd. However, they are typically multiplies of 300 (often the lowest baud rate to support) or 25 (for very low baud rates of 25, 50, 75, 150) and a power of two. Powers of two are used, because it is easy to divide a frequency by a power of two, thus making a configurable or programmable baud rate generator possible.
For example, dividing the rather odd crystal frequency 4.915200 MHz by 256 (a power of two), gives the nice value 19200 Hz. Crystals with frequencies like 4.915200 MHz are called baud rate crystals or magic crystals. The following table lists common baud rate crystal frequencies.
- 1.8432 MHz, 3.6864 MHz, 4.9152 MHz, 5.5296 MHz, 6.1440 MHz, 7.3728 MHz, 9.8304 MHz
- 11.0592 MHz, 12.2880 MHz, 12.9024 MHz, 14.7456 MHz, 16.5888 MHz, 18.4320 MHz, 19.6608 MHz
- 20.2752 MHz, 22.1184 MHz, 23.9616 MHz, 25.8048 MHz, 27.6480 MHz , 29.4912 MHz
- 31.3344 MHz, 33.1776 MHz, 35.0208 MHz, 36.8640 MHz
Which crystal frequency to use depends on the baud rate generator / UART in use. The datasheet of the baud rate generator / UART should be consulted for details.
The UART and the baud rate generator are just chips or parts of a larger chip. For simplifying the discussion we assume they are one chip. This chip typically has a bunch of pins which somehow need to be written by or read by the CPU. So there needs to be some way for the CPU to address the chip to talk to it. There also needs to be some way for the chip to make itself heard in certain situations.
It is the job of the interface logic to provide this connection. The logic vastly differs from system architecture to system architecture. E.g. one common way is to map the UARTs transmission and receive buffer in the CPU's memory address space. That way they look like ordinary memory to the CPU. Another way is to connect these buffers to some CPU-specific I/O port. Not only the transmission and receive buffer need to be reachable by the CPU, but also some control lines and e.g. the baud rate, parity, character size programming interface of the UART.
Since a UART can't buffer an endless amount of incoming data (some can only buffer one byte in the receiver buffer), the CPU (or some other hardware) needs to read from the UART 'fast enough'. The CPU can either continuously poll the UART to check if some data is available, or the UART can notify when some data is there. This notification is typically done via a CPU interrupt. It is the job of the interface logic to trigger such an interrupt on behalf of the UART. Both mechanisms (polling or interrupt driven) have advantages and disadvantages.
In the other direction, when sending data, there is a similar problem. The CPU needs to deliver the data 'fast enough', otherwise a buffer underrun happens, and the serial line is not used efficiently. This is not fatal, but can degrade the performance of the serial communication. But not only a buffer underrun can happen. The CPU must not deliver the data 'too fast' to the UART, otherwise the transmitter buffer in the UART is overrun. This can again be controlled by polling (the CPU checks if the UART is ready to send) or via an interrupt (the UART tells the CPU it needs more data). Again, the interface logic has to provide the means to do one or the other, or both.
The interface logic determines how and where a programmer finds the UART he/she wants to program, and in which operating mode(s) (polling, interrupt driven) he/she can transmit and receive data. The interface logic in e.g. modern PCs is programmable to some extend. However, this is typically done by the BIOS (setting I/O addresses and interrupt numbers). In older PCs and many other computers the interface logic is not programmable. From an application programmer's view there is very seldom the need to program the interface logic. Typically it is treated as hard-wired logic.
In order to program a particular hardware one needs to know:
- How to reach the UART and baud rate generator (how the interface logic works). Typically this means to know some I/O or memory addresses.
- How the communication with the UART and baud rate generator works (does the interface logic provide polling or interrupts, or both?).
- How to program the UART. Having the UARTs data sheet available is very helpful.
- How to program the baud rate generator, which just might be part of the UART.
Alternatively, one uses the serial programming API of an operating system, which is supposed to know all this, and 'do the right thing'.
- The page Programming the 8250 UART provides details for programming a particular UART in a particular hardware combination.