PIC16F1829 UART Programming

Today, many PIC microcontrollers include a Universal Synchronous/Asynchronous Receiver Transmitter (USART) module. We can configure this module as follows:

a Full-Duplex asynchronous system that can communicate with peripheral devices, such as CRT terminals and personal computers

a 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
recPointer++;
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
sendPointer++;
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()
{
if (RCSTAbits.FERR||RCSTAbits.OERR)
{
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.

Share This Post: