Lab Practicals Using TivaC LaunchPad Board

From EdWiki

Revision as of 06:45, 2 March 2020 by Jshankar (Talk | contribs) (On-Chip Peripheral Programming)

On-Chip Peripheral Programming

Programming the GPIO in TM4C123

In this article we are going to write our first program for accessing GPIOs on the EK-TM4C123GXL LaunchPad Board.

Memory Map

The ARM Cortex-M4 has 4GB (Giga bytes) of memory space. It uses memory mapped I/O, which means that the I/O peripheral ports are mapped into the 4GB memory space.

The TI EK-TM4C123GXL LaunchPad uses the TM4C123GH6PM microcontroller, which has 256K bytes (256KB) of on-chip Flash memory for code, 32KB of on-chip SRAM for data, and a large number of on-chip peripherals.

Allocated size Allocated address
Flash 256 KB 0x0000.0000 To 0x0003.FFFF
SRAM 32 KB 0x2000.0000 To 0x2000.7FFF
I/O All the peripherals 0x4000.0000 to 0x400F.FFFF

GPIO & Special Purpose I/O

While memory holds code and data for the CPU to process, the I/O ports are used by the CPU to access input and output devices. In the microcontroller we have two types of I/O :

  • General Purpose I/O (GPIO)
    The GPIO ports are used for interfacing devices such as LEDs, Switches, LCD, Keypad, and so on.
  • Special purpose I/O
    These I/O ports have designated function such as ADC (Analog-to-Digital), Timer, UART, PWM and so on.

TM4C123GH6PM Block Diagram

Tm4c123gh6pm io pins.png

TM4C123GH6PM Micro controller

ARM chip used in TI Tiva LaunchPad is Tiva C series TM4C123GH6PM microcontroller.

Tm4c123gh6pm pin Diagram.png

The pins are designated as:
➢ PA0 - PA7
➢ PB0 - PB7
➢ PC0 - PC7
➢ PD0 - PD7
➢ PE0 - PE5
➢ PF0 – PF4

Port-E and Port-F do not have all the 8 pins implemented.


The ARM chips have two buses:

  1. Advanced Peripheral Bus (APB) and
  2. Advanced High-Performance Bus (AHB).

The AHB bus is much faster than APB. The AHB allows one clock cycle access to the peripherals. The APB is slower and its access time is minimum of two clock cycles.

GPIO APB Memory Map

The I/O ports addresses assigned to the Port A - Port F for APB are as follow:

  • GPIO Port A : 0x4000.4000
  • GPIO Port B : 0x4000.5000
  • GPIO Port C : 0x4000.6000
  • GPIO Port D : 0x4000.7000
  • GPIO Port E : 0x4002.4000
  • GPIO Port F : 0x4002.5000

GPIO AHB Memory Map

The Base addresses for the GPIOs of AHB is as follow:

  • GPIO Port A : 0x4005.8000
  • GPIO Port B : 0x4005.9000
  • GPIO Port C : 0x4005.A000
  • GPIO Port D : 0x4005.B000
  • GPIO Port E : 0x4005.C000
  • GPIO Port F : 0x4005.D000

4K bytes of memory space is assigned to each of the GPIO port. Each GPIO has a large number of special function registers (SFR) associated with it and the GPIO DATA register supports bit-banding. The GPIO DATA register is 8-bit wide. With bit-banding, it will need 256 words (4 bytes each, 1 KB total). There are many registers associated with each of the above I/O ports and they have designated addresses in the memory map. The above addresses are the Base addresses meaning that within that base address we have registers associated with that port.

Digital I/O Pads

The TM4C123GH6PM microcontroller contains six ports and thus six of these physical GPIO blocks. Note that not all pins are implemented on every block. Some GPIO pins can function as I/O signals for the on-chip peripheral modules.

Tm4c123gh6pm gpio block diagram.png
Figure: TM4C123GH6PM Micro controller Digital I/O Pads

GPIO Register Map

Name Offset Tivaware Name Description
GPIOIS 0x404 GPIO_PORTn_IS_R GPIO Interrupt Sense
GPIOIBE 0x408 GPIO_PORTn_IBE_R GPIO Interrupt Both Edges
GPIOIM 0x410 GPIO_PORTn_IM_R GPIO Interrupt Mask
GPIORIS 0x414 GPIO_PORTn_RIS_R GPIO Raw Interrupt Status
GPIOMIS 0x418 GPIO_PORTn_MIS_R GPIO Masked Interrupt Status
GPIOAFSEL 0x420 GPIO_PORTn_AFSEL_R GPIO Alternate Function Select
GPIODR2R 0x500 GPIO_PORTn_DR2R_R GPIO 2-mA Drive Select
GPIODR4R 0x504 GPIO_PORTn_DR4R_R GPIO 4-mA Drive Select
GPIODR8R 0x508 GPIO_PORTn_DR8R_R GPIO 8-mA Drive Select
GPIOPDR 0x514 GPIO_PORTn_PDR_R GPIO Pull-Down Select
GPIOSLR 0x518 GPIO_PORTn_SLR_R GPIO Slew Rate Control Select
where n = A or B or C or D or E or F

Accessing GPIO of this microcontroller includes much more steps as this microcontroller provides a lot of feature in a single board, hence requires lots of configuration.

For accessing and configuring the microcontroller pins, we have to load some values in the registers, associated with that particular pin.

Initialization of an I/O port

To initialize an I/O port for general use:

  1. Activate the clock for the port in the Run Mode Clock Gating Control Register 2 (RCGC2).
  2. Unlock the port (LOCK = 0x4C4F434B). This step is only needed for pins PC0-3, PD7 and PF0 on TM4C123GXL LaunchPad.
  3. Disable the analog function of the pin in the Analog Mode Select register (AMSEL), because we want to use the pin for digital I/O. If this pin is connected to the ADC or analog comparator, its corresponding bit in AMSEL must be set as 1. In our case, this pin is used as digital I/O, so its corresponding bit must be set as 0.
  4. Clear bits in the port control register (PCTL) to select regular digital function. Each GPIO pin needs four bits in its corresponding PCTL register. Not every pin can be configured to every alternative function.
  5. Set its direction register (DIR). A DIR bit of 0 means input, and 1 means output.
  6. Clear bits in the alternate Function Select register (AFSEL).
  7. Enable digital port in the Digital Enable register (DEN).
  • Please note that we need to add a short delay between activating the clock and setting the port registers.
  • PC0-PC3 is used for JTAG connections to the debugger on the LaunchPad. So we’d better do not use these pins normally.

See Also

  1. EK-TM4C123GXL LaunchPad Board
  2. Switch Inputs and LED Outputs in TM4C123

EK-TM4C123GXL Blinky Program

  1. /* 
  2. Toggling LEDs using special function registers by their names defined in the TivaWare header file
  3. Runs on TI EK-TM4C123GXl LaunchPad Board
  4. */
  7. #include <stdint.h>
  8. #include "inc/tm4c123gh6pm.h"
  10. void delayMs(int n);
  12. int main(void)
  13. {
  14.     SYSCTL_RCGC2_R |= 0x00000020;     /* enable clock to GPIOF at clock gating control register */
  16.     GPIO_PORTF_DIR_R = 0x0E;          /* enable the GPIO pins for the LED (PF3, 2, 1) as output */
  17.     GPIO_PORTF_DEN_R = 0x0E;         /* enable the GPIO pins for digital function */
  19.     while(1) {
  20.         GPIO_PORTF_DATA_R = 0x0E;     /* turn on all LEDs */
  21.         delayMs(500);
  22.         GPIO_PORTF_DATA_R = 0;        /* turn off all LEDs */
  23.         delayMs(500);
  24.     }
  25. }
  27. /* delay n milliseconds (16 MHz CPU clock) */
  28. void delayMs(int n)
  29. {
  30.     int i, j;
  31.     for(i = 0 ; i < n; i++)
  32.         for(j = 0; j < 3180; j++) {}   /* do nothing for 1 ms */
  33. }

