Serial Interfaces

Serial Interfaces

Serial interfaces transmit bits one after the other on a single conductor. They have become quite important in data transmission, as in the SCSI (small computer serial interface), USB (universal serial bus), FireWire and other examples. There are two principal types, synchronous and the somewhat misnamed asynchronous links. In a synchronous link, timing information to separate the bits is transmitted either on a separate conductor, or in the data stream itself. The fast serial links are always synchronous. The idea of the asynchronous link goes back to Baudot's printing telegraph, which evolved into the Teletype. In this kind of link, the data is sent in words or groups, perhaps corresponding to individual characters. The beginning of each group starts a timing device that is accurate enough to separate the individual bits. It would not be possible to stay synchronized for an arbitrarily long string of bits, but this is easy for a small number of bits. The transmitter and receiver must agree on the speed to be used, but timing information need not be sent with the data. This permitted communication over a single telegraph line. Baudot's code had five bits, but normally eight data bits are now sent in each group, preceded by a start bit that triggers the receiver timing, an optional parity bit for error checking, and followed by a stop bit that allows time for the receiver to reset for the next group.

New serial interfaces that are very useful for connecting sensors to microcontrollers and other similar applications have been introduced. These include the Serial Peripheral Interface (SPI) or 4-wire interface, the Inter-Integrated Circuit (I2C) or 2-wire interface, and the 1-Wire interface. These are treated in later pages, after PIC and AVR microcontrollers.

The parity bit checks for the incorrect reception of any one bit in the data plus parity string. If even parity is agreed, then if the data bits have an even number of 1 bits, the parity bit is 0. If there is an odd number of 1 data bits, then the parity bit is 1, so that the total number of 1 bits in data plus parity is even. Odd parity works the same way, but the total number of 1 bits is made odd instead of even. The receiver checks the parity of the received group, and if it is wrong raises a flag, the PE (parity error) bit. The parity will be wrong if a 1 bit is received as 0, or a 0 bit is received as 1, in data plus parity. Two errors, however, will not be detected by a parity check. This elementary check is not very useful for the usual low-speed serial link, because errors are very rare, and if there are errors, the link does not work at all. Faster serial links depend on elaborate checking and error correction mechanisms, causing retransmission of any doubtful data. These checks are complex and system-specific.

A framing error occurs when the stop bit is not found in the expected place, implying that reception began on a false start bit (spacing condition of the line, possibly caused by noise or interference), or that the transmitter is out of order. An overrun error happens when received data is overwritten by subsequent data before it is read or otherwise disposed of. Parity, framing and overrun errors are usually automatically reported by the receiver circuits.

The bit rate is the number of bits transmitted per second, and the reciprocal of this is the bit time, or the length of a single bit. Each group consists of a stop bit, a certain number of data bits (usually 5,7 or 8), a possible parity check bit, and 1, 1-1/2 or 2 stop bits. Therefore, groups can be from 7 to 12 bit times long. We will assume a format of one start bit, eight data bits, and one stop bit, 10 bits in all. At 1200 bits per second, then, it requires 8.333 ms to send a complete group, or 120 characters per second. Allowing five characters per word, this is 1440 words per minute. This assumes on-off control of the line. More complicated modulation systems can send much more rapidly, as is done with modern modems. 1200 bits per second is sometimes called 1200 baud, but the baud unit has a somewhat esoteric definition that can lead to confusion with other than on-off control.

American telegraph lines were normally closed-circuit, with current flowing continuously. The current attracted the armature (through a relay) of the Morse register to emboss a mark on the paper tape, so this state was called mark. When the current was interrupted, the armature was released, and a space appeared on the moving tape. This state was called space, not surprisingly. These words are still used today for the two states of the line. An extended space was called a break, and was used to signal another operator. If an operator was sending, he heard himself on his sounder. If there was a break, the sound stopped and he knew something was up. This term is also still used.

RS-232 Levels

