Sign in

Learn Asynchronous Serial

Follow

Topology - where do the wires go?

Async Serial data is sent with just one wire (not including ground). In most cases there is one device that is the sender, and one or more devices which listen.

If communication needs to be two-way, then generally you need to add another wire for the other direction. These wires are often referred to TX (outgoing transmission) and RX (receiving line) to convey their direction with respect to a particular device.

Voltages

Serial uses two voltage levels to convey information - 1s and 0s. The exact voltages that represent 1 and 0 can be different, depending on the setup, but both sides of the communication need use the same voltages. In "logic level" serial, the voltages are 0V for a digital 0, and the logic-high voltage for digital 1. (The logic high voltage would be something like 5V, 3.3V, etc, depending on the application).

When using the RS-232 electrical standard, used by older computers and equipment but still being used today (although considerably less so) Logic 0 is 15V, and Logic 1 is -15V. A bit backwards seeming, yes. A lot of equipment cheats a little and doesn't use +/-15V, opting instead for voltages that are easier to generate, such as +/-7V. This is still RS-232 compliant, and will work with RS-232 equipment.

Serial Type| Logic 0| Logic 1
---|---|---
5V | 0V | 5V
3.3V | 0V | 3.3V
RS-232 | 15V | -15V
| |

How do you transmit information with only one wire anyway?

Since you only have one wire to work with, the only way to send information is by using some sort of timing scheme that both sides agree on. We'll start by agreeing on the amount of time each bit gets on the wire (we'll start calling it a bus now) before the next bit replaces it. Bus just means wire, or group of wires.

The bits on the bus go one at a time

When you talk about bit rates, you're really describing how long each bit has on the bus, before the next bit gets put on the bus. For example, if you send 10 bits every second, each bit will get .1 seconds on the bus, and then the next bit will be placed on the bus.

Sending bits to a friend in another room, using a switch

Imagine you are putting bits on the bus, once every second. You use a stop watch. Every second you decide what the next bit to send is, and then move a switch to either on or off (1 or zero). It turns on (or off) a light in another room. Remember, the light may or may not change every bit. Maybe you need to send a lot of 1s in a row, for example.

In the very middle of each second (.5 seconds after the new bit is on the bus, and .5 seconds before the next one) your friend writes down the value. 1 if the light is on, 0 if the light is off.

Ways your friend could get confused

However there are some issues with this approach. For example, if you don't yell into the other room - how does your friend know exactly when you started sending bits?

What if their watch (or yours) isn't very accurate, and over a long time, they start writing down bits at the wrong times? They think they are writing down the bit exactly in the middle of the time it's on the bus, but this starts to drift. It can drift so far that every so often they end up skipping a bit, or adding in an extra bit. You can imagine the mayhem this will produce if these bits are supposed to be grouped into bytes, which they usually are.

More speed, more problems

The faster bits get put on the bus, the more difficult the issue becomes. Even small drifting in the senders and receivers clocks will mean losing track of what bit you are on. If each bit is only on the bus for 1 microsecond, you would have to have very accurate and expensive clocks on both ends to be sure you didn't drift more than 1 microsecond, over a long period - maybe even hours or days!

The solution part one - let's agree to periodically re-synchronize our clocks.

If we could come up with a way to re-synchronize our clocks every so often, then we could get away with cheap clocks that drift a lot. In fact, the more often we synchronize our clocks, the more error we can tolerate.

Resetting the clocks every time the bit changes from a 0 to a 1, or a 1 to a zero.

Well, you might point out - one great way to synchronize our clocks would be reset them every single time the bit on the bus changed from a 0 to a 1, or a 1 to a 0. This is a great idea! Congrats if you already thought of it.

But what if the sender is sending a LOT of zeros, or a LOT of 1s?

If the sender sends a lot of zeros, or a lot of 1s, then we wind with the same problem. We don't have a chance to re-synchronize our cheap clocks.

You might think - well that won't happen in real life, or not very often in any case - but, recall we need a solution that works in general, for every situation.

And - how do we synchronize the very first time, so we know for sure we have the very first bit?