See Also

  1. TM4C123GH6PM GPIO Programming
  2. EK-TM4C123GXL LaunchPad Board
  3. Switch Inputs and LED Outputs in TM4C123

Switch Inputs and LED Outputs

There are four ways to interface a switch to the microcontroller as shown in the following Figure:

Interface of a switch to a microcomputer input

We can use either positive or negative logic, and we can use an external resistor or select an internal resistor. Notice the positive logic circuit with external resistor is essentially the same as the positive logic circuit with internal resistance; the difference lies with whether the pull-down resistor is connected externally as a 10 kΩ resistor or internally by setting the corresponding PDR bit during software initialization.

SW1 and SW2 on LaunchPad

SW1 and SW2 on LaunchPad
  • SW1 push-button switch is connected to PF0 pin.
  • SW2 push-button switch is connected to PF4 pin.
  • There is no pull-up resistor connected to SW1 & SW2
  • To use the SW1 and SW2, we need to enable the internal pull-up resistor for PF0 and PF4 pins.

Read SW1 & Display it on the Green LED

To read SW1 and display it on the green LED, the following steps must be taken.

  1. Enable the clock to PortF
  2. Set the Direction register PF4 as input, and PF3 as output
  3. Enable the digital I/O feature of PortF
  4. Enable the pull up resistor option in PUR register since the switch circuit does not have pull-up resistor
  5. Read SW1 on PortF
  6. Invert the value since the switch is active low and the LED is active high
  7. Shift right the switch bit (PF4) to green LED bit(PF3) of the value
  8. Write the value to green LED of PortF
  9. Repeat steps 5 to 8

Source Code

  1. /* Read a switch and write it to the LED */
  2. /* This program reads SW1 of Tiva LaunchPad and writes the inverse of the value to the green
  3.    LED. SW1 is low when pressed (Normally High). LED is ON when high.
  4. */
  6. #include <stdint.h>
  7. #include "inc/tm4c123gh6pm.h"
  9. int main(void)
  10. {
  11.     unsigned int value;
  13.     SYSCTL_RCGC2_R |= 0x00000020;;   /* enable clock to GPIOF */
  15.     GPIO_PORTF_DIR_R = 0x08;         /* set PORTF3 pin as output (LED) pin */
  16.                                      /* and PORTF4 as input, SW1 is on PORTF4 */
  17.     GPIO_PORTF_DEN_R = 0x18;         /* set PORTF pins 4-3 as digital pins */
  18.     GPIO_PORTF_PUR_R = 0x10;         /* enable pull up for pin 4 */
  20.     While(1) {
  21.         value = GPIO_PORTF_DATA_R;  /* read data from PORTF */
  22.         value = ~value;             /* switch is low active; LED is high active */
  23.         value = value >> 1;         /* shift it right to display on green LED */
  24.         GPIO_PORTF_DATA_R = value;  /* put it on the green LED */
  25.     }
  26. }

Read SW2 and Write it on Red LED

To read SW2 and display it on the Red LED, the program is similar to the previous program except extra steps needed to take care of PortF0 as described below:

  • The SW2 is connected to PortF0 pin, which is shared with NMI (non-maskable interrupt).
  • To prevent accidental write to configuration registers and thus disables NMI, the configuration register bits for PortF0 are normally locked.
  • They may be unlocked by writing a pass code value of 0x4C4F434B to the LOCK Register followed by setting bit 0 of the Commit Register (GPIOCR).

Source Code

  1. /* Read a switch and write it to the LED */
  2. /* This program reads SW2 of Tiva LaunchPad and write the inverse of the value to the red LED.
  3.    SW2 is low when pressed. LED is on when high. */
  4. /* SW2 is connected to PORTF0, which is an NMI pin. */
  5. /* In order to use this pin for any function other than NMI, the pin needs be unlocked first. */
  7. #include <stdint.h>
  8. #include "inc/tm4c123gh6pm.h"
  10. int main(void)
  11. {
  12.     unsigned int value;
  14.     SYSCTL_RCGC2_R |= 0x00000020;       /* enable clock to GPIOF */
  16.     GPIO_PORTF_LOCK_R = 0x4C4F434B;     /* unlock commit register */
  17.     GPIO_PORTF_CR_R = 0x01;             /* make PORTF0 configurable */
  18.     GPIO_PORTF_DIR_R = 0x02;            /* set PORTF1 pin as output (LED) pin */
  19.                                         /* and PORTF0 as input, SW2 is on  PORTF0 */
  20.     GPIO_PORTF_DEN_R = 0x03;            /* set PORTF pins 1-0 as digital pins */
  21.     GPIO_PORTF_PUR_R = 0x01;            /* enable pull up for pin 0 */
  23.     while(1) {
  24.         value = GPIO_PORTF_DATA_R;      /* read data from PORTF */
  25.         value = ~value;                 /* switch is low active; LED is high active */
  26.         value = value << 1;             /* shift it left to display on red LED */
  27.         GPIO_PORTF_DATA_R = value;      /* put it on red LED */
  28.     }
  29. }

GPIO Tasks

Interrupts in TM4C123GH6PM Launchpad

In this article we are going to deal with GPIO PORTF interrupt that will toggle the on board blue led on rising edge whenever a user switch is pressed. Since the GPIO related initialization has already been discussed in our GPIO section. Here we will look at the initialization rituals for GPIO interrupts only.

As we already know that a micro controller has a built in capability to perform several tasks without waiting for completion of ongoing task. Generally there are two methods by which a peripheral device can receive a service from micro controller, they are:

  1. Polling
  2. Interrupts