It is possible to use the ordinary TTL levels for short-distance communication. In fact, the parallel port does just this, and is reliable up to about 20 feet or so. The TTL levels are a good ground for low, or 0, and a wire pulled up to above 2.4 V for high, or 1. Such a circuit is susceptible to noise, interference from electrical discharges, such as the opening of a circuit, and crosstalk (interference with neighboring circuits). For longer distances, other arrangements must be made. A very popular serial interface for computers is the EIA Standard RS-232C. This is an excellent interface for moderate distances and low signalling speeds. The circuits have a common ground, and a line for which the mark state is from -3 to -15 V, while the space state is +3 to +15 V. To remember which is which, think Mark is Minus. If mark is given a logic level of 1, and space 0, then the line voltages are inverted with respect to the logic voltages.

It is clear that we need a bipolar supply and special transmitter and receiver circuits for RS-232 lines. We'll discuss later what the various lines are in a complete interface, and consider them generally. All are electrically the same. The classic interface chips are the 1488 transmitter, and the 1489 receiver. The first thing you should do is wire up a test circuit, as shown at the left, and measure the logic and line voltages at each end of the link. If you use ±12 V supplies, the line will be at about -10 V marking, and +10 V spacing. The 1488 will make these levels, and the 1489 will accept them. The 1488 has an output resistance of about 300Ω, and the 1489 an input resistance of 7kΩ, which is specified by the standard. Of course, RS-232 lines should not be connected to the inputs of TTL, because they will fry the chips. Be careful with the wiring, because it is easy to ruin a 1488 by mistaken connections. Note that three of the 1488's buffers are NAND gates. The middle connections on the 1489's buffers are for adjusting the transition level. They can be connected to +5 or -5 through a 11k resistor to move the level up or down, but generally they will be left not connected. The maximum input voltage for the 1489 is ±30 V, while the maximum supply voltages for the 1488 are ±15 V.

An alternative to the 1488 and 1489 is the Maxim MAX232 chip, which not only transmits on two lines and receives on two, but also makes its own ±V supplies. One MAX232 is enough for a basic RS-232 interface, saving a lot of bother. Its circuit is shown at the right. Wire it up and test the voltage levels. Its output is close to ±10 V as well. Take special care to get the polarities of the electrolytic capacitors correct. Use 25 V rating capacitors.

Note that the TTL levels are mark = 1 or high, and space = 0 or low, and should not be confused with the RS-232 signals of opposite polarity.

RS-232 Circuits and COM Ports

The RS-232 standard was created for communication between a computer (a data terminal, and a modem (a data set) connected to a telephone line. The names of the circuits reflect this intent, but are just names, and have no other significance. The data circuits are TD (transmit data) from the computer to the modem, and RD (receive data) from the modem to the computer. They are connected to pins 2 and 3, respectively, of the DB-25P connector. It is now customary to use a DB-9P instead, and these are pins 3 and 4. Pin 7 of the DB-25, and pin 1 of the DB-9 are ground. Note that this is a full-duplex link, and data can be going both ways at once. A control, or handshaking, pair of circuits is formed by RTS (request to send) from the computer, and CTS (clear to send) from the modem, on pins 4 and 5 of the DB-25 or pins 8 and 7 of the DB-9. The computer controls another output, DTR (data terminal ready) on pin 20 of the DB-25 or pin 2 of the DB-9. This corresponds to the DSR (data set ready) circuit from the modem to the computer, on pin 6 of the DB-25, pin 9 of the DB-9. This makes two complete handshaking circuits, usually one more than is required. The modem can send additional information to the computer on the DCD (data carrier detect) and RI (ring indicator) at pins 8 and 22 of the DB-25 and pins 5 and 6 of the DB-9. These have particular uses when a modem is involved, but in general are just two more input bits. In all, we have 4 input bits, and 2 output bits, surrounding the 2-wire data link.

The communications ports of a computer present RS-232 levels at the connector. I found outputs of ±6.5 V (approximately) for the control lines, and ±8.0 V for the data lines. The best way to look at the status of the circuits is with an RS-232 tester having red-green LED's for each of the circuits (RI is usually not included). My tester shows red for a negative line voltage (mark) and green for a positive line voltage (space). To give the inputs some value, use a loopback adapter. This is a DB-25S or DB-9S connector with pins 2 and 3 connected, pins 4 and 5 connected, and pins 20, 8 and 6 connected, simulating a terminal at the other end. The tester and loopback adapter are good tools to have around when working with RS-232 links.

The operating system knows the communications ports by the names COM1, COM2, COM3, and so forth. The original PC had just two, the PS-2's had four, and most recent computers can handle even more. A block of 8 I/O addresses is reserved for each port. COM1 uses 3F8-3FF, COM2 2F8-2FF, COM3 3E8-3EF and COM4 2E8-2EF. These addresses are stored in RAM in the table at 0000:0400 (see DEBUG to find out how to look at the table). By looking here, you can find out what ports are available. Often, one is used by a modem, and another by the mouse. By reading and writing bytes to these addresses, the port can be controlled.

Let's assume the base address is 2F8 in what follows, and that we are using DEBUG to investigate things. Data can be written or read to the base address using DEBUG. If you write a byte to 2F8 (-o2f8 4a), it will show up when you read from 2F8 (-i2f8 4a), provided you have used a loopback adapter. The DTR and RTS outputs are controlled by bits 0 and 1 of a byte written to 2fc (-o2fc 3 turns them on, and -o2fc 0 turns them off). The CTS, DSR, RI and DCD inputs are read at address 2fe. They are the upper four bits, in that order. The lower four bits show when there have been changes in their values (in the same order). Now you should be able to control any of the two output bits, read any of the four input bits, and transmit or receive bytes of data.

It is necessary to initialize a COM port before using it. This sets the group format as well as the bit rate. What we have been using is the default initialization carried out automatically by BIOS when the power came on. The group format is set by writing a byte to 2fb. The various possibilities can be found in the manuals. For our 8 data, 1 stop, no parity group, the byte is 03h. If we want to make the port go to a break state, the byte is 43h. Try this with DEBUG. Setting the bit rate is more complicated, so I will not explain how to do it here. What we have to do is access the divisor register and write the proper 16-bit divisor into it.

Instead, we can use the BIOS interrupt 14h, function 0, to initialize the port. We put 00 in AH, and the port number in DX (0-COM1, 1-COM2, etc.). In AL we put the initialization byte, which is built as follows: bits 0 and 1 select the data length. 11 gives 8 data bits. Bit 2 selects the number of stop bits. 0 gives one stop bit. Bits 3 and 4 set the parity. 00 gives no parity. Bits 5,6 and 7 set the bit rate. 1200 b/s is 100 (011 is 600 b/s, 101 is 2400 b/s). Rates from 110 b/s to 9600 b/s can be set. For our standard group, the byte is 10000011 = 83h, which we put in AL. If these values are loaded into the registers in DEBUG, the only program we need is: int 14, jmp 0. In Borland C++, the library function int bioscom(int cmd, char abyte, int port) does exactly the same thing. Unfortunately, this function does not allow control of DSR and RTS, but this can be done by writing directly to 2fc, as we did above in DEBUG.

The Universal Asynchronous Receiver and Transmitter (UART)

Now let's consider making a data interface. We require some way to create the group and send it. One way is to shift out the required bits at the agreed rate, say 1200 b/s. We put a start bit (0) on the front, then the data bits from lsb to msb, and finally let the line rest in the marking state. Since we are sending bytes at well-spaced intervals, there is no need for an explicit stop bit. This is not very difficult to do, but there is a chip that will do it all for us. This is the UART, of which we use the 40-pin DIP AY3-1015D, which is commonly available and not very expensive. This chip can create the desired serial data stream, but not the RS-232 levels. The UART can stand alone, without a computer to control it, so it is necessary here. With computer control, a chip in a smaller package called an ACIA (asynchronous communications interface adapter) could be used instead. In an ACIA, the configuration information is stored internally, and does not have to be hard-wired. We will use only the part of the UART that transmits data, leaving the receiving side unused.

A clock signal is required for the UART. The frequency of this clock is 16 times the desired bit rate, so for 1200 b/s we require 19.2 kHz. A crystal-controlled oscillator is required for this duty. There are chips especially designed for this service, called baud rate generators, but we do not need to use one here since we will pick a fixed frequency. Instead, we will start with a 5.0688 MHz oscillator and divide it by 28 = 256 to get 19.2 kHz. This can be done with a 74HC4060 counter, and the circuit is shown at the right. Bit rates differing by factors of 2 can be obtained by using different output pins on the 74HC4060.

The UART test circuit is shown at the left. For the data strobe DS, use the TTL output of a function generator at about 50 Hz. When DS goes low, the UART is prepared to transmit and the data must remain stable. When DS goes high again, the UART begins shifting out the data at the SO (serial out) output. Look at DS and SO on two channels of your oscilloscope, set for 1 ms/division. Every time DS goes high, you should see the serial data shifted out. Identify the start and stop bits, and measure the length of the group. I used hex thumbswitches to change the data bits conveniently.

Let's suppose we want an A/D converter interface. We need one output and one input bit to control the A/D converter (start and end of conversion), and one output bit to control the UART (DS). Of course, SO uses the RD circuit. This can be done easily, so we have yet another way to input data to the computer. A MAX232 can handle the RS-232 level conversion. Output works the same way, with data from the computer entering via SI (serial in) from the TD circuit.

The pins in the upper right-hand part of the package select the group format. EPS selects even parity when 1, odd when 0, but NP sets no parity check when 1, parity when 0. B1 and B2 set the number of data bits, 5 to 8 when 00 to 11. SB sets 1 stop bit when 0, 2 when 1. CS is a strobe for entering the control bits into an internal register, and can be hard-wired to 1. XR is an external reset, 0 to allow normal operation. TBE shows when the transmit buffer is empty, and EOC when the end of a group is reached. On the read side, RC is the read clock, which can be different from TC, and serial data enters by SI. RS low enables the data output pins, which are otherwise hi-Z. PE, FE and OR signal a parity error, framing error (false start) or overrun (second byte came in before RDA for preceding byte). DA shows when incoming data is available, and RDA resets this signal. When SWE (status word enable) is 0, the status (PE, FE, OR and TBE) are enabled. Otherwise, these pins are hi-Z. RS and SWE can be hard-wired to 0 if desired.

If you wish, the receiving side of the UART can be tested simply by connecting RS and SWE to ground, putting the clock signal on RC as well, and connecting SO to SI. Use a 4.7k resistor to pull up RDA. Connect the normally-high output of a debounced pushbutton to DS. When the button is pressed, the data will be shifted out of the transmitting side into the receiving side. DA will go high, and can be reset by a low level on RDA. If you press the button twice in a row, the OR output will show the overrun.


Many PIC microcontrollers include an Enhanced Universal Synchronous Asynchronous Receiver and Transmitter port that can support asynchronous serial communication as described above, as well as a synchronous interface similar to the SPI. Let us use this port to communicate with a SparkFun COM-09764 serial 4-digit 7-segment display. Many sensors and displays use this interface. Using this display with the SPI is explained in the page on making a digital compass, and the PIC MCU is explained in another page, which should be consulted. In particular, study the EUSART in the data sheet for the PIC16F690.

The display uses a baud rate of 9600 bps by default. If the internal RC oscillator of the MCU is used, configured to give 4 MHz, then the 9600 bps is selected by loading decimal 25 in the SPBRG register, with BRGH in the TXSTA register set and BRG16 cleared in the BAUDCTL register. Also in the TXSTA register, TXEN should be set to enable the transmitter, and SYNC cleared to select asynchronous transmission. In the RCSTA register, SPEN and CREN should be set. The SPBRGH register can be left cleared, since we do not use it. Summarizing, I loaded 0xA7 in TXSTA, 0xB0 in RCSTA, 0x40 in BAUDCTL, and 0x19 in SPBRG.

Data is transmitted on the RB7/TX/CK pin, pin 10, and received on RB5/AN11/RX/DT, pin 12. Note that this is an analog input by default, so we should clear the associated bit in ANSELH (or just clear this register if we are not using the other analog inputs). Only one wire is required from pin 10 to the RX pin of the display. TRISB can be set to make RB7 an output and RB5 an input. The data sheet implies that this is done by enabling the port, but it is neater to arrange it explicitly.

All that needs to be done is to poll the interrupt flag TXIF in PIR1. When this flag is set, it is all right to send a byte to TXREG. It is not necessary to enable interrupts. On receiving a byte, RCIF in PIR1 plays the same role: when it is set, a byte can be read from RCREG. If the overrun error bit OERR is set, it is necessary to clear CREN to clear it. CREN is then set again to continue. A framing error, flagged by FERR, is cleared by reading RCREG. Routines are shown at the right.

Return to Electronics Index

Composed by J. B. Calvert
Created 5 August 2002
Last revised 20 August 2010