In the last tutorial, we learned to use UART communication modules of TM4C123G Tiva C Launchpad. We have seen a demo to transmit and receive data between the computer and TM4C123G Tiva C on UART5 port using a busy-wait or polling method. In this tutorial, we will see how to make the UART receiver port interrupt-driven. In short, we will learn to use the UART interrupt of TM4C123G Tiva C Launchpad.
Let’s first discuss the drawback of using a polling method.
Why is Interrupt better than polling method?
In the last tutorial, we used a polling or busy-wait method to transmit and receive data. The disadvantage of the polling method over the interrupt method is that a microcontroller has to repeatedly check if data is available on the receiving port of UART or not. Microcontroller will not do any other useful function except check the status of the Rx pin.
Let’s try to understand the drawback of polling method using this code from the previous tutorial.
main()
{
while(1)
{
char c = UART5_Receiver(); //line1
UART5_Transmitter(c); // line2
}
}
char UART5_Receiver(void)
{
char data; //line1
while((UART5->FR & (1<<4)) != 0); //line2
data = UART5->DR ; //line3
return (unsigned char) data; //line4
}
In above code, when control of microcontroller goes to line1 inside the main function, it will start to execute UART5_Receiver() function, but inside the UART5_Receiver() function when microcontroller starts to execute line2, it will stuck inside this “while((UART5->FR & (1<<4)) != 0)” loop and the TM4C123G MCU keeps checking the status of the RX pin of the UART5 module using the RXFF status flag until data becomes available on the Rx pin. Hence, control will transfer to line3 only when data is available. Otherwise, Tiva C MCU will go into a blocking state waiting for the data to become available. This is a wastage of microcontroller resources.
When data becomes available on RX pin, microcontroller comes out of blocking state and starts to execute this line:
data = UART5->DR ; //line3
MCU reads data from UART5 data register (UART5->DR) and returns data to character ‘c’ inside the main function.
return (unsigned char) data; //line4
After that TM4C123G microcontroller, transmit the received data through the Tx pin of TM4C123G Tiva C Launchpad.
UART5_Transmitter(c); // line2
In summary, Using interrupts is generally more efficient than polling for handling peripheral data in microcontrollers. In polling, the microcontroller continuously checks the status of a peripheral, such as a UART receiver, which results in wasted processing time and prevents the execution of other tasks. For instance, in the provided code example, the microcontroller is stuck in a loop waiting for data to become available, effectively blocking other functions and consuming valuable processing resources. In contrast, interrupts allow the microcontroller to perform other operations while waiting for an event. When data arrives, an interrupt is triggered, which then prompts the microcontroller to process the data immediately. This approach enhances efficiency and responsiveness, as it optimizes resource usage and allows for concurrent task execution.
Difference Between Interrupt and Polling Method
Hence, TM4C123GH6PM microcontroller will do nothing except checking the status of the RX pin. Therefore, it is kind of a waste of microcontroller processing time and resources. On the contrary, by using interrupt driven approach, microcontroller does not have to keep checking the status of Rx pin instead Tiva C microcontrolller will receive an interrupt signal from UART interrupt register and this interrupt signal indicates that the data is available to receive. But if data is not available, a microcontroller can execute other parts of the code.
The difference will become more clear to you when you see the actual working of TM4C123 UART interrupt code in the later sections of this tutorial.
How to Enable UART interrupt of TM4C123G Tiva C Launchpad
In order to enable UART interrupt feature, UART interrupt mask register (UARTMI) is used. Setting the related bit of UART interrupt mask register allows the UART module to send interrupt requests to nested vector interrupt controller (NVIC) of TM4C123G microcontroller.
NVIC is the main part of the ARM Cortex M processor that manages interrupts, If you want to explore further about NVIC, you can read this article:
Coming back to the main discussion, bit 5 of UARTMI register is used to enable or disable UART receiver interrupt request to NVIC. For example, setting bit5 of the UARTMI register will enable UART Rx interrupt and clearing it will disable the interrupt.
This line enables the Rx interrupt for UART5 port.
UART5->IM = 0x0010; // enable UART5 Rx interrupt
The second register associated with UART interrupt is the interrupt clear register (UARTICR). Bit 5 (RXIS) of UARTICR register is Receive Interrupt Clear flag. When UART receiver interrupt occurs, the programmer should explicitly clear the RXIS bit. Writing a 1 to this bit clears the RXRIS bit in the UARTRIS register and the RXMIS bit in the UARTMIS register.
This line clears the receive interrupt of UART5:
UART5->ICR &= ~(0x010); // Clear receive interrupt
Write UART Receiver Interrupt Handler function
We will be using UART5 port in this tutorial. Therefore, we will discuss UART5. But concepts and process is same for other UART modules also.
In order to write UART5 Receiver Interrupt Handler function, you should first find its interrupt number. Interrupts numbers of all peripheral interrupts and system exceptions are defined inside the interrupt vector table and numbers are assigned in the register definition file of TM4C123GH6PM microcontroller.
If you want to find the interrupt number of UART5 Receiver Interrupt Handler, you can check inside the datasheet of TM4C123GH6PM or using TM4C123GH6PM.h file in keil IDE. The interrupt number of UART5 is 61.
TM4C123G MCU supports 78 peripheral interrupts and before using each peripheral interrupt , it should be enabled using NVIC enable register. This line enables UART5 interrupt in NVIC
NVIC->ISER[1] |= 0x20000000; /* enable IRQ61 for UART5 */
UART Handler Function in Startup File
After that go to startup.s file in your project folder and find the name of the UART5 handler function as shown in the picture below:
Now initialize the UART receive interrupt function using the same function name. If you want to know the reason behind using same name, you should read these articles:
UART Interrupt Code TM4C123
In this example code, we will control a GPIO pin PF3 which is connected with a red LED of TM4C123G Tiva launchpad. We will use Putty as a terminal to send messages from the computer to the Tiva C launchpad.
If a received character on the RX5 pin of UART5 module is ‘A’, LED will turn on and if the received message is ‘B’, LED will turn off. For any other character, LED will retain its current state.
#include "TM4C123.h"
#include <stdint.h>
#include <stdlib.h>
void Delay(unsigned long counter);
char UART5_Receiver(void);
void UART5_Transmitter(unsigned char data);
void printstring(char *str);
void UART5_Handler( void )
{
unsigned char rx_data = 0;
UART5->ICR &= ~(0x010); // Clear receive interrupt
rx_data = UART5->DR ; // get the received data byte
if(rx_data == 'A')
GPIOF->DATA = 0x02;
else if(rx_data == 'B')
GPIOF->DATA = 0x00;
UART5_Transmitter(rx_data); // send data that is received
}
int main(void)
{
SYSCTL->RCGCUART |= 0x20; /* enable clock to UART5 */
SYSCTL->RCGCGPIO |= 0x10; /* enable clock to PORTE for PE4/Rx and RE5/Tx */
Delay(1);
/* UART0 initialization */
UART5->CTL = 0; /* UART5 module disbable */
UART5->IBRD = 104; /* for 9600 baud rate, integer = 104 */
UART5->FBRD = 11; /* for 9600 baud rate, fractional = 11*/
UART5->CC = 0; /*select system clock*/
UART5->LCRH = 0x60; /* data lenght 8-bit, not parity bit, no FIFO */
UART5->CTL = 0x301; /* Enable UART5 module, Rx and Tx */
/* UART5 TX5 and RX5 use PE4 and PE5. Configure them digital and enable alternate function */
GPIOE->DEN = 0x30; /* set PE4 and PE5 as digital */
GPIOE->AFSEL = 0x30; /* Use PE4,PE5 alternate function */
GPIOE->AMSEL = 0; /* Turn off analg function*/
GPIOE->PCTL = 0x00110000; /* configure PE4 and PE5 for UART */
// enable interrupt
UART5->ICR &= ~(0x0780);; // Clear receive interrupt
UART5->IM = 0x0010;
NVIC->ISER[1] |= 0x20000000; /* enable IRQ61 for UART0 */
SYSCTL->RCGCGPIO |= 0x20; // turn on bus clock for GPIOF
GPIOF->DIR |= 0x02; //set GREEN pin as a digital output pin
GPIOF->DEN |= 0x02; // Enable PF3 pin as a digital pin
Delay(1);
printstring("Enter the Command");
Delay(10);
while(1)
{
}
}
void UART5_Transmitter(unsigned char data)
{
while((UART5->FR & (1<<5)) != 0); /* wait until Tx buffer not full */
UART5->DR = data; /* before giving it another byte */
}
void printstring(char *str)
{
while(*str)
{
UART5_Transmitter(*(str++));
}
}
void Delay(unsigned long counter)
{
unsigned long i = 0;
for(i=0; i< counter; i++);
}
As you can see in this code, inside the main code, we are not using any UART receive function. UART5 handler function, which is an interrupt service routine for UART5, reads data from the UART5 data register on the interrupt signal. In other words, we can add any other code that we want to execute inside the main function and the interrupt handler will handle the data reception on the RX pin when data becomes available and we do not need to keep polling to receive a character.
Hardware Demo
Now create a project using Keil and upload this code to TM4C123G Tiva launchpad. If you don’t know how to use Keil, you can read these getting started guides:
- How to download and install Keil uVision for ARM and 8051
- Getting started with Keil uVision: Write your first Program for Tiva LaunchPad
Make connections between FTDI cable and TM4C123 Tiva launchpad, according to this table:
TM4C123 Pins | FTDI Cable |
---|---|
TX5/PE5 | RXD (white cable) |
RX5/PE4 | TXD (green cable) |
GND | GND (black cable) |
After that open the putty and click on the reset button of TM4C123 Tiva C, first you will get this string on the putty terminal “Enter the command”.
Now if you send capital A, the red LED will turn on and if you send B, the red LED will turn off. For any other character, LED will retain its current state.
Summary:
In conclusion, transitioning from a polling-based method to an interrupt-driven approach for UART communication on the TM4C123G Tiva C Launchpad significantly enhances system efficiency and responsiveness. While polling continuously occupies the microcontroller with status checks, leading to wasted processing time and blocked operations, interrupts allow the microcontroller to remain free for other tasks until data is available. By utilizing UART interrupts, the microcontroller can promptly respond to data events without the need for constant monitoring, thus optimizing resource utilization and enabling more complex and responsive applications. This approach not only improves the efficiency of the system but also demonstrates a key advantage of interrupt handling over polling in embedded systems.
Related content:
- I2C Communication TM4C123G Tiva C Launchpad
- MPU6050 Gyroscope Accelerometer sensor interfacing with TM4C123G Tiva C Launchpad
- ADC TM4C123G Tiva C Launchpad – Measure Analog Voltage Signal
- GPIO Interrupts TM4C123 Tiva Launchpad – External Interrupts
- Timer Interrupt TM4C123 – Generate Delay with GPTM Interrupt Service Routine