In polling method a micro controller continuously monitors the status of a device or a particular condition and waits until the required status or condition is met and then it performs the service, after performing this task it executes the next task. For example, just take a look at our section of UART code you can see that we are continuously monitoring the bit 5 of UART0_FR_R register to ensure that any previous transmission has been completed.

    while((UART0_FR_R & 0x00000010) != 0) {
    UART0_DR_R = data;

If yes, then transmit the next incoming data, while in interrupt the same thing is serviced by a microcontroller without continuously monitoring the status of a device though it serves when it gets notified by the device by receiving an interrupt signal.

Interrupt service routine (ISR)

For every interrupt there must be a program associated with it. When an interrupt occurs this program is executed to perform certain service for the interrupt. This program is commonly referred to as an interrupt service routine (ISR) or interrupt handler. When an interrupt occurs, the CPU runs the interrupt service routine. Now the question is, how the ISR gets executed? As shown in the Table 7.1, in the ARM CPU there are pins that are associated with hardware interrupts. They are input signals into the CPU. When the signals are triggered, CPU pushes the PC register onto the stack and loads the PC register with the address of the interrupt service routine. This causes the ISR to get executed.

Interrupt Vector Table

Since there is a program (ISR) associated with every interrupt and this program resides in memory (RAM or ROM), there must be a look-up table to hold the addresses of these ISRs. This look-up table is called interrupt vector table. In the ARM, the lowest 1024 bytes (256 * 4 = 1024) of memory space are set aside for the interrupt vector table and must not be used for any other function. Of the 256 interrupts, some are used for software interrupts and some are for hardware IRQ interrupts.

Nested Vectored Interrupt Controller (NVIC)

It is a control unit for a cortex-M4 MCU, It provides the group of programmable registers where all the exceptions and interrupts, including maskable and non-maskable interrupts are handled and preprocessed in a specific sequences.

Nvic in arm cortex.png
Table 7.1: NVIC in ARM Cortex-M
  • Interrupts on the Cortex-M are controlled by the Nested Vectored Interrupt Controller (NVIC).
  • Each exception has an associated 32-bit vector that points to the memory location where the ISR that handles the exception is located.

With CCS IDE, a new project will get a C startup code tm4c123gh6pm_startup_ccs_gcc.c created by the project wizard. For each interrupt, there is a dummy interrupt hander that does not perform any thing and will never return from the handler. The addresses of these interrupt handlers are listed in the interrupt vector table named g_pfnVectors in the file. You need to carefully find the appropriate vector position and replace IntDefaultHandler with the name of your Interrupt handler. The linker will overwrite the interrupt vector table with the new interrupt handler. The interrupt handler is written with a format of a function in C language.

A detailed default vector table can be found at tm4c123gh6pm_startup_ccs_gcc.c file.

Interrupt and Exception assignments in ARM Cortex-M

The NVIC of the ARM Cortex-M has room for the total of 255 interrupts and exceptions. The interrupt numbers are also referred to as INT type (or INT #) in which the type can be from 1 to 255 or 0x01 to 0xFF. The NVIC in ARM Cortex-M assigns the first 15 interrupts for internal use. The memory locations 0 to 3 are used to store the value to be loaded into the stack pointer when the device is coming out of reset. See Table 7.2

Interrupt # Interrupt Memory Location Priority Level
Stack Pointer Initial Value 0x00000000
Reset 0x00000004 -3 Highest
NMI 0x00000008 -2
Hard Fault 0x0000000C -1
Memory Management Fault 0x00000010 Programmable
Bus Fault 0x00000014 Programmable
Usage Fault (undefined instructions,
divide by zero, unaligned memory
access, ....)
0x00000018 Programmable
Reserved 0x0000001C Programmable
Reserved 0x00000020 Programmable
Reserved 0x00000024 Programmable
Reserved 0x00000028 Programmable
SVCall 0x0000002C Programmable
Debug Moniter 0x00000030 Programmable
Reserved 0x00000034 Programmable
PendSV 0x00000038 Programmable
SysTick 0x0000003C Programmable
IRQ for peripherals 0x00000040 Programmable
IRQ for peripherals 0x00000044 Programmable
IRQ for peripherals 0x000003FC Programmable
Table 7.2: Interrupt Vector Table for ARM Cortex-M.

IRQ Peripheral interrupts

  • An ISR can be launched as a result of an event at the peripheral devices such as timer timeout or analog-to-digital converter (ADC) conversion complete. The largest number of the interrupts in the ARM Cortex-M belong to this category.
  • Notice from Table 7.2 that ARM Cortex-M NVIC has set aside the first 15 interrupts (INT 1 to INT 15) for internal use and exceptions and is not available to chip designer. The Reset, NMI, undefined instructions, and so on are part of this group of exceptions. The rest of the interrupts can be used for peripherals.
  • Many of the INT 16 to INT 255 are used by the chip manufacturer to be assigned to various peripherals such as timers, ADC, Serial COM, external hardware interrupts, and so on. There is no standard in assigning the INT 16 to INT 255 to the peripherals.
  • Each peripheral device has a group of special function registers that must be used to access the device for configuration.
  • For a given peripheral interrupt to take effect, the interrupt for that peripheral must be enabled. The special function registers for that device provide the way to enable the interrupts.

Interrupt Priority for ARM Cortex-M

  • All exceptions and interrupts in the Cortex-M4 system have certain priority levels, either maskable or unmaskable sources.
  • Most maskable interrupts have programmable priority levels, but all Non-Maskable Interrupts (NMIs) have fixed priority levels.
  • When an exception or interrupt occurs, the NVIC performs a comparison between the priority level of current exception or interrupt and the priority level of the new coming exception/interrupt. The current running task will be suspended and the control will be transferred to the service routine of the new coming exception/interrupt if the priority level of the new coming exception/interrupt is higher.
  • In the ARM Cortex-M4 system, the interrupt priority levels are controlled by the Interrupt Priority Registers, as shown in Table 7.3.
  • Each priority register can use 3 bits, 4 bits, or 8 bits to cover all priority levels used in the priority control system.
  • A total of 8 priority levels can be used if 3 bits are used in this register, and 16 priority levels can be obtained if 4 bits are used in this register.
  • Devices within the Tiva family support up to 154 interrupt sources and 8 priority levels, which means that 3 bits are used in the priority register in the TM4C123GH6PM MCU.
  • To activate an interrupt source we need to set its priority and enable that source in the NVIC. This activation is in addition to the arm and enable steps.
    To arm a device means to allow the hardware trigger to interrupt. Conversely, to disarm a device means to shut off or disconnect the hardware trigger from the interrupts
  • Table 7.3 lists some of the interrupt sources available on the TM4C family of micro controllers. Interrupt numbers 0 to 15 contain the faults, software interrupt and SysTick; these interrupts will be handled differently from interrupts 16 and up.
Interrupt #
(Priority Register)
Interrupt Source Priority Register
(Tivaware Name)
0xE000.ED20 PendSV NVIC_SYS_PRI3_R 23 - 21
0xE000.ED20 SysTick NVIC_SYS_PRI3_R 31 - 29
0xE000.E400 GPIO Port A NVIC_PRI0_R 7 - 5
0xE000.E400 GPIO Port B NVIC_PRI0_R 15 - 13
0xE000.E400 GPIO Port C NVIC_PRI0_R 23 - 21
0xE000.E400 GPIO Port D NVIC_PRI0_R 31 - 29
0xE000.E404 GPIO Port E NVIC_PRI1_R 7 - 5
0xE000.E404 UART0, Rx Tx NVIC_PRI1_R 15 - 13
0xE000.E404 UART1, Rx Tx NVIC_PRI1_R 23 - 21
0xE000.E404 SSI0, Rx Tx NVIC_PRI1_R 31 - 29
0xE000.E408 I2C0 NVIC_PRI2_R 7 - 5
0xE000.E408 PWM Fault NVIC_PRI2_R 15 - 13
0xE000.E408 PWM Gen 0 NVIC_PRI2_R 23 - 21
0xE000.E408 PWM Gen 1 NVIC_PRI2_R 31 - 29
0xE000.E40C PWM0 Gen 2 NVIC_PRI3_R 7 - 5
0xE000.E40C Quad Encoder 0 NVIC_PRI3_R 15 - 13
0xE000.E40C ADC Seq 0 NVIC_PRI3_R 23 - 21
0xE000.E40C ADC Seq 1 NVIC_PRI3_R 31 - 29
0xE000.E410 ADC Seq 2 NVIC_PRI4_R 7 - 5
0xE000.E410 ADC Seq 3 NVIC_PRI4_R 15 - 13
0xE000.E400 Watchdog NVIC_PRI4_R 23 - 21
0xE000.E410 Timer 0A NVIC_PRI4_R 31 - 29
0xE000.E414 Timer 0B NVIC_PRI5_R 7 - 5
0xE000.E414 Timer 1A NVIC_PRI5_R 15 - 13
0xE000.E414 Timer 1B NVIC_PRI5_R 23 - 21
0xE000.E414 Timer 2A NVIC_PRI5_R 31 - 29
0xE000.E418 Timer 2B NVIC_PRI6_R 7 - 5
0xE000.E418 Comp 0 NVIC_PRI6_R 15 - 13
0xE000.E418 Comp 1 NVIC_PRI6_R 23 - 21
0xE000.E418 Comp 2 NVIC_PRI6_R 31 - 29
0xE000.E41C System Control NVIC_PRI7_R 7 - 5
0xE000.E41C Flash Control NVIC_PRI7_R 15 - 13
0xE000.E41C GPIO Port F NVIC_PRI7_R 23 - 21
0xE000.E41C GPIO Port G NVIC_PRI7_R 31 - 29
0xE000.E420 GPIO Port H NVIC_PRI8_R 7 - 5
0xE000.E420 UART2, Rx Tx NVIC_PRI8_R 15 - 13
0xE000.E420 SSI1, Rx Tx NVIC_PRI8_R 23 - -21
0xE000.E420 Timer 3A NVIC_PRI8_R 31 - 29
0xE000.E424 Timer 3B NVIC_PRI9_R 7 - 5
0xE000.E424 I2C1 NVIC_PRI9_R 15 - 13
0xE000.E424 Quad Encoder 1 NVIC_PRI9_R 23 - 21
0xE000.E424 CAN0 NVIC_PRI9_R 31 - 29
0xE000.E428 CAN1 NVIC_PRI10_R 7 - 5
0xE000.E428 CAN2 NVIC_PRI10_R 15 - 13
0xE000.E428 Ethernet NVIC_PRI10_R 23 - 21
0xE000.E428 Hibernate NVIC_PRI10_R 31 - 29
0xE000.E42C USB0 NVIC_PRI11_R 7 - 5
0xE000.E42C PWM Gen 3 NVIC_PRI11_R 15 - 13
0xE000.E42C uDMA Soft Tfr NVIC_PRI11_R 23 - 21
0xE000.E42C uDMA Error NVIC_PRI11_R 31 - 29
Table 7.3: The relationship between vectors and NVIC definitions.

The following five conditions must be true for an interrupt to be generated:

  1. Device arm
    Each potential interrupt trigger has a separate arm bit that the software can activate or deactivate. The software will set the arm bits for those devices from which it wishes to accept interrupts, and will deactivate the arm bits within those devices from which interrupts are not to be allowed. In other words it uses the arm bits to individually select which devices will and which devices will not request interrupts.
  2. NVIC enable
    For most devices there is a enable bit in the NVIC that must be set (periodic SysTick interrupts are an exception, having no NVIC enable).
  3. Global enable
    Bit 0 of the special register PRIMASK is the interrupt mask bit, I. If this bit is 1 most interrupts and exceptions are not allowed, which we will define as disabled. If the bit is 0, then interrupts are allowed, which we will define as enabled.
  4. Interrupt priority level must be higher than current level executing
    The BASEPRI register prevents interrupts with lower priority interrupts, but allows higher priority interrupts. For example if the software sets the BASEPRI to 3, then requests with level 0, 1, and 2 can interrupt, while requests at levels 3 and higher will be postponed. The software can also specify the priority level of each interrupt request. If BASEPRI is zero, then the priority feature is disabled and all interrupts are allowed.
  5. Hardware event trigger.
    Hardware triggers are bits in the GPIO_PORTx_RIS_R register that are set on rising or falling edges of digital input pins.

For an interrupt to occur, these five conditions must be simultaneously true but can occur in any order.

GPIO Port Interrupt Programming

Interrupt numbers 16 to 255 are assigned to the peripherals. The INT (IRQ) 46 is assigned to the GPIO Port of F. Although PortF has 8 pins, we have only one interrupt assigned to the entire PortF. In other words, when any of the PortF pins trigger an interrupt, they all go to the same address location in the interrupt vector table. It is the job of our Interrupt Service Routine (ISR or interrupt Handler) to find out which pin caused the interrupt.

Interrupt trigger point

When an input pin is connected to an external device to be used for interrupt, we have 5 choices for trigger point. They are:

  • low-level trigger (active Low level),
  • high-level trigger (active High level),
  • rising-edge trigger (positive-edge going from Low to High),
  • falling-Edge trigger (negative-edge going from High to Low),
  • Both edge (rising and falling) trigger.

Upon Reset, all the interrupts are disabled. To enable any interrupt:

  1. Enable the interrupt for a specific peripheral module.
  2. Enable the interrupts at the NVIC module.
  3. Enable the interrupt globally

GPIO Interrupt Control Registers

GPIO Register Tivaware Name Each Bit Value (Lowest 8-Bit) and Each Pin Function
GPIOIS GPIO_PORTx_IS_R Interrupt sense register
Determines level or edge triggered
0: Detect an edge (edge-sensitive) on the pin,
1: Detect a level (level-sensitive) on the pin.
GPIOIBE GPIO_PORTx_IBE_R 0: Interrupt is controlled by GPIOIEV,
1: Both edges on the corresponding pin trigger an interrupt
GPIOIEV GPIO_PORTx_IEV_R GPIO Interrupt Event Register
Determines the detecting edges or levels.
0: A falling edge or a LOW level,
1: A rising edge or a HIGH level triggers an interrupt
GPIOIM GPIO_PORTx_IM_R GPIO Interrupt Mask Register
Masks (disables) or unmask (enable) an interrupt.
0: Interrupt is masked (disabled),
1: Interrupt is unmasked (enabled).
GPIORIS GPIO_PORTx_IS_R GPIO Raw Interrupt Status Register
Indicates the raw interrupt status for a pin.
0: No interrupt occurred on the pin,
1: An interrupt is occurred on the pin.
For the edge-triggered interrupts,
write a 1 to the pin to clear that interrupt.
For level-triggered interrupt, no action is needed.
GPIOMIS GPIO_PORTx_MIS_R GPIO Masked Interrupt Status Register
Indicates the state of the interrupt.
0: No interrupt occurred or the pin has been masked,
1: An interrupt has been occurred.
GPIOICR GPIO_PORTx_ICR_R GPIO Interrupt Clear Register
Clears an edge-triggered interrupt.
0: No action,
1: The corresponded edge-triggered interrupt is cleared.
Table: The bit values and functions for GPIO interrupt controls.
  • All of these registers are 32-bit, but only lowest 8 bits are used and each bit corresponds to each pin in the selected GPIO Port: bit 0 is for pin 0, bit 1 is for pin 1, and so on. The above table shows the bit values and their functions for these registers.
  • Before any exception or interrupt can be applied to any pin on any GPIO Port, all GPIO pins on selected GPIO Port should be initialized and configured via related GPIO registers.

GPIO Interrupt Control Registers Description

GPIO Interrupt sense register (GPIOIS)

Tm4c gpiois r.png

Bit Bit Name Description
7-0 IS GPIO Interrupt Sense
0: The edge on the corresponding pin is detected (edge-sensitive).
1: The level on the corresponding pin is detected (level-sensitive).

First, we must use GPIO Interrupt Sense (GPIOIS) register to decide the level or edge. Only after using the GPIOIS register we need to indicate which level or edge. To do that, we use the GPIO Interrupt Event (GPIOIEV) to decide low-level, high-level, falling, or rising-edge. The GPIO Interrupt Both Edges (GPIOIBE) register bits overwrite the decision in GPIOIEV. Unless both edge interrupt is desired, the bit in GPIOIBE needs to be cleared.

GPIO Interrupt Event Register (GPIOIEV)

Tm4c gpioiev r.png

(interrupt sense)
(Interrupt Event)
Falling edge
Rising edge
Low level
High level
Table 7.4: Using GPIOIM and GPIOIEV Registers.
  • Since we need a rising edge triggered interrupt at PF4, so writing 1 to the respective bit field will do the same for us.

GPIO Interrupt Both Edge (GPIOIBE)

Tm4c gpioibe r.png

Bit Bit Name Description
7-0 IBE GPIO Interrupt Both Edges
0: Interrupt generation is controlled by the GPIO Interrupt Event (GPIOIEV) register
1: Both edges on the corresponding pin trigger an interrupt

  • Setting the corresponding bit field enables the interrupts for both edges i.e. rising edge and falling edge but our concern is to enable the interrupts for rising edge only. Making the corresponding bit field i.e. PF4 to 0 will allow us to use this register in conjunction with the GPIOIEV register.

GPIO Interrupt Clear Register (GPIOICR)

Tm4c gpioicr r.png

  • The corresponding bit fields in this register clears the interrupt for the respective pin. To ensure that any previous interrupt has been cleared writing 1 to the respective bit field will clear the interrupt on that pin.
  • It is critical that the interrupt handler clears the interrupt flag before returning from interrupt handler. Otherwise the interrupt appears as if it is still pending and the interrupt handler will be executed again and again forever and the program hangs.

GPIO Interrupt Mask Register (GPIOIM)

Tm4c gpioim r.png

  • We need to enable the interrupt capability of a given peripheral at the module level. This should be done after other configurations of that peripheral are done. In the case of I/O ports, each pin can be used as a source of external hardware interrupt. This is done with the GPIO Interrupt Mask (GPIOIM) register.
  • Notice that, the lower 8 bits of this register is used to enable the interrupt capability of each pin of the I/O port. To enable the interrupts for PF0 and PF4 pins, we will need the following:
GPIO_PORTF_IM_R |= 0x11;  /* unmask interrupt */

GPIO Raw Interrupt Status (GPIORIS)

Tm4c gpioris r.png

Bit Bit Name Description
7-0 RIS GPIO Interrupt Raw Status
0: An interrupt condition has not occurred on the corresponding pin.
1: An interrupt condition has occurred on the corresponding pin.

  • For edge-detect interrupts, this bit is cleared by writing a 1 to the corresponding bit in the GPIOICR register.
  • For a GPIO level-detect interrupt, the bit is cleared when the level is deasserted.

GPIO Masked Interrupt Status (GPIOMIS)

Tm4c gpiomis r.png

Bit Bit Name Description
GPIO Masked Interrupt Status
0: An interrupt condition on the corresponding pin is masked or has not occurred.
1: An interrupt condition on the corresponding pin has triggered an interrupt to the interrupt controller.

  • For edge-detect interrupts, this bit is cleared by writing a 1 to the corresponding bit in the GPIOICR register.
  • For a GPIO level-detect interrupt, the bit is cleared when the level is deasserted.

NVIC Interrupt Priority Register

TM4C Interrupt Priority.jpg

Bit/Field Name Description
7:5 INTA Interrupt Priority for Interrupt [4n]
This field holds a priority value, 0-7, for the interrupt with the number [4n], where n is the number of the Interrupt Priority register (n=0 for PRI0, and so on). The lower the value, the greater the priority of the corresponding interrupt.
15:13 INTB Interrupt Priority for Interrupt [4n+1]
This field holds a priority value, 0-7, for the interrupt with the number [4n+1], where n is the number of the Interrupt Priority register (n=0 for PRI0, and so on). The lower the value, the greater the priority of the corresponding interrupt.
23:21 INTC Interrupt Priority for Interrupt [4n+2]
This field holds a priority value, 0-7, for the interrupt with the number [4n+2], where n is the number of the Interrupt Priority register (n=0 for PRI0, and so on). The lower the value, the greater the priority of the corresponding interrupt.
31:29 INTD Interrupt Priority for Interrupt [4n+3]
This field holds a priority value, 0-7, for the interrupt with the number [4n+3], where n is the number of the Interrupt Priority register (n=0 for PRI0, and so on). The lower the value, the greater the priority of the corresponding interrupt.

PRIn Register Bit Field Interrupt Source Priority Register Macros
Bits 31:29 Interrupt[IRQ] = Interrupt[4n+3]
Bits 23:21 Interrupt[IRQ] = Interrupt[4n+2]
Bits 15:13 Interrupt[IRQ] = Interrupt[4n+1]
Bits 7:5 Interrupt[IRQ] = Interrupt[4n]
Table 7.5: Priority register bit fields.
  • Table 7.5 represents the priority register bit fields against the interrupt sources. This priority register bit field is 3-bits wide which means that for an interrupt there can be eight priority levels from 000 to 111 and it can be programmed as per the need.
  • n represents the group number of priority registers, for each group n registers NVIC_PRIn can control up to 4 peripheral ranging from 4n to 4n+3.
    To understand this better just take an example, suppose that for an interrupt handler whose IRQ number is 0, in order to satisfy the equation interrupt[4n]=0 the value of n must be zero which corresponds to the NVIC_PRI0_R also this same register can configure the priority levels for 4n to 4n+3 peripherals.
  • In this case the value of n is 0 therefore the same priority level register can be used to configure the interrupts whose IRQ Number are within 4×0 to 4×0+3, which corresponds to the IRQ number 0,1,2,3.
  • In tm4c123gh6pm_startup_ccs_gcc.c file the IRQ number 0 to 3 represents the interrupt handlers for GPIOA to GPIOD respectively. In a program for each interrupt the priority level can be set in the corresponding bit fields.

Table 7.6 shows some of the priority registers on the NVIC. Each register contains an 8-bit priority field for four devices. On the TM4C micro controllers, only the top three bits of the 8-bit field are used. This allows us to specify the interrupt priority level for each device from 0 to 7, with 0 being the highest priority.

Address 31 - 29 23 - 21 15 - 13 7 - 5 Name
0xE000E400 GPIO Port D GPIO Port C GPIO Port B GPIO Port A NVIC_PRI0_R
0xE000E404 SSI0, Rx Tx UART1, Rx Tx UART0, Rx Tx GPIO Port E NVIC_PRI1_R
0xE000E408 PWM Gen 1 PWM Gen 0 PWM Fault I2C0 NVIC_PRI2_R
0xE000E40C ADC Seq 1 ADC Seq 0 Quad Encoder 0 PWM0 Gen 2 NVIC_PRI3_R
0xE000E410 Timer 0A Watchdog ADC Seq 3 ADC Seq 2 NVIC_PRI4_R
0xE000E414 Timer 2A Timer 1B Timer 1A Timer 0B NVIC_PRI5_R
0xE000E418 Comp 2 Comp 1 Comp 0 Timer 2B NVIC_PRI6_R
0xE000E41C GPIO Port G GPIO Port F Flash Control System Control NVIC_PRI7_R
0xE000E420 Timer 3A SSI1, Rx Tx UART2, Rx Tx GPIO Port H NVIC_PRI8_R
0xE000E424 CAN0 Quad Encoder 1 I2C1 Timer 3B NVIC_PRI9_R
0xE000E428 Hibernate Ethernet CAN2 CAN1 NVIC_PRI10_R
0xE000E42C uDMA Error uDMA Soft Tfr PWM Gen 3 USB0 NVIC_PRI11_R
0xE000ED20 SysTick PendSV --- Debug NVIC_SYS_PRI3_R
Table 7.6: Most popular priority registers used in the TM4C123GH6PM NVIC.
  • The interrupt number is loaded into the IPSR register. The servicing of interrupts does not set the I bit in the PRIMASK, so a higher priority interrupt can suspend the execution of a lower priority ISR.
  • If a request of equal or lower priority is generated while an ISR is being executed, that request is postponed until the ISR is completed. In particular, those devices that need prompt service should be given high priority.

Enabling and Disabling an Interrupt

Upon Reset, all the interrupts are disabled. To enable any interrupt we should:

  1. Enable the interrupt for a specific peripheral module.
    This is done with the GPIO Interrupt Mask (GPIOIM) register.
  2. Enable the interrupts at the NVIC module.
  3. Enable the interrupt globally .

Interrupt enabling with all 3 levels

Tm4c123 int enable 3level.png

Figure:Interrupt enabling with all 3 levels

Tm4c gpiom.png

Figure:GPIO Interrupt Mask (GPIOIM)

Interrupt Set Enable Register

It is the interrupt set enable register ranging from NVIC_EN0_R ... NVIC_EN3_R, each ISER register is 32 bit wide that can be used to set the interrupt against the respective IRQ numbers. Each ISER register can be used to enable 32 different types of interrupt sources. For example NVIC_EN0_R is used to enable the interrupt sources whose IRQ numbers are 0-31, similarly the interrupt sources whose IRQ numbers are 32-63 can be enabled by NVIC_EN1_R and so on. The corresponding bit field is required to be set according to the IRQ number of the interrupt sources since each register is 32 bit wide. If the interrupt source whose IRQ number is greater than 31 the corresponding bit number can be calculated by this formula:

bit number = IRQ number - 32(n-1) 

where n=1 for interrupt sources having IRQ number 0-31. Similarly n=2 for interrupt sources having IRQ number 32-63 and so on. For enabling interrupt at PF4 pin writing 1 to the bit field 30 of NVIC_EN0_R will do the same for us, as its IRQ number is 30.

To disable interrupts there are another registers: NVIC_DIS0_R to NVIC_DIS3_R (Interrupt Clear Enable). Writing zeros to the NVIC_EN0_R through NVIC_EN3_R registers has no effect. To disable interrupts we write ones to the corresponding bit in the NVIC_DIS0_R through NVIC_DIS3_R register.

Enable Register 32-Enable Bits Address
NVIC_EN1_R PORTH UART2 SSI1 Timer3A Timer3B I2C1 ... UART6 UART7 0xE000E104
NVIC_EN2_R ... ... ... ... I2C2 I2C3 ... WTimer0A WTimer0B 0xE000E108
Table 7.7: Relationship between each bit on interrupt enable register and related peripheral.

Global interrupt enable/disable

Global interrupt enable/disable allows us with a single instruction to mask all interrupts during the execution of some critical task such as manipulating a common pointer shared by multiple threads. In ARM Cortex M, we do the global enable/disable of interrupts with assembly language instructions of CPSID I (Change processor state-disable interrupts) and CPSIE I (Change processor state-enable interrupts). In C language we use inline assembly language instructions:

void DisableInterrupts(void){__asm ("CPSID  I\n");}
void EnableInterrupts(void){__asm  ("CPSIE  I\n");}

Initialize and Configure GPIO Interrupt Control Registers

To initialize and configure GPIO interrupt controls, one needs to program the GPIOIS, GPIOIBE, GPIOEV, and GPIOIM registers to configure the type, event, and mask of the interrupts for each port. The following steps should be used to do this initialization and configuration process:

  • Configure the GPIOIM register to disable (mask) the undesired pins.
  • Configure the GPIOIS register to indicate the interrupt-triggering type, edge, or level.
  • Configure the GPIOIBE register to indicate if this interrupt is triggered by both edges.
  • Reset the GPIORIS register to 0 to make it ready to set a flag if any interrupt occurred.
  • Configure the GPIOIM register to enable (unmask) the desired pins.

How to configure and set up an interrupt and its handler

Now let’s provide an example to illustrate how to configure and set up an interrupt and its handler based on the above Tables. For example, we want to set up and configure an interrupt for GPIO PortF. Perform the following operations to complete this job:

  1. Find the IRQ number for this interrupt from Table 7.3. The IRQ number for the GPIO PortF is 30.
  2. Based on Table 7.6, obtain the group number of the priority register (n). The IRQ number is 30. In order to make the equation 30 = interrupt[4n + 2] hold, n must be 7. This means that we need to use group 7 priority register NVIC_PRI7_R with bits 23-21 to configure the priority level for the GPIO PortF. Since the IRQ number of this port is 30, we can set the priority level for this port as 5, (101B), in bits 23-21 on the NVIC_PRI7_R. This setup can be written as NVIC_PRI7_R = (NVIC_PRI7_R & 0xFF00FFFF) | 0x00A00000 in the user’s program.
  3. Based on Table 7.7, obtain the bit field in the NVIC Set Enable Register to enable this port. Since the IRQ number of the Port F is 30, the bit 30 in the NVIC_EN0_R register should be set to 1 to enable this port. This setup can be written as NVIC_EN0_R = 0x40000000.
  4. Open tm4c123gh6pm_startup_ccs_gcc.c for editing. This file contains the vector table among other things. Open the file and look for the GPIO PortF vector. You need to carefully find the appropriate vector position and replace IntDefaultHandler with the name of your Interrupt handler.
  5. After going through all these steps an EnableInterrupts() function is called to perform the global interrupt enable function. Note that we have to enable the pull up at PF4 in order to provide high or low input signal.
  6. It is critical that the interrupt handler clears the interrupt flag before returning from interrupt handler. Otherwise the interrupt appears as if it is still pending and the interrupt handler will be executed again and again forever and the program hangs. The PORTF interrupt flag is cleared by writing a 1 to the bit that corresponds to the pin that triggered the interrupt in the GPIO Interrupt Clear Register (GPIOICR). In the case of SW1 (PF4), the interrupt flag is cleared by:

Note:The ARM Cortex writes are buffered. That means a write does not take effect immediately. It may take many clock cycles before the write to ICR to clear the interrupt flags. When return from interrupt handler and the interrupt flag is still pending, the interrupt handler will be executed again. One way to ensure that interrupt flags are cleared before returning from interrupt handler is to perform a read to force the write to take effect.

  1. /* sw1_int.c
  2.  *
  3.  * Runs on EK-TM4C123GXL LaunchPad
  4.  *
  5.  * GPIO PORTF interrupt that will toggle the on board Blue LED
  6.  * on rising edge whenever a user switch (SW1) is pressed.
  7.  *
  8.  */
  9. #include <stdint.h>
  10. #include "inc/tm4c123gh6pm.h"
  12. void DisableInterrupts(void);
  13. void EnableInterrupts(void);
  14. void WaitForInterrupt(void);
  15. void GPIOPortF_Init(void);
  16. void GPIOPortF_Handler(void);
  18. void GPIOPortF_Init(void)
  19. {
  21.     SYSCTL_RCGC2_R |= 0x00000020;   /* 1) activate clock for Port F */
  23.     GPIO_PORTF_LOCK_R = 0x4C4F434B; /* 2) unlock GPIO Port F */
  24.     GPIO_PORTF_CR_R = 0x1F;         /* allow changes to PF4-0 */
  25.     GPIO_PORTF_AMSEL_R = 0x00;      /* 3) disable analog on PF */
  26.     GPIO_PORTF_PCTL_R = 0x00000000; /* 4) PCTL GPIO on PF4-0 */
  27.     GPIO_PORTF_DIR_R = 0x0E;        /* 5) PF4,PF0 in, PF3-1 out */
  28.     GPIO_PORTF_AFSEL_R = 0x00;      /* 6) disable alt funct on PF7-0 */
  29.     GPIO_PORTF_PUR_R = 0x11;        /* enable pull-up on PF0 and PF4 */
  30.     GPIO_PORTF_DEN_R = 0x1F;        /* 7) enable digital I/O on PF4-0 */
  32.     GPIO_PORTF_IS_R &= ~0x10;       /*  PF4 is edge-sensitive */
  33.     GPIO_PORTF_IBE_R &= ~0x10;      /*  PF4 is not both edges */
  34.     GPIO_PORTF_IEV_R &= ~0x10;      /*  PF4 falling edge event */
  35.     GPIO_PORTF_ICR_R = 0x10;        /*  Clear flag4 */
  36.     GPIO_PORTF_IM_R |= 0x10;        /*  arm interrupt on PF4 */
  37.     NVIC_PRI7_R = (NVIC_PRI7_R & 0xFF1FFFFF) | 0x00A00000; /*  priority 5 */
  38.     NVIC_EN0_R = 0x40000000;        /*  Enable interrupt 30 in NVIC */
  40.     EnableInterrupts();             /* Enable global Interrupt flag (I) */
  41. }
  43. void GPIOPortF_Handler(void)
  44. {
  45.     volatile int readback;
  47.     GPIO_PORTF_ICR_R = 0x10;        /* clear PF4 int */
  48.     GPIO_PORTF_DATA_R ^= (1<<2);    /* toggle Blue LED */
  49.     readback = GPIO_PORTF_ICR_R;    /* a read to force clearing of interrupt flag */
  50.     readback = readback;
  51. }
  53. int main(void)
  54. {
  55.     GPIOPortF_Init();               /* initialize GPIO Port F interrupt */
  57.     while(1) {
  58.         WaitForInterrupt();
  59.     }
  60. }
  62. /*********** DisableInterrupts ***************
  63. *
  64. * disable interrupts
  65. *
  66. * inputs:  none
  67. * outputs: none
  68. */
  70. void DisableInterrupts(void)
  71. {
  72.     __asm ("    CPSID  I\n");
  73. }
  75. /*********** EnableInterrupts ***************
  76. *
  77. * emable interrupts
  78. *
  79. * inputs:  none
  80. * outputs: none
  81. */
  83. void EnableInterrupts(void)
  84. {
  85.     __asm  ("    CPSIE  I\n");
  86. }
  88. /*********** WaitForInterrupt ************************
  89. *
  90. * go to low power mode while waiting for the next interrupt
  91. *
  92. * inputs:  none
  93. * outputs: none
  94. */
  96. void WaitForInterrupt(void)
  97. {
  98.     __asm  ("    WFI\n");
  99. }

1. Sample program (SW1 Interrupt)

  • Create a CCS Project with "Empty Project"
  • Download sw1_int.c program and Add this file to your project.
  • Open tm4c123gh6pm_startup_ccs_gcc.c for editing and replace IntDefaultHandler with the GPIOPortF_Handler in the GPIO PortF vector position.
  • Build and Debug (Run) the program.
    Run this code and load it into the flash of microcontroller. You will see that the blue LED gets turned on when user switch is pressed first then if it is pressed again it gets turned off.

2. GPIOF interrupt from SW1 and SW2

  • Rewrite sw1_int.c program to distinguish the interrupt pin. Toggle Red LED on PF1 continuously. Upon pressing either SW1 or SW2, the Green or Blue LED will toggle three times. main program toggles Red LED while waiting for interrupt from SW1 or SW2.

3. Interrupt from External Source

  • Write a program to toggle an LED at the same rate as the input frequency by connecting a square wave of 3.3V to a pin of Portx. (You can make it to toggle on positive or negative edge)
    Hint: Configure PORTD6 to trigger interrupt by falling edge. In the interrupt handler, the green LED (PF3) is toggled. The green LED should have half the frequency of the input signal at PORTD6. IRQ3 is assigned to PORTD6.

See Also

  1. EK-TM4C123GXL LaunchPad Board
  2. GPIO Programming

Using Systick Timer with TM4C123GH6PM Launchpad

So far we have learned that how to program general purpose input output(GPIO) of ARM Cortex M-4. But we have not generated the delay in micro controller. In this section we will generate delay using different methods and blink on board LEDs. There are two types of delays that can be generated by our program:

  1. Software delay
  2. Hardware delay

Software delay

This type of delay is nothing but making the controller busy, doing the task which will occupy a certain duration. For example if we want our friend to wait for us then instead of telling him to wait we can simply engage him in other task. But the generated time may or may not be so accurate.

Example: In C code if we can use this for loop for generating a delay of 1 second.

  1.  void delay_1sec(void)
  2.  {
  3.     for( unsigned long i = 0; i <= 3000000; i++ )
  4.         ;
  5.  }

This method doesn’t generate the accurate time delay, but approximate. For generation of accurate delay in microsecond we prefer hardware delay.

Hardware delay

The hardware delay is most precise method to generate delay. In this method we use timer which is the most popular and useful feature of microcontrollers. Generally delay with timer is like waiting a bucket to be empty if we make a small hole to leak its filled liquid more the liquid more time it will take to empty the bucket. In this article, we are going to use a special timer called systick timer. In our micro controller we have six 64 bit and six 32 bit timer excluding systick timer. This timer is very useful and plays very important role in RTOS design. We will see other timers later. In our micro controller, Systick timer is 24 bit wide. For the generation of delay, at first we need to make sure that our controller is working on the clock frequency as we are expecting. This step is required to select the clock source only at external crystal without PLL.

SysTick Timer

SysTick is a simple counter that we can use to create time delays and generate periodic interrupts. It exists on all Cortex-M microcontrollers, so using SysTick means the system will be easy to port to other microcontrollers.

SysTick Timer Internal Structure

As we learned earlier that modifying registers are the key for configuration and desired functionality. Hence systick timer can be used by modifying these registers.

  1. Systick Reload value (STRELOAD)
  2. Systick Control and Status Register (STCTRL)
  3. Systick Current Value (STCURRENT)

Systick Reload Value Register

This register acts like a bucket filled with a liquid that will be empty after some time duration and we will get acknowledgement by a change in flag bit in STCTRL Register. This register is 24 bit wide.
Tm4c systick streload r.png

Since this register is 24 bit wide it can store upto 0xFFFFFF or 16777215. when we load any value into this register and trigger the timer it starts to count down and when the value reaches to 0 it sets the COUNT FLAG. We will see how to reload and check flag bit in the next few steps.

Systick control and Status register

This register is shown in the following figure
Tm4c systick stctl r.png
Bit Name Description
0 Enable Enable
0: the counter is disabled
1: enables SysTick to begin counting Down
1 INTEN Interrupt Enable
0: Interrupt generation is disabled
1: when SysTick counts to 0 an interrupt is generated
2 CLK_SRC Clock Source
0: Precision internal oscillator (PIOSC) divided by 4
1: System clock
16 COUNT Count Flag
0: the SysTick has not counted Down to zero since the last time this bit was read
1: the SysTick has counted Down to Zero
Note: this flag is cleared by reading the STRCTL or writing to STCURRENT Register

We need to set our main clock to system clock by setting CLK_SRC to 1. And we don’t want interrupt method so INTEN bit will be 0. Now we are going to trigger our timer by writing ENABLE bit 1 so writing 0x5 will trigger our timer at the same time it will also set the configuration. Now the loaded value will be started to count down. When the timer gets rollover the bit 16 will set to one. hence we need to monitor 16th bit.

If INTEN=1, when the COUNT flag is set, it causes an interrupt via the NVIC. INTEN is D1 of the STCTRL register.

Systick Register Summary

Address 31-24 23-17 16 15-13 2 1 0 Name
$E000E014 o 24-bit RELOAD value NVIC_ST_RELOAD_R
$E000E018 o 24-bit CURRENT value of SysTick counter NVIC_ST_CURRENT_R

Delay time calculation

Since we are working with external clock i.e. 16 MHz, each pulse generated by the clock source will have:

1/XTAL frequency = 1/(16*10^6) = 62.5ns time period. 

So if we load 253 into the RELOAD register and trigger the counter it will count down with next pulse and will take 62.5ns to change its value from 253 to 252. Hence, In order to generate the delay, we can calculate the approximate value that has to be loaded into this register by the formula-

Reload Value = XTAL*Time delay

one extra clock delay is already included to set the flag for rollover, hence we get one extra clock delay. By subtracting by one will give exact time delay.

Reload Value = (XTAL*Time Delay)-1

Remember that in one shoot, it can only take 0xFFFFFF maximum value. Therefore in one shoot, we can only generate maximum of Time delay

TimeDelay = (ReloadValue+1)/XTAL=16777215+(1/(16*10^6))
TimeDelay = 1.048575937 sec.

Example: For generating 1 sec of delay the value that has to be loaded into the RELOAD Register

Reload Value = (XTAL*Time delay)-1
Reload Value = (16*10^6*1)-1
Reload Value = 15999999

SysTick timer Initialization

There are four steps to initialize the SysTick timer.

  1. Clear the ENABLE (NVIC_ST_CTRL_R) bit to turn off SysTick during initialization.
  2. Set the RELOAD (NVIC_ST_RELOAD_R) register.
  3. Write to the NVIC_ST_CURRENT_R value to clear the counter.
  4. Write the desired mode to the control register, NVIC_ST_CTRL_R.
    • Set the CLK_SRC bit specifying the core clock will be used.
    • We must set CLK_SRC=1 bit specifying the core clock will be used, because CLK_SRC=0 external clock mode is not implemented on the LM3S/TM4C family.
    • Set INTEN (NVIC_ST_CTRL_R) to enable interrupts, but in this first example we clear INTEN so interrupts will not be requested.
    • Set the ENABLE (NVIC_ST_CTRL_R) bit so the counter will run.

When the CURRENT (NVIC_ST_CURRENT_R) value counts down from 1 to 0, the COUNT flag is set. On the next clock, the CURRENT is loaded with the RELOAD value. In this way, the SysTick counter (CURRENT) is continuously decrementing. If the RELOAD value is n, then the SysTick counter operates at modulo n+1 (...n, n-1, n-2 ... 1, 0, n, n-1, ...). In other words, it rolls over every n+1 counts.

The COUNT flag could be configured to trigger an interrupt. However, in this first example interrupts will not be generated.

Source Code

Delay in 62.5ns units

  1. /* delay is in 62.5ns units */
  2. void SysTick_Wait(uint32_t delay)
  3. { 
  4.     NVIC_ST_CTRL_R = 0;            /* (1) disable SysTick during setup */
  5.     NVIC_ST_RELOAD_R = delay-1;    /* (2) number of counts to wait */
  6.     NVIC_ST_CURRENT_R = 0;         /* (3) any value written to CURRENT clears */
  7.     NVIC_ST_CTRL_R |= 0x5;         /* (4) enable SysTick with core clock */
  9.     while((NVIC_ST_CTRL_R&0x00010000)==0) {
  10.         ;                          /* wait for COUNT flag */
  11.     }
  12. }

One Second Delay

  1. void One_Second_Delay(void)
  2. {
  3.     NVIC_ST_CTRL_R = 0;            /* disable SysTick during setup */
  4.     NVIC_ST_RELOAD_R = 15999999;    /* Reload Value goes here */
  5.     NVIC_ST_CTRL_R |= 0x5;          /* enable SysTick with core clock */
  6.     while( (NVIC_ST_CTRL_R & (1<<16) ) == 0)
  7.         ;                           /* Monitoring bit 16 to be set */
  8.     NVIC_ST_CTRL_R = 0;             /* Disabling SysTick Timer */
  9. }

See Also

Using SysTick with Interrupt

In the previous article, we used SysTick in a blocking mode. In this article, we will use SysTick with interrupt.

The SysTick interrupt can be used to initiate an action on a periodic basis. This action is performed internally at a fixed rate without external signal. For example, in a given application we can use SysTick to read a sensor every 200 msec. SysTick is used widely for an operating system so that the system software may interrupt the application software periodically (often 10 ms interval) to monitor and control the system operations.

If INTEN=1, when the COUNT flag is set, it causes an interrupt via the NVIC. INTEN is D1 of the STCTRL register. Examining interrupt vector table for ARM Cortex, we see the SysTick is the interrupt #15 assigned to the system itself.

Source Code

  1. /* systick_int.c
  2.  *
  3.  * Toggle the red LED using the SysTick interrupt
  4.  *
  5.  * This program sets up the SysTick to interrupt at 1 Hz.
  6.  * The system clock is running at 16 MHz.
  7.  * 1sec/62.5ns = 16,000,000 for RELOAD register.
  8.  * In the interrupt handler, the red LED is toggled.
  9. */
  11. #include <stdint.h>
  12. #include "inc/tm4c123gh6pm.h"
  14. void enable_irq(void);
  16. int main (void)
  17. {
  18.     /* enable clock to GPIOF at clock gating control register */
  19.     SYSCTL_RCGC2_R |= 0x20;
  20.     /* enable the GPIO pins for the LED (PF3, 2, 1) as output */
  21.     GPIO_PORTF_DIR_R = 0x0E;
  22.     /* enable the GPIO pins for digital function */
  23.     GPIO_PORTF_DEN_R = 0x0E;
  25.     /* Configure SysTick */
  26.     NVIC_ST_RELOAD_R = 16000000-1;  /* reload with number of clocks per second */
  27.     NVIC_ST_CTRL_R = 7;             /* enable SysTick interrupt, use system clock */
  29.     enable_irq();                   /* global enable interrupt */
  31.     while(1) {
  32.         ;
  33.     }
  34. }
  36. void SysTick_Handler(void)
  37. {
  38.     GPIO_PORTF_DATA_R ^= 2;         /* toggle the red LED */
  39. }
  41. /* global enable interrupts */
  42. void inline enable_irq(void)
  43. {
  44.     __asm  ("    CPSIE  I\n");
  45. }

The above program uses the SysTick to toggle the red LED of PF1 every second. We need the RELOAD value of 16,000,000-1 since 1sec / 62.5nsec = 16,000,000. We assume the CPU clock is 16MHz (1/16MHz=62.5ns). The COUNT flag is raised every 16,000,000 clocks and an interrupt occurs. Then the RELOAD register is loaded with 16,000,000-1 automatically.

Notice the enabling of interrupt in SysTick Control register. Since SysTick is an internal interrupt, the COUNT flag is cleared automatically when the interrupt occurs. Also notice the SysTick is part of INT1-INT15 and not part of IRQ. For this reason we must use the CTLR register to enable it.

Note: Open tm4c123gh6pm_startup_ccs_gcc.c for editing. This file contains the vector table among other things. Open the file and look for the SysTick vector. You need to carefully find the appropriate vector position and replace IntDefaultHandler with the name of your Interrupt handler (SysTick_Handler).

See Also

  1. We used Software delay in our blinky, read_sw1, and read_sw2 programs. Replace Software Delay with SysTick Timer Delay.
  2. If we are sure the execution speed of our function is less than (224 bus cycles), we can use this timer to collect timing information with only a modest amount of intrusiveness. Use the SysTick timer to measure the Software Delay functions we used in the earlier programs.
  3. If we activate the PLL and change the bus clock to 50 MHz (20ns), what is the longest elapsed time we could measure?

This is the UART panel of the basic tab example. This is the third panel of the basic tab example.

This is the GPTM panel of the basic tab example. This is the fourth panel of the basic tab example.

This is the PWM panel of the basic tab example. This is the third panel of the basic tab example.

This is the ADC panel of the basic tab example. This is the fourth panel of the basic tab example.