GPIO Interrupts TM4C123 Tiva Launchpad – External Interrupts

In this tutorial, we will discuss how to use GPIO interrupts which are also known as external interrupts of ARM Cortex M4 based TM4C123 microcontroller using the Tiva C launchpad. We will learn to configure GPIO interrupts as an edge triggered such as positive or negative edge or level triggered such as active high or active low level triggered. We will program the TM4C123G Tiva C launchpad with Keil uvision IDE. 

GPIO Interrupts Introduction

General-purpose input-output pins are the vital components of embedded systems. GPIO pins allow easy integration of external components with microcontrollers. Input pins allow microcontrollers to receive information from the external world and output pins are used to display information or control devices such as motors, etc. 

Why do we need to  use TM4C123 GPIO Interrupts ? 

In the last tutorial on controlling an LED with push button using TM4C123 Tiva C launchpad, we have seen an example to control an onboard LED of Tiva Launchpad using onboard switches such as SW1 (PF0) and SW2 (PF4). In that tutorial, the TM4C123 microcontroller keeps checking the status of the push button by polling PF0 and PF4 bits of PORTF of TM4C123G microcontroller. But one of the main drawbacks of the polling method is that microcontroller will have to check the status of input switches on every sequential execution of the code or keep monitoring continuously (Polling method). Therefore, external or GPIO interrupts are used to synchronize external physical devices with microcontrollers. 

Hence, instead of checking the status of input switches continuously, a GPIO pin which is configured as digital input, can be initialized to produce an interrupt whenever the state of the switch changes. A source of interrupt trigger can be on falling edges, rising edges or both falling and rising edges, level triggered also.

In summary, use of external GPIO interrupts makes embedded system event driven, responsive and they make use of microcontroller’s processing time and resources efficiently. 

TM4C123GH6PM Microcontroller GPIO Interrupts

TM4C123GH6PM microcontroller has 6 GPIO ports such as PORTA, PORTB, PORTC, PORD, PORTE, and PORTF. Each pin of every GPIO port can be configured as a source of external interrupt. In this tutorial, we will see how to configure PF0 and PF4 pins as an external interrupt source. But the procedure to configure other GPIO interrupts will remain the same. 

TM4C123 Tiva C Launchpad has two onboard switches SW1 and SW2 which are connected with PF0 and PF4 GPIO pins. These input switches will be used to demonstrate GPIO interrupt programming examples. 

TM4C123 Push button switches PF0 and PF4

How to Configure External Interrupts of TM4C123 

In this section, we will learn to configure TM4C123 microcontroller GPIO interrupts using their respective configuration registers. 

Find GPO Interrupt Number

TM4C123 microcontroller has an integrated Nested Vectored Interrupt Controller (NVIC) which manages all interrupt requests which are issued either by a processor  (exceptions) or peripherals(IRQs). TM4C123GH6PM microcontroller supports 76 peripheral interrupts (some are reserved)  and each interrupt has a unique number assigned to it. This interrupt number is defined inside the startup file and header file of TM4C123GH6PM. 

NVIC identifies each exception or peripheral interrupt by its numbers. If you see table 2.9 in the datasheet of TM4C123GH6PM MCU, you will find the unique number assigned to each exception and peripheral interrupt. As you can see in the second column of figure shown below, the interrupt number of GPIO PORTF is 30. 

GPIO Interrupt number in vector table TM4C123G Tiva C Launchpad

But one important thing to note here is that PORTF has 8 pins and only one interrupt number is assigned to all the pins of PORTF. This is because the separate interrupt register is used to distinguish which pin of PORTF caused the interrupt. We will discuss it later on in this article. 

Enable GPIO Interrupts

Before enabling any peripheral interrupt, we must enable the source of interrupt requests for this particular interrupt source using the NVIC interrupt control register. In the interrupt vector table, there is an interrupt enable bit number for each interrupt source. We can enable these interrupt bits using NVIC interrupt enable registers. There are four NVIC interrupts enable registers such as EN0(ISER[0]),  EN1(ISER[1]), EN2(ISER[2]) and EN3(ISER[3]). 

The ENn(ISER[n])registers enable interrupts and shows which interrupts are enabled. Bit0 of EN0 corresponds to Interrupt 0; bit31 corresponds to Interrupt 31. Bit 0 of EN1(ISER[1])corresponds to Interrupt 32; bit31 corresponds to Interrupt 63 and so on other two registers. 

For example, the interrupt number of PORTF is IRQ30. Hence the corresponding NVIC interrupt enable register of PORTF is ISER[0] and setting bit 30 of ISER[0] will enable the PORTF interrupt. 

NVIC->ISER[0] |= (1<<30); /*Enable PORTF Interrupt IRQ30 */

After enabling interrupt in NVIC register, enable the interrupt of the peripheral which you want to use. For example, we will be using GPIOF pins interrupt. In order to enable GPIO interrupt GPIO interrupt mask enable register is used. First eight bits of GPIOIM register enable or disable interrupt functionality for each pin as shown in figure below:

GPIO Interrupt mask register TM4C123 microcontroller tiva c