The solution part 2: Idle periods, dedicated start bits, and dedicated stop bits.

Idle

Before data staring getting sent, we agree that we'll have something called Idle, where nothing is going on. Idle is when Logic 1 is on the bus for an extended period of time. Both sides - the transmitter and the receiver - agree that we'll both start up in this situation.

The Start bit

Both sides agree that the very first thing that will happen after Idle, is that a start bit is put on the bus. The start bit has the value of logic 0 - otherwise it would be the same as Idle, after all, and we wouldn't have known it even happened.

When the start bit starts - when the logic level changes from 1 to 0, we restart our clock.

Remember, the start bit isn't data that we're trying to send. It's ALWAYS 0. No useful data getting sent here. It's here to reset our clock, and let us know that more bits are coming up.

The first real data bit - the bit that comes after the start bit. What if it's also a zero?

How do we know when enough time has passed that we're now in the middle of our first actual data bit? Simple: We time it. This is how the serial port on the IBM PC did it, it's how a PIC, or MSP430, or Arduino does it.

8 data bits - why not more or less?

By convention, serial usually has 8 data bits after the start bit. It's enough for exactly one byte. However, any variation is possible, especially if you are making up your own serial data protocol - you can have as many or as few data bits as you like. Just remember the problem of timing drift again (what if our nifty 128-bit serial packet was all zeros - we might need to have better and more expensive clocks or, equivalently, run at slower bit rates).

The stop bit - what is it even for? It looks indistinguishable from the idle period that follows it.

The stop bit (or bits) come after the very last data bit is finished. They are logic 1 - the same as idle. In fact, there may be quite a bit of idle time after the stop bit.

Here's the secret. It's not really a bit. It's just a "minimum, guaranteed amount of idle time"

Why do we need a "minimum, guaranteed amount of idle time" - aka stop bits?

Well, we need to re- synchronize. We need to make absolutely sure that the receiver can reliably detect that next start bit - even if its clock isn't very accurate. The less accurate our clock, the more idle time the better. In practice, a couple if bit times should be enough.

What's the deal with 1.5 stop bits, or 2 stop bits, etc?

As you've probably guessed by now, the more stop bits we put in, the more idle time we are guaranteeing there to be. And the less we need to rely on the receiver's (or the transmitter's!) clock being really good.

What happens after the stop bits?

What happens after the previous serial "frame" as completely finished, stop bits and all? Well, one of two things. Either some more idle time, or the next start bit, for the next set of data bits. That's why the stop bit(s) are really a "minimum, guaranteed amount of idle time."

What's a framing error?

Let's say you're listening to serial bit sequence - start bit, data bits, stop bit(s) - which is known as a frame. And you're listening to those stop bits - which as you might imagine really had better be logic 1s, the idle logic level.

Instead of logic 1s, however, you discover that they are zeros, or in any event they aren't solid 1s. This is known as a framing error. It means that something has gone wrong, and we're not synchronized properly! This could happen if we were assuming the data was a different bit rate than it really was, or if we started listening to serial data right in the middle of a transmission.

A framing error could be useful

A microcontroller could use a framing error to try and synchronize itself with a serial transmission that it started listening to right in the middle. Ultimately, the only really 100% reliable method is to wait for a long idle period - at least one full serial frame's worth. This might happen however, unless the transmitter makes sure it does.

If the sender was sending a checksum, CRC, or some other known sequence, it would be possible to synchronize with a transmission that was already "under way". Such algorithms are out of the scope of this article, but you probably already have some ideas on how it could be done.

What order do data bits get sent in - most significant bit first, or least bit significant first?

Most significant and least significant refer the location of a bit in a byte. In the byte 128 (0x80 , 0b 1000 0000 ) the most significant bit is a 1, and all the other bits are 0s. In the byte 1 (0x01, 0b 0000 0001) the least significant bit is a 1, and all the other bits are 0s.

The convention for async serial data is that that the least significant bit goes first, and the rest of the bits follow in order. However it can easily be the other way around, so be sure to check. Normal RS-232 style serial is always least significant first.

What's this about a parity bit?

The parity bit was an extra (9th, typically) bit that was added to a serial frame to act as a crude error detection mechanism.

If a system was using "odd" parity, that meant that if you took all the data bits, and the parity bit, and added up all the 1s - it would give you an odd number. For example if the byte was 0x01, then the number of ones is already odd, so the transmitter would make the parity bit a zero. However if the number of 1s was even, such as if there were 4 1s, the parity bit would be set to a 1 such that the total would be become odd.

The opposite would be true for a system using "even" parity.

The idea was that if one of the bit was received incorrectly, there would be a "parity error" - the number of 1s wouldn't be the agreed upon even or odd. And the receiver would know then to reject that byte.

Note that this is a fairly primitive error detection mechanism by today's standards. A far better method of detecting errors is a CRC number or similar method. In addition, a method is needed to request that the failed series of bytes be re-transmitted.

Out of the box limitations with serial - and some ways to improve it

In addition to not having a good method to detect errors, there is also no way to address certain bytes to a particular receiver, (if there is more than one). There is also not a good method of reliably "dropping in" on an ongoing transmission.

These limitations can all be overcome by the addition of higher layers of abstraction. In fact, it is quite possible to run TCP/IP over a two-way serial connection.

A more simple improvement is MP Mode (also called 9-bit serial and Multiprocessor Mode). This adds an extra bit to specify if a given byte is an address, or if it is data. This allows for many listeners on a single line to be given data (perhaps commands) individually.

You may wish to invent your own, higher level protocol. For example you could create a packet structure, where the first byte was a particular "header" code, like 0x21 (to pick a random number), the next 2 bytes was an address, the next 4 bytes was some data, and the last 2 bytes was a 16-bit CRC. Or something like that. You could build up quite an impressive robust communication system using regular old serial as the transmission medium.

What exactly does Asynchronous Serial mean?

Serial means one bit at a time, typically on one wire. The other option is parallel, which has many bits at the same time, on different wires.

Asynchronous means that there's no one telling you exactly when you need to read the value of the data on your wire. You need to figure it out for yourself. The opposite case is "synchronous serial" where a separate line, usually called the clock line, tells you exactly when to read the data line. It does this by transitioning from one state to another.

Other examples of asynchronous serial protocols include 1-Wire, CAN, UNI/O, and Manchester.

Examples of synchronous serial protocols include SPI, I2C, and Async Serial/PCM.

How do I use RS-232 serial with a microcontroller?

RS-232 voltages will probably damage your microcontroller, and won't work in any event. You have to convert it. The MAX232 IC (such as http://www.sparkfun.com/products/316) (and its many variants) is a good way of doing it. There are other ways as well. There are also many adapter modules you can buy, such as:

Is Async Serial on the way out?

RS-232 is on the way out. But serial, since it is just so simple and useful, is not going anywhere in the foreseeable future. In addition to logic-level serial used with microcontrollers, there are more functional serial communication links such as RS-422.

Async Serial in a USB World

USB has almost entirely replaced RS-232 and the associated DB-9 and DB-25 connectors on the back of PCs. However because serial is so easy to use (especially compared with USB) many USB adapters have been created, both providing RS-232, as well as logic-level serial.

RS-232 voltages will probably damage your microcontroller, and won't work in any event. You have to convert it. The MAX232 IC (such as http://www.sparkfun.com/products/316) (and its many variants) is a good way of doing it. There other others as well. There are also many adapter modules you can buy, such as:

An example of receiving serial data on a microcontroller

Let's take a moment to look at how we would pull off reading serial if we didn't have a fancy serial peripheral (or the boss says we need to use a dirt-cheap $.10 microcontroller that doesn't have one).

To make things simple, we'll assume that our function is called when the bus is idle (not right in the middle of some serial data).#### An example of receiving serial data on a microcontroller

U8 GetSerialByte()

{

    U8 serial_byte = 0;  //we'll save our result here.

    U8 i;

                        while( serial_input == high );  // wait until the start bit



    wait( 1.5_bit_periods ); // wait until we're in the middle of the first bit.



                        if( serial_input == high )

        serial_byte |= 0x80;  

                        //if the first (in our case least signifincat) bit is high, 

                        //we'll need to set the most significant bit.

                        //we'll be rotating this to the right, so this bit will 

                        //eventually be in the least significant position



                        for( i=0; i<7; i++ )

    {

        serial_byte >>= 1; // shift your result byte to the left by one.  

                        // We'll do this a total of 7 times.



        wait( 1_bit_period );



                        if( serial_input == high )

            serial_byte |= 0x80; 

    }



                        //we could also check the stop bits, to see if there is a framing error.



                        return serial_byte;

}

When doing this sort of thing in code, be aware that all operations, such as looping, testing bits, setting bits, etc, take time. Note that in the sample above we have branches that take different amounts of time depending on conditions. We would want to balance that:

if( serialinput == high ) serialbyte |= 0x80; else serial_byte |= 0x00;

Even though the 2nd operation does nothing - it will burn the same amount of time as in the opposite case.

If you use loops for timing, these usually do not behave ideally, there will be extra delay in the call and return for instance.

While you can, in principle, use the microcontroller's data sheet to exactly predict the timing everything takes, it generally is easier to tweek your algorithm using a logic analyzer. For example you could toggle a pin whenver you read the serial input. You could then look at this pin and compare it with the real serial data - tweaking where needed to make the timing perfect. When you take out the pin toggle code, you'll want to replace it with some NOPs that take the same amount of time. (Or, you can leave it in, but change it to operate on a pin that doesn't really exist).

What are the settings for the Saleae serial analyzer?

Bit Rate

The number of bits/second being sent. (Note that this is also often called the baud rate, but this is a term that actually is more relevant to modems, and is typically misused - it does not actually mean bits per second http://en.wikipedia.org/wiki/Baud>http://en.wikipedia.org/wiki/Baud)

You can figure out what the bit rate is by recording some data, and then measuring how wide the stop bit is.

In the example above, the pulse width of a single bit is measured to be about 0.104ms. This is the "bit period" or how long each bit is on the bus. Taking 1/period gives us the frequency, in this case yealds 9592bps (ahh hah, probably 9600bps is what they're going for).

Once you know the bit rate, you can enter it into the serial analyzer settings.

Common serial bit rates

You may notice that there's a lot of serial bit rates that seem very common, such as 9600bps. This is a legacy of the early serial controllers, and reflects the various multiples they were able to produce with their hardware. 300, 1200, 9600, 14400, 28800, 57600, 115200. These probably ring a bell. You don't need to limit yourself to these however, any bit rate is fine. The main thing is that devices agree to use the same bit rate.

Bits per transfer

Bits per transfer is usually 8, as you might imagine. However custom serial implementations could easily use a different number of bits per transfer, such as 16 or 32.

Stop Bits

The only thing the Saleae analyzer does with the stop bits setting is check for framing errors.

Parity Bits

If your application uses the parity bit, here's where you can specify whether the Saleae serial analyzer should check for even or odd parity. The analyzer will report parity errors if they occur.

Bit order

You can specify if bits arrive most signifcant bit first, or least significant bit first.

Inverted

Somtime serial can be inverted - so that logic high means 0 and logic low means 1. If the data looks like it's idle level is low (rather than the more common high) then it might be inverted.

The above data looks to be inverted.

MP Mode

MP Mode (also called Multiprocessor mode, muti-drop, or 9-bit serial) is a version of serial where the most significant data bit (almost always the last bit) indicates if the preceeding 8-bits is data or an address.

Serial data as displayed on Saleae logic analyzers

Sampling Dots

Little white dots are displayed at the location where the analyzer thought was the correct locations to sample the bits. If this doesn't appear to be lined up properly with the bits, its most likely that the sample rate setting doesn't match the real data.

Error dots

If there is a framing error or parity error detected, a red dot will appear where the data was sampled, and the text in the bubble will indicate what the error is.

Hello World

You can change the display type as shown

Lastly, you can export the data to a text format if you like, as shown.

References

Wikipedia


trello ID: 57215d6370ea3b4f27eb4ead
Have more questions? Submit a request

Comments

Powered by Zendesk