Home » PIC Microcontroller Programming Tutorials » Asynchronous Serial (USART) Communication with PIC16F877A

Asynchronous Serial (USART) Communication with PIC16F877A

PIC microcontrollers, obviously, can do more than just light up LEDs or reading button states. Microcontrollers can also communicate with another microcontroller or with other devices like sensors, memory cards, etc. Often the communication is done serially, where data bits are sent one at a time. The microcontroller serial communication article will teach more about this communication method.

In this article, we will look at how to implement serial communication (USART) with PICs both in assembly language and in XC8.

Hardware USART registers

While you can implement serial communications through “bit-banging”, i.e., setting a pin high or low in specific time intervals (also known as software serial), using the hardware USART module is a much more reliable and easier approach. Software serial offers the advantage of assigning transmit and receive pins it to any output pin. This is useful when you ran out of pins and need to communicate to multiple devices. In contrast, hardware USART exclusively uses the pins RC6 (TX) and RC7 (RX). But hardware serial is not found on all PICs  - the PIC16F84A doesn’t have one. For this tutorial, we’ll be using the PIC16F877A.

To configure the PIC’s hardware USART, we need three registers: TXSTA, RCSTA and SPBRG. The SPBRG is used to calculate the baud rate of the transmissions. The TXSTA and RCSTA registers are shown below:

That’s a lot of bits to look at. Our purpose here is to introduce serial transmission and reception so there’s only four bits to look at TXSTA and 1 bit on RCSTA.

Bit TXEN (5) on TXSTA enables/disables transmission, SYNC (4) sets asynchronous or synchronous mode while BRGH (2) sets high speed or low speed mode. The formula to be used to compute for the baud rate is different in high speed or low speed:

Where X is the contents of the register SPBRG. So for a baud rate of 9600 using a 4 MHz oscillator at high speed and asynchronous,

$X=(4,000,000/9600*16)-1$

$X=25$

TRMT (1) is a flag that sets if the data has been sent. Meanwhile, SPEN (7) of the RCSTA register enables RC6 and RC7 as serial port pins.

The data to be transmitted must be placed inside the TXREG register while the data received is placed inside the RCREG register.

We can simulate the serial communication using Proteus ISIS. Here is the schematic diagram:

Note that other essential parts (like MCLR pullup, oscillator) were not included in the diagram above.

Assembly Code for Transmitting Data

Here’s a PIC ASM code that sends a character ‘A’ out of the serial port of the PIC16F877A at a baud rate of 9600.

You should see a single ‘A’ on the serial monitor.

The first part of the code above configures the serial port by making SPBRG = 25 (as per the calculations above) and enabling transmit (at high speed, async) and receive ports via TXTSA and RCSTA.

The character to be sent is placed inside char0 and then moved to TXREG. Then we use a loop using btfss on TRMT to check if the character has been sent out.

Using XC8 to Send Serial Data

This is how the code above is implemented in XC8:

How about sending strings? XC8 has a built-in library <stdio.h> for that. Apparently, you can use the printf() function to send strings out as long as you have a putch() function defined in your code. Here’s how to send the string “hello world” via the serial port:

Receiving Data using Assembly

As mentioned, the received data through the serial port is stored in RCREG. When RCREG is read and emptied, a flag, RCIF is set. RCIF is an interrupt flag that can be disabled using the RCIE bit in PIR1. However, we don’t need to set interrupts for the RCIF to trigger! Here’s an assembly code that sets PORTB according to the value received from the serial port:

Reception using C

Here’s how the code above looks like in XC8:

Now how do we receive strings? Unfortunately, there isn't direct library function for this one like printf() for displaying strings. I’ve created a simple routine that reads each character from a string and ends the string if a newline is received or the maximum number of characters (10 in this case) per string is received:

Here I declared a getch() function which is actually part of the <conio.h> library but is an empty stub (which means it’s useless unless you create one yourself). This getch() function passes each character received from the serial port to a character array input which is pointed by buffer to start at 0x20, which is the starting address of the general purpose registers of the PIC16F877A.

While asynchronous communication is enough for most applications, there are instances when you need more transmission speed. The additional speed can be achieved if timing between transmitter and receiver has been properly established, which is the case with synchronous serial communication. That is what we’ll cover next.

Was this tutorial useful? If you have any questions, drop a comment below.