Embedded Software Development: PIC16F1829 UART Programming

Today, many PIC Micro Controllers include a Universal Synchronous/Asynchronous Receiver Transmitter (USART) module. USART module can be configured as follows:

  • Full-Duplex asynchronous system that can communicate with peripheral devices, such as CRT terminals and personal computers
  • Half-Duplex synchronous system that can communicate with peripheral devices, such as A/D or D/A integrated circuits, serial EEPROMs, etc

Getting the UART module up and running on PIC is quite easy. In this example, we setup UART for PIC16F1829. The below code sets up the PIC registers for UART operations.

void main()
//Complete basic PIC configurations here
//UART Module Configuration
SSP1CON1bits.SSPEN = 0; //disable I2C port
SSP2CON1bits.SSPEN = 0; //disable SPI port
BAUDCONbits.BRG16 = 1; //use 16 bit baud
SPBRG = 207; //setup for baud rate 9600
TXSTAbits.TXEN = 1; //enable transmit
RCSTAbits.CREN = 1; //enable reception
RCSTAbits.SPEN = 1; //enable UART module
PIR1bits.TXIF = 0; //clear transmit interrupt
//Interrupt Configuration
PIR1bits.RCIF = 0; //clear reception flag
PIE1bits.RCIE = 1; //enable interrupt
INTCONbits.PEIE = 1; //enable peripheral interrupts
INTCONbits.GIE = 1; //enable global interrupts

We are relying on interrupts to make use of the UART module for sending and receiving bytes. With the above configuration in place, as soon a byte is received the program enters the ISR. Below, we write code to place the received byte into an input buffer and clear all related interrupts and flags. Please note, that it is very important that you check for and clear all interrupts and related flags within the ISR for the execution to resume to the main program. Unless and until you do it, the program would consider the interrupt unhandled and will not exit the ISR.

We also keep a maximum value for the buffer size to define the maximum number of received bytes that we will store in RAM. If the number bytes received exceeds the buffer size, the program starts writing to the first index of the array again. This is important to corruption of program RAM resulting from overrun of data.

void interrupt ISR()
//Handle UART reception
if (PIE1bits.RCIE)
if (PIR1bits.RCIF)
PIR1bits.RCIF = 0; //clear interrupt flag
recBuffer[recPointer] = RCREG; //copy data to receive buffer
if (recPointer >= maxRecBufferSize)
recPointer = 0; //don’t let receiver buffer exceed max
//Handle UART Transmit
if (PIE1bits.TXIE)
if (PIR1bits.TXIF)
TXREG = sendBuffer[sendPointer]; //copy byte to send
if (sendPointer >= totalElementstoSend)
PIE1bits.TXIE = 0; //if all elements sent
PIR1bits.TXIF = 0;
//Handle other interrupts

In order to send a byte, we simply copy the required byte(s) to the transmit data buffer and enable the transmit interrupt which will trigger transmit of data automatically. All the data received using the UART module will be stored in the recBuffer array variable and can be consumed by the main program.

Another issue to address to complete the code is setting up error handlers to handle the framing and overrun errors. When PIC’s FIFO buffer receives more than 2 full bytes before the RCREG is read an overrun error occurs. If you don’t clear the error the serial port will not receive any new data until a power on reset is done. Since I’m using interrupts this shouldn’t be an issue, but let’s be safe. The code below helps handle this condition.

void errorHandler()
PIE1bits.RCIE = 0;
PIR1bits.RCIF = 0;
char dummy;
dummy = RCREG;
dummy = RCREG; //Read RCREG twice
RCSTAbits.SPEN = 0;
RCSTAbits.SPEN = 1;
PIE1bits.RCIE = 1;

With that in place you should be able to send and receive data using your PICs UART module. This is by no means a complete program, but the idea it share with you the important bits of information required to setup and get UART working.