For example, we will use PF0 and PF4 pins of  PORTF to get SW1 and SW2 status on interrupt. This line enables interrupt for PF0 and PF4 pins.

GPIOF->IM |= (1<<4)|(1<<0);

GPIO Interrupt edge or level triggered setting (GPIOIS)

As we mentioned earlier, external GPIO interrupts of TM4C123G microcontroller can be configured in four modes:

  1. Positive edge triggered 
  2. Negative edge triggered
  3. Positive Level ( active high)
  4. Negative Level  (active low)

GPIO interrupt sense register is used to configure respective pins either as a level or edge triggered. Setting a bit in the GPIOIS register configures the corresponding pin to detect levels while clearing a bit configures the corresponding pin to detect edges.

GPIO Interrupt sense register TM4C123 edge or level sense

For example, this line initializes the PF0 and PF4 pins as edge triggers.

GPIOF->IS  &= ~(1<<4)|~(1<<0); /* make bit 4, 0 edge sensitive */

Similarly, this line initializes the PF0 and PF4 pins as level triggers

GPIOF->IS  |= (1<<4)|(1<<0); /* make bit 4, 0 level sensitive */

GPIO Interrupt Even Register (GPIOIEV)

GPIOIS register only configures the pins as level or edge triggered. But we should also configure the pin either as a positive/negative edge or positive/negative level triggered. Setting the respective bit in GPIOIEV register configures the corresponding pin to detect positive edge or positive level depending on in which mode pin is configured in GPIOIS register. Similarly, Clearing the respective bit in GPIOIEV register configures the corresponding pin to detect negative edge or negative level. 

TM4C123 GPIO Interrupt event register
GPIOF->IEV &= ~(1<<4)|~(1<<0);  /*PF0, PF4 falling edge trigger */

GPIO Interrupt Both Edges (GPIOIBE) 

We can also configure GPIO pins to cause interrupt on both positive or negative edges. GPIOIBE register is used to configure each pin to cause an interrupt on both edges. setting a bit in the GPIOIBE register configures the corresponding pin to detect both rising and falling edges, regardless of the corresponding bit in the GPIO Interrupt Event (GPIOIEV) register (see page 666). Clearing a bit configures the pin to be controlled by the GPIOIEV register.

TM4C123 GPIO Interrupt on both edges register GPIOIBE

These lines configure the PF0 and PF4 pins to detect positive edges.

GPIOF->IS  &= ~(1<<4)|~(1<<0); /* make bit 4, 0 edge sensitive */
GPIOF->IBE &=~(1<<4)|~(1<<0);  /* trigger is controlled by IEV */
GPIOF->IEV &= ~(1<<4)|~(1<<0);   /* falling edge trigger */

GPIO Interrupt Handler Name 

In the startup file of TM4C123G microcontroller, there is a vectored mapped table which contains the starting addresses of all system exceptions and peripheral interrupt service routines. This file also contains the dummy implementations of exception Handlers or interrupt Handlers. These dummy implementations do nothing except an infinite loop and we can modify these dummy interrupt handlers inside our main code. 

So in order to implement specific functionality inside the respective interrupt handler function, we should find the name of the corresponding peripheral interrupt in the startup file. For example, if you open the startup file of TM4C123 and explore it, you will find that the name of PORTF interrupt handler is GPIOF_Handler. Inside our main code, we will use GPIOF_Handler as a function name for GPIOF interrupt service routine.

GPIOF Interrupt Handler in startup file of TM4C123G

The linker script file will automatically replace the dummy implementation of GPIOF_Handler inside the startup file with our defined implementation. 

TM4C123 Tiva C GPIO Interrupt Example 

For demonstration purposes, in this example, we will configure PF0 and PF1 pins to cause interrupts on negative edges. On TM4C123 Tiva C Launchpad, there are two onboard switches such as SW1 and SW2. SW1 is connected with the PF4 pin and SW2 is connected with PF0 pin as shown in figure below. 

TM4C123 GPIO Interrupt example with push button as an input

One end of both input switches are connected with ground and the other end with PF0 and PF4 pins. TM4C123GH6PM microcontroller provides internal pull-up resistors which can be configured using GPIOPUR register. This line enables the pull-up resistor for PF4 and PF0 pin.  

 GPIOF->PUR |= (1<<4)|(1<<0);  /* enable pull up for PORTF4, 0 */

By enabling pull-up resistors, active high signals will appear on PRF4 and PF0 pins when switches are not pressed. But when we press the switch button, the transition of the signal occurs from active high to active low that means a negative edge will occur. We will configure PF0 and PF4 pins to detect negative edges and cause an interrupt. Hence, whenever the user presses either of the push buttons, an interrupt will be generated and TM4C123 microcontroller executes the interrupt handler function of PORTF. 

Inside the PORTF interrupt handler function, we will turn on the onboard green LED of TM4C123 Tiva C launchpad, if SW1 is pressed and turns off the LED if SW2 is pressed. 

External Interrupt Code TM4C123 MCU

This code controls the green LED of the TM4C123 Tiva launchpad based on SW1 and SW2 states. Both switches are used to generate external interrupt signals on negative edges (falling edge). If the interrupt is caused by SW1(PF4) LED will turn on and if the interrupt is caused by SW2(PF0) LED will turn off. 


/*PORTF PF0 and PF4 fall edge interrupt example*/
/*This GPIO interrupt example code controls green LED with switches SW1 and SW2 external interrupts */

#include "TM4C123.h"                    // Device header

int main(void)
{
    
	  SYSCTL->RCGCGPIO |= (1<<5);   /* Set bit5 of RCGCGPIO to enable clock to PORTF*/
    
	 /* PORTF0 has special function, need to unlock to modify */
    GPIOF->LOCK = 0x4C4F434B;   /* unlock commit register */
    GPIOF->CR = 0x01;           /* make PORTF0 configurable */
    GPIOF->LOCK = 0;            /* lock commit register */


    /*Initialize PF3 as a digital output, PF0 and PF4 as digital input pins */
	
    GPIOF->DIR &= ~(1<<4)|~(1<<0);  /* Set PF4 and PF0 as a digital input pins */
    GPIOF->DIR |= (1<<3);           /* Set PF3 as digital output to control green LED */
    GPIOF->DEN |= (1<<4)|(1<<3)|(1<<0);             /* make PORTF4-0 digital pins */
    GPIOF->PUR |= (1<<4)|(1<<0);             /* enable pull up for PORTF4, 0 */
    
    /* configure PORTF4, 0 for falling edge trigger interrupt */
    GPIOF->IS  &= ~(1<<4)|~(1<<0);        /* make bit 4, 0 edge sensitive */
    GPIOF->IBE &=~(1<<4)|~(1<<0);         /* trigger is controlled by IEV */
    GPIOF->IEV &= ~(1<<4)|~(1<<0);        /* falling edge trigger */
    GPIOF->ICR |= (1<<4)|(1<<0);          /* clear any prior interrupt */
    GPIOF->IM  |= (1<<4)|(1<<0);          /* unmask interrupt */
    
    /* enable interrupt in NVIC and set priority to 3 */
    NVIC->IP[30] = 3 << 5;     /* set interrupt priority to 3 */
    NVIC->ISER[0] |= (1<<30);  /* enable IRQ30 (D30 of ISER[0]) */

    
    while(1)
    {
			// do nothing and wait for the interrupt to occur
    }
}

/* SW1 is connected to PF4 pin, SW2 is connected to PF0. */
/* Both of them trigger PORTF falling edge interrupt */
void GPIOF_Handler(void)
{	
  if (GPIOF->MIS & 0x10) /* check if interrupt causes by PF4/SW1*/
    {   
      GPIOF->DATA |= (1<<3);
      GPIOF->ICR |= 0x10; /* clear the interrupt flag */
     } 
    else if (GPIOF->MIS & 0x01) /* check if interrupt causes by PF0/SW2 */
    {   
     GPIOF->DATA &= ~0x08;
     GPIOF->ICR |= 0x01; /* clear the interrupt flag */
    }
}

Now create a new project using Keil uvision and upload this code to TM4C123G Tiva C Launchpad. After that press the reset button of the launchpad. Finally, check the working of code, by pressing SW1 and SW2. 

TM4C123 GPIO Interrupts example with push button negative edge triggered

Differentiating which GPIO pin causes Interrupt

As we mentioned earlier, there is only one interrupt service routine for each GPIO port. For example, PORTF has one interrupt handler function that is  GPIOF_Handler(). Interrupt caused by all the pins of PORTF executes the same  GPIOF_Handler() interrupt service routine. Now the question is how to differentiate which of the pins of PORTF forces the  GPIOF_Handler() function to execute? 

Well that’s pretty easy, TM4C123GH6PM microcontroller has GPIO masked interrupt status register (GPIOMIS). This register provides the status of interrupt caused by each pin. The first eight bits of this register corresponds to the PIN0 to PIN7 of each GPIO interrupt status. 

For example, if the interrupt service routine of PORTF is called due to PF0 pin, the 0th bit of GPIOMIS register will be one and if it is caused by PF4 pin, 4th bit of GPIOMIS register will set automatically. Hence, by checking the value of each bit of GPIOMIS register inside the PORTF interrupt handler function, we can identify which pin causes this particular interrupt. 

In this code, we used GPIOMIS register to differentiate either PF0 or PF4 causing the external interrupt, and based on this, the code executes only the true condition block of if-else.

/* SW1 is connected to PF4 pin, SW2 is connected to PF0. */
/* Both of them trigger PORTF falling edge interrupt */
void GPIOF_Handler(void)
{	
  if (GPIOF->MIS & 0x10) /* check if interrupt causes by PF4/SW1*/
    {   
      GPIOF->DATA |= (1<<3);
      GPIOF->ICR |= 0x10; /* clear the interrupt flag */
		} 
    else if (GPIOF->MIS & 0x01) /* check if interrupt causes by PF0/SW2 */
    {   
     GPIOF->DATA &= ~0x08;
     GPIOF->ICR |= 0x01; /* clear the interrupt flag */
    }
}

Other TM4C123 MCU Interrupt Tutorials

Leave a Comment

sixteen + 1 =