How to use UART with a PIC microcontroller

As we’ve seen in the tutorial about creating a distance sensor for your garage, it’s important to have a convenient method to debug the systems we’re developing. This allows us to quickly access the values of variables, and lets us know the state of our program. In the distance sensor tutorial we used a LCD display, which is very user-friendly, but not the most efficient as it uses many pins of the PIC, and a lot of space on the board. In this tutorial, we’ll learn how to use one of the easiest and most lightweight embedded communication interface, the UART interface.

What is UART?

UART stands for Universal Asynchronous Receiver/Transmitter. It is a serial communication link that lets us exchange data between two devices. There are other variants of UART, such as USART, but we’ll only focus on the classic UART communication in this tutorial.

When using UART, there are two data lines: TX and RX. This means that each device will have one port to transmits data (TX), and another one to receive data (RX). When interfacing two systems with UART, the TX and RX ports need to be crossed, so that the RX port of device1 is connected to the TX port of device2, and the TX port of device1 is connected to the RX port of device2.

connect_uart
Connecting 2 devices with UART

Bill of material

I’ll use a PIC16F88 microcontroller for this tutorial. In addition to the usual components required for the PIC to run (see this article for a reminder), we’ll only need a USB to UART adapter. I use this FTDI board by Adafruit. It simply converts the UART protocol to USB and vice-versa.  This lets us use a USB port on a computer to communicate with UART. So why don’t we just use USB instead of UART in the first place? That’s simple: USB is much more complex that UART, and it is not included in most MCUs. UART on the other hand, is a very lightweight and straight forward communication method, that’s included in pretty much any MCU.

Build the circuit

The circuit is very simple. We just need to connect the USB to UART adapter to the microcontroller. As we’ve seen before, the TX pin of the PIC (pin 11) needs to be connected to the RX pin of the adapter, and the RX pin of the PIC (pin 8) needs to be connected to the TX pin of the adapter. You can leave the pins RTS, CTS and Vcc of the adapter unconnected. The first two are used for the flow control of the UART, which is not needed for a simple application like ours. Vcc can be unconnected because the adapter will be powered by the USB cable. However, Gnd must be connected to the ground of our circuit.

pic_uart_circuit
Circuit for UART communication with a PIC

Software

Setting up the UART module

Pins RX and TX

We need to look at the datasheet of our microcontroller to figure out how to use the UART module. Reading the part about UART operation, we first learn that the bits SPEN (RCSTA<7>) and bits TRISB<5,2> have to be set for us to use the pins for UART communication. We’ll set pin RX (pin 8) as an input, and pin TX (pin 11) as an output:

Baud rate

The baud rate is the speed of data transmission on the UART interface. The microcontroller has a register SPBRG, that we can use to set up the baud rate generator value (BRG). This value will be controlling a timer inside the PIC, that will be used to send and receive data on the UART lines. To calculate BRG, we’ll use the formula below:

brg_formula
Formula to calculate the BRG value

We’ll use the UART module in asynchronous mode, which means full duplex (one line per direction, e.g. RX and TX are separate lines). BRGH will be set to 1 in the TXSTA register that we’ll talk about a bit later. This will give us a smaller error percentage. In this tutorial we’ll use a baud rate of 9600, which corresponds to BRG = 51 in high speed mode.

TXSTA register

This register controls the TX side of the UART module. Here are the bits to configure, taken from the datasheet:

txsta_reg
TXSTA register

We’ll set the values below:

  • TX9 = 0, for 8-bit transmission
  • TXEN = 1, to enable transmission
  • SYNC = 0, for asynchronous mode
  • BRGH = 1, for high speed mode

RCSTA register

This register controls the RX side of the UART module, here is its description:

rcsta_reg
RCSTA register

We’ll set the values below:

  • SPEN = 1, to enable the serial port
  • RX9 = 0, to use 8-bit mode
  • CREN = 1, to enable continuous reception
  • FERR = 0, disable framing error bit (we could enable it later, but let’s keep it simple for now)
  • OERR = 0, disable overrun error

Code of the init function

To sum it up, here is the code of the init function:

Transmitting data

Let’s now look at how to transmit data using the UART module. The steps are described in the datasheet as shown below:

uart_transmit
Transmit data with UART

To keep it simple for now, we won’t be using any interrupt. In a real product, it’s often better to use interrupts, as it allows you to wait for data in a non-blocking manner. Without interrupts, like we’ll see in a bit, when we’ll read data we’ll have to wait or poll for data to be available, which will either block the program or use CPU cycles for nothing.

We’ve already done steps 1) to 6) as part of our initialization function. Now the only thing we need to do is to load data into the TXREG register. This will automatically start the transmission. Before we load data in TXREG though, we need to wait for the TRMT bit to be set to 1, which means that the transmit buffer is empty. Otherwise, the previous transmission is not finished yet and we would overwrite previous data before it’s sent out.

Send a single character

Note that because we’re sending 8 bits at a time, we’ll only send one character at a time. We’ll need to repeat the operation to send a string. Below is the code for the UARTSendChar() function:

Send a string

To send a string, or array of characters, we’ll simply call UARTSendChar() for each character in the string. We’ll stop when we read the null termination character '\0' , or when we’ve sent out the number of character given as an argument. Here is the function UARTSendString():

Test the transmission

We’ll write a simple main function to test UARTSendString(), that will send “Hello world!”. We’ll add a new line and a carriage return to go to the beginning of the next line after each print. Here is the code used to do this:

Now let’s open a terminal and connect to the USB to UART adapter. On Windows, you can use RealTerm, which is very good. If you’re on Mac or Linux, and are not afraid of the command line, you can use the command screen . This is the method I’ll describe.

You first need to find your USB to UART adapter in the /dev folder. Usually, the name will contain “usb”, and so you can use grep to filter the list of devices as shown below:

find_usb_uart_adapter
Find the USB to UART adapter in /dev

Now we’ll type in the command to open a screen session with the USB to UART adapter, also giving the baud rate we previously set to 9600:

And that’s it, now you should see the text “Hello world!” being printed every 500 ms!

uart_tx
Example of UART transmission

One last note, because it took me a while to figure it out. To close the screen session, hit ctrl+a, and then type “:quit” and hit enter. This will exit the screen terminal, and close the session. If you don’t close it, you may have trouble reconnecting to the adapter later, as it will still be connected to the previous screen session.

Receive data

Now let’s see how to receive data from the UART link. According to the datasheet, the steps to follow are:

uart_receive
Receive data with UART

As we already did steps 1) to 6), we now only need to read the register RCREG.

Check for available data

Similarly to how we wait for the TX register to be empty before sending out new data, we can check whether new data is available, using the RCIF bit of the PIR1 register. This is an interrupt flag, but we can read it even if interrupts are disabled. The flag is cleared by reading RCREG, so we don’t need to manually clear it.

This is the function we’ll use to check if new data is available:

Read one character

Again, each data received will be only 8 bits (or 1 character). This is the function we’ll use to receive a single character:

Read a string

To receive a string, we’ll read characters until we read '\0' , or until we’ve read a given number of characters, passed as an argument. The function will return the number of characters actually read. We’ll terminate each read string by the null termination character, which means that max_length must be chosen accordingly, as the last character will always be '\0'. Here is the code:

Test UART reception

We’ll write a quick test for the new functions we just wrote. What we’ll do is:

  1. Send a prompt
  2. Read the response
  3. Send the number of characters read, and the content of the response
  4. Repeat

Here is the code for this test:

The result is shown on this screenshot. Note that screen  doesn’t print the characters types, this is why the prompt line is always empty.

bidirectionan_uart
Bidirectional UART comminucation

As you can see, it’s exactly what we want: it reads bytes until we receive one of the termination characters we chose ( '\0' , '\n'  or '\r' ), or until it reads 16 characters (minus 1 for the '\0'  final character).

You probably noticed that we used a constant  MAX_LENGTH_UART  when we know that the strings are null-terminated. This way we don’t have to count the characters in every string; we can just add the '\0'  character at the end of the string and give a long enough value for max_length.

Full code and conclusion

The full code for this tutorial is available on my Github page. You can re-use the UART library in any project, by simply copying the UART.c and UART.h files.

As we’ve seen, it’s pretty easy to set up a PIC to communicate with UART. This is a great way to debug your program, as you can quickly print some text out, as well as send data to the PIC. UART can also be used to communicate with another microcontroller, or any other device that supports it (Raspberry Pi, computer, Arduino, …).

If you select it as a communication interface in a project, you’ll most likely need to set up a protocol that determines how you’ll exchange data. For example, if one device needs to get data from another one, you could implement a simple master/slave protocol, so that the master will request data to the slave. It can, for example, send a specific byte to the slave, which will send some data depending on the value of this byte.

Be the first to comment

%d bloggers like this: