UART Communication from Linux Userspace

1.0 Introduction

Oftentimes sensors used in embedded systems, have as a method of communication a UART interface to send and receive data to/from the sensor.

There are several advantages and disadvantages to this hardware interface which we will go over in this article. We will also go over how to interact with a UART interface in Linux when connecting an external sensor to the UART4 pins exposed on the J3 expansion header of the Pandaboard ES.

2.0 Hardware Interface Short Description

The Universal Asynchronous Receiver Transmitter (UART) hardware interface is considered an asynchronous interface because it lacks the use of a clock signal to synchronize and frame the data between the consumer and the producer.

Due to the lack of this clock signal we must rely on START and STOP bits to frame the data for us. These framing bits add a considerable amount of overhead to the data transmission. For example if we select our hardware interface to have 2 start bits followed by our 8 bits of data followed by  1 stop bit, we have added 3 bits of overhead for every 8 bits of data or about 38% of overhead data. In addition, the protocol also supports the use of a parity bit, a way of detecting an odd number of bit errors transmitted on the wire.

However, despite these limitations, UART can still be quite useful. One of its main advantages is the connection simplicity, needing on two wires for the TX and RX paths. Moreover, we can implement UART in software with relative ease using a timer interrupt and two GPIOs. Lastly, the bit rates supported by the protocol are normally sufficient to interact with “slower” sensors.

Bit rates from 110 to 921600 bits/second are typically supported.

3.0 Electrical Example

In this example I attached a scope probe to the output of an UART chip running at 9600 bits/s. The UART was outputting on its TX line the character “c” with a hex value of 0x63  and a binary value of 0b 0110 0011.

The oscilloscope output was as follows:

scope3

Please note that the blue signal on channel 2 is an external signal I am using simply to frame the data such that we can see what’s going on. It is in no way part of the actual UART communication.The channel 2 signal is a 9600 Hz signal and as we can see approximately one cycle of this signal represents 1 bit of data.

By following the change of the Channel 1 signal we can observe all the bits of the transmission. There is one last tricky part here, which we must be careful with, the processor outputting this data runs in little endian mode, and as such the least significant bits will be outputted first. This is better exemplified if we pick a letter to output which is not symmetric in its ones and zeros.

The letter “p” has a hex value of 0x70 and a binary value of 0b111 0000 the output of this letter when transmitted through UART looks like:

scope4

As we notice, the zeros are transmitted first, followed by the ones. This is because the x86 PC we are using is configured to use little endian as a storage mode. Some other platforms, mostly ones based on the Power architecture, are configured for big endian storage, and if we looked at the output of a UART on a big endian machine, the bit order would be reversed. This brings about the interesting problem of two machines using differing endianness interacting, a classic problem in the field of computer comunication.

4.0 Interacting with a UART port Through Linux

The Linux operating system implements its own driver stack for a UART hardware interface. The entire driver stack is very deep, drawing its roots from the early UNIX days and mounting a “tty” (teletype) inteface to expose the UART hardware. A fantastic article explaining the tty driver stack can be found here.

Pragmatically we will take a look on how to interact with the Linux driver such that we can communicate with our GPS sensor.

The tty driver stack normally exposes a device node (previously discussed here) in /dev/ by the name of tty.  If we navigate to /dev/ and list all the device nodes we see several tty devices.
scope4
Here, we must be careful as not all these tty devices represent UARTs, Linux uses this driver stack to represent console interfaces, ssh sessions and other character driven interfaces. The device node we are interested in however, is ttyO3. This is the device node which exposes UART4 of the PandaboardES.

To interact with this device node, we can treat it as a file as we did previously with the I2C device node and perform reads and writes. However, in order not to get lost in potential issues, it would serve us well to ensure that the device that we have electrically connected to the J3 UART port on the Pandaboard, has the same transfer rate out of reset as the ttyO3 driver. If there is a mismatch, the driver will sample the framing bits at the wrong time and the data will be garbled.

From our GPS sensor’s data sheet we notice that out of reset, the device broadcasts at a speed of 9600 baud.  As such we must ensure that the Linux driver attempts to read at a speed of 9600 baud also.

To do that, we have to check the properties of the device node using the command stty.

 stty -F /dev/ttyO3 

This command outputs the following:

speed 9600 baud; line = 0;
-brkint -imaxbel

Here we see that the speed is correctly set, if it had not been, we could set the baud rate by entering:

 stty -F /dev/ttyO3 9600  

There is one more issue that we need to take care of before we can communicate with our device. By default the tty device node has echoing enabled, i.e whenever data is received by the driver from the device on the RX line, it will return the data back to the device on its TX line. Since we don’t want to send the data we get from the sensor back to it, we can disable echoing using the command:

 stty -F /dev/ttyO3 -echo 

Now that we have all the settings for the link correctly set, we can communicate with our device, by ready and writing to and from /dev/ttyO3.

Running

echo /dev/ttyO3 

, we get the output from our GPS sensor:

$GPRMC,054730.212,V,,,,,,,240413,,,N*49
$GPVTG,,T,,M,,N,,K,N*2C
$GPGGA,054731.212,,,,,0,00,,,M,0.0,M,,0000*53
$GPGLL,,,,,054731.212,V,N*7F
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GPRMC,054731.212,V,,,,,,,240413,,,N*48
$GPVTG,,T,,M,,N,,K,N*2C

Of course we can write a C/Python program to decode and interpret the GPS sensor data by interacting with the device node. The best part about this, is that we can write a userspace driver and avoid the headaches and dangers associated with kernel driver development.

Until next time.

Happy Coding!

Andrei

Leave a Reply