Embedded electronic devices are collections of various circuits (processors or other integrated circuits). In order to exchange information between these independent circuits, they must follow the same communication protocol. To accommodate different needs, engineers have defined hundreds of thousands of communication protocols, which can generally be divided into two categories: parallel or serial.
1. Parallel vs Serial
Parallel interfaces transmit multiple bits of data simultaneously. They typically require a data bus (eight, sixteen, or more data lines) for transmission.
An 8-bit data bus, controlled by a clock, transmits one byte with each clock pulse. Using 9 wires.
Serial interfaces can only transmit one bit of data at a time. These interfaces only require one data line to operate, usually not exceeding four.
Example of a serial interface, transmitting one bit with each clock pulse. Only 2 wires required!
A parallel interface is like an 8+ lane highway, while a serial interface is more like a two-lane country road. Over time, the massive highway can get more people to their destination, but the rural two-lane can also get people there and costs a fraction of the construction funds.
Parallel communication certainly has its benefits, being fast, simple, and relatively easy to implement. However, it requires more input/output (I/O) lines. If you have ever had to migrate a project from a basic Arduino Uno to a Mega, you know how precious I/O lines are on a microprocessor. Therefore, we often choose serial communication to utilize the limited I/O lines at the expense of potential transmission speed.
2. Asynchronous vs Synchronous
Over the years, dozens of serial protocols have been designed to meet the specific needs of embedded systems. USB (Universal Serial Bus) and Ethernet are two very well-known serial interfaces. Other very common serial interfaces include SPI, I2C, and the serial standard we will discuss today. These serial interfaces can be divided into two categories: synchronous or asynchronous.
Synchronous serial interfaces always have a paired clock signal, and all devices on the synchronous serial bus share this common clock. This makes serial transmission more straightforward and faster, but this type of communication interface requires at least one additional line between the communicating devices. Synchronous serial interfaces include SPI, I2C, etc.
Asynchronous means that data transmission does not require support from an external clock signal. This method of transmission is very suitable for minimizing the required wires and I/O pins, but it means we need to put in some extra effort to reliably transmit and receive data. The serial protocol we will discuss in this article is the most common form of asynchronous transmission. In fact, this is so common that when most people say “serial,” they are referring to asynchronous serial.
The asynchronous serial protocols we will discuss in this article are widely used in embedded electronic products. If you want to add a GPS module, Bluetooth, XBee, serial LCD, or many other external devices to your project, you will use it.
3. Serial Rules
To ensure robust and error-free data transmission, asynchronous serial protocols have many built-in rules. They are: 1) Data bits, 2) Synchronization bits, 3) Parity bits, 4) Baud rate.
Through variations of these signaling mechanisms, you will find more than one way to serially send data. The protocol is highly configurable. The key part is to ensure that both devices on the serial bus are configured to use the exact same protocol.
3.1 Baud Rate
Baud rate specifies the speed at which data is sent over the serial line. The unit is: bits per second (bps). If you invert the baud rate, you can determine how long it takes to transmit one bit. This value determines how long the transmitter holds the serial line at high/low levels or the period at which the receiving device samples its line.
Baud rates can be any reasonable value. The only requirement is that both devices operate at the same rate. For data transmissions where speed is not critical, a common baud rate is 9600 bps. Other “standard” baud rates are 1200, 2400, 4800, 19200, 38400, 57600, and 115200.
The higher the baud rate, the faster the speed of sending/receiving data, but the speed of data transmission is limited. You typically won’t see speeds exceeding 115200 – this is fast for most microcontrollers. If it is too high, you will see errors at the receiving end because the clock and sampling period cannot keep up.
3.2 Building Data Frames
Each data block (usually a byte) is actually sent within a data packet or frame. A frame is created by attaching synchronization and parity bits to our data. Here are the details of each frame.
Serial frame. Some symbols in the frame have configurable bit sizes.
3.3 Data Blocks
The true core of each serial data packet is the data it carries. We vaguely refer to this data block as a chunk, as its size is not specified. The amount of data in each packet can be set to any value between 5 to 9 bits. Of course, the standard data size is the basic 8-bit byte, but other sizes also have their uses. A 7-bit data block is more efficient than an 8-bit data block, especially when you are only transmitting 7-bit ASCII characters.
Once an agreement on character length is reached, the two serial devices must also agree on the byte order of their data. Is the sent data transmitted from the most significant bit (msb) to the least significant bit, or vice versa? Unless otherwise specified, the default data is transmitted with the least significant bit (lsb) first.
3.4 Synchronization Bits
Synchronization bits are two or three special bits transmitted with each data block. They are the start bit and stop bit. As the name suggests, these bits mark the beginning and end of the data packet. There is always only one start bit, but the number of stop bits can be configured to 1 or 2 (though it is usually kept to 1).
The start bit is always indicated by an idle data line transitioning from 1 to 0, while the stop bit transitions back to idle by keeping that line at 1.
3.5 Parity Bits
Parity is a very simple form of low-level error checking. It has two forms: odd or even. To generate a parity bit, you sum all the bits of the data byte (5-9 bits), and the parity of the sum determines whether the bit is set. For example, suppose the parity is set to even, and it is added to the data byte 0b01011101 (which has an odd number of 1’s (5)), then the parity bit will be set to 1. Conversely, if the parity mode is set to odd, the parity bit will be set to 0.
Parity is optional and not widely used. It is helpful for transmission over noisy media, but it also slightly slows down data transmission speed and requires both sender and receiver to implement error handling (usually, erroneous data received must be resent).
3.6 9600 8N1 (Example)
9600 8N1: 9600 baud rate, 8 data bits, no parity, and 1 stop bit – is one of the commonly used serial protocols. So, what would one or two 9600 8N1 data packets look like? Let’s take an example!
The devices transmitting the ASCII characters “O” and “K” must create two data packets.O (uppercase) has an ASCII value of 79, which is binary value 01001111, and K (uppercase) has a binary value of 01001011. The rest is adding synchronization bits.
By default, the least significant bit is transmitted first. Note how each byte in the two bytes is sent when read from right to left.
Since we are transmitting at a rate of 9600 bps, the time spent holding each bit at high or low is 1/(9600 bps) or 104 µs per bit.
For each byte of data transmitted, there are actually 10 bits: one start bit, 8 data bits, and one stop bit. Therefore, at 9600 bps, we are actually sending 9600 bits or 960 (9600/10) bytes per second.
Now that you know how to construct serial data packets, let’s continue discussing the hardware part. There we will see how these 1’s and 0’s and baud rates are implemented at the signal level!
4. Wiring and Hardware
4.1 Wiring
A serial bus consists of only two wires: one for sending data and the other for receiving data. Therefore, serial devices should have two serial pins: receive (RX) and transmit (TX).
It is worth noting that these RX and TX labels are for the devices themselves. Therefore, the RX of one device should connect to the TX of another device, and vice versa. If you are used to connecting VCC to VCC, GND to GND, MOSI to MOSI, etc., this might seem strange, but if you think about it, it makes sense. The transmitter should be talking to the receiver, not to another transmitter.
Serial interfaces that allow both devices to send and receive data are called full-duplex or half-duplex. Full-duplex means both devices can send and receive simultaneously. Half-duplex communication means the serial devices must take turns sending and receiving.
Some serial buses may only require one connection between the sending and receiving devices. For example, our serial-enabled LCD does not require any data to be sent back to the controlling device. This is known as simplex serial communication. All you need is one wire from the TX of the master device to the RX of the listener.
4.2 Hardware Implementation
We have conceptually introduced asynchronous serial. We know what wires we need. But how is serial communication actually implemented at the signal level? In reality, there are multiple ways. Serial signals have various standards. Let’s look at a few popular serial hardware implementations: logic level (TTL) and RS-232.
When microcontrollers and other ICs communicate serially, they usually do so at TTL levels. TTL serial signals are within the power supply voltage range of microcontrollers – typically 0V to 3.3V or 5V. VCC levels (3.3V, 5V, etc.) indicate idle lines, bits with a value of 1, or stop bits. 0V (GND) signals indicate start bits or bits of data with a value of 0.
RS-232 can be found on some older computers and peripherals, similar to inverted TTL serial. RS-232 signals typically range between -13V and 13V, although the specification allows any value from +/- 3V to +/- 25V. On these signals, low voltage (-5V, -13V, etc.) indicates idle lines, stop bits, or bits of data with a value of 1. High RS-232 signals indicate start bits or bits of data with a value of 0. This is the opposite of TTL serial signals.
When connecting two serial devices, it is very important to ensure that their signal voltages match. You cannot directly connect a TTL serial device to an RS-232 bus.
5. UART
The last and most challenging part of serial communication is converting data on a parallel bus to data on a serial interface. This is where UART comes in.
5.1 Hardware UART
Universal Asynchronous Receiver/Transmitter (UART) is the circuit block responsible for implementing serial communication. Essentially, the UART acts as an intermediary between parallel and serial interfaces. One end of the UART is a bus consisting of about 8 data lines (plus some control pins), and the other end consists of two serial lines RX and TX.
Super simplified UART interface. One end parallel, the other end serial.
UART can exist as a standalone IC, but they are more commonly found within microcontrollers. You can check the datasheet of a microcontroller to see if it has a UART. Some do, some have one, and some have many. For example, the Arduino Uno based on ATmega328 has only one UART, while the Arduino Mega based on ATmega2560 has up to four UARTs.
As indicated by the R and T in the acronym, the UART is responsible for sending and receiving serial data. At the sending end, the UART must create a data packet, attach synchronization and parity bits, and send that packet to the TX line with precise timing (according to the set baud rate). At the receiving end, the UART must sample the RX line based on the predetermined baud rate, pick out the synchronization bits, and output the data.
UART internal block diagram (provided by Exar ST16C550 datasheet)
More advanced UARTs may place received data into a buffer, where the data can be held until the microcontroller retrieves it. UARTs typically release their buffered data in a first-in-first-out (FIFO) manner. The buffer can be as small as a few bits or as large as several thousand bytes.
5.2 Software UART
If the microcontroller does not have a UART (or not enough), the serial interface can be bit-banged directly controlled by the processor. This is the approach taken by Arduino libraries like SoftwareSerial. Software UARTs are usually not as precise as hardware UARTs, but they can be useful in a pinch!
6. Common Pitfalls
That’s all there is to serial communication. I want to remind you of some common mistakes engineers make:
6.1 RX to TX, TX to RX
It seems simple, but I know this is a mistake I have made many times. Always ensure that the RX and TX lines between serial devices are crossed.
FTDI Basic programming the Pro Mini. Note that RX and TX are crossed!
6.2 Baud Rate Mismatch
Baud rate is like the language of serial communication. If two devices do not speak at the same speed, data may be misunderstood or completely lost. If the receiving device sees all garbage on its receiving line, check to ensure the baud rate matches.
Data is sent at 9600 bps but received at 19200 bps. Baud rate mismatch = garbage.
6.3 Bus Contention
Serial communication is designed to allow only two devices to communicate over one serial bus. If multiple devices try to transmit data over the same serial line, bus contention may occur.
For example, if you want to connect a GPS module to an Arduino, you only need to connect the TX line of that module to the RX line of the Arduino. However, the Arduino RX pin is already connected to the TX pin of the USB to serial converter, which will be used when programming the Arduino or using the Serial Monitor. This creates a potential situation where the GPS module and the FTDI chip are trying to transmit over the same line simultaneously.
Two transmitters sending data to a single receiver can lead to bus contention.
It is very bad for two devices to try to transmit data over one line at the same time! In the best case, neither device will send data. In the worst case, both devices’ transmission lines may fail (although this is rare and usually protected).
Connecting multiple receiving devices to a single transmitting device is safe. Although it is not very compliant with the specification, it does work. For example, if you want to connect a serial LCD to an Arduino, the simplest method might be to connect the RX line of the LCD module to the TX line of the Arduino.
From a firmware perspective, linking TX lines like this is still dangerous because you cannot choose which device receives the transmitted signal. The LCD may receive signals that are not suitable for it, causing it to enter an unknown state. In general: one serial bus, two serial devices.
If I could live my life again, I think I would still choose a life that burns at hundreds of millions of degrees.
–Takeshi Kitano