Lab Practicals Using TivaC LaunchPad Board
From EdWiki
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 Micro controller
ARM chip used in TI Tiva LaunchPad is Tiva C series TM4C123GH6PM microcontroller.
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.
GPIO APB & AHB Bus
The ARM chips have two buses:
- Advanced Peripheral Bus (APB) and
- 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.
GPIO Register Map
Name | Offset | Tivaware Name | Description |
---|---|---|---|
GPIODATA | 0x000 | GPIO_PORTn_DATA_R | GPIO Data |
GPIODIR | 0x400 | GPIO_PORTn_DIR_R | GPIO Direction |
GPIOIS | 0x404 | GPIO_PORTn_IS_R | GPIO Interrupt Sense |
GPIOIBE | 0x408 | GPIO_PORTn_IBE_R | GPIO Interrupt Both Edges |
GPIOIEV | 0x40C | GPIO_PORTn_IEV_R | GPIO Interrupt Event |
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 |
GPIOICR | 0x41C | GPIO_PORTn_ICR_R | GPIO Interrupt Clear |
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 |
GPIOODR | 0x50C | GPIO_PORTn_ODR_R | GPIO Open Drain Select |
GPIOPUR | 0x510 | GPIO_PORTn_PUR_R | GPIO Pull-Up Select |
GPIOPDR | 0x514 | GPIO_PORTn_PDR_R | GPIO Pull-Down Select |
GPIOSLR | 0x518 | GPIO_PORTn_SLR_R | GPIO Slew Rate Control Select |
GPIODEN | 0x51C | GPIO_PORTn_DEN_R | GPIO Digital Enable |
GPIOLOCK | 0x520 | GPIO_PORTn_LOCK_R | GPIO Lock |
GPIOCR | 0x524 | GPIO_PORTn_CR_R | GPIO Commit |
GPIOAMSEL | 0x528 | GPIO_PORTn_AMSEL_R | GPIO Analog Mode Select |
GPIOPCTL | 0x52C | GPIO_PORTn_PCTL_R | GPIO Port Control |
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:
- Activate the clock for the port in the Run Mode Clock Gating Control Register 2 (RCGC2).
- Unlock the port (LOCK = 0x4C4F434B). This step is only needed for pins PC0-3, PD7 and PF0 on TM4C123GXL LaunchPad.
- 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.
- 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.
- Set its direction register (DIR). A DIR bit of 0 means input, and 1 means output.
- Clear bits in the alternate Function Select register (AFSEL).
- 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
GPIO Initialization and Configuration
The GPIO modules may be accessed via two different memory apertures. The legacy aperture, the Advanced Peripheral Bus (APB), is backwards-compatible with previous devices. The other aperture, the Advanced High-Performance Bus (AHB), offers the same register map but provides better back-to-back access performance than the APB bus. These apertures are mutually exclusive. The aperture enabled for a given GPIO port is controlled by the appropriate bit in the GPIOHBCTL register.
To configure the GPIO pins of a particular port, follow these steps:
- Set up the system clock
- Set up the system clock for the specific GPIO Port to enable the clock to drive the port by setting the appropriate bits in the GPIO Run Mode Clock Gating Control (RCGCG2) Register. More ...
- Unlock the port
- After enabling the clock some ports are still locked. This step is only needed for pins PC0-3, PD7 and PF0 on TM4C123GXL LaunchPad. More ...
- Disable Analog function
- Disable the analog function of the pin in the Analog Mode Select register (AMSEL) More ...
- Configure each bit as a GPIO Mode or Alternate Mode
- Optionally you can configure the GPIOAFSEL register to program each bit as a GPIO Mode or Alternate Mode. More ...
- Set up the direction for each pin on the GPIO
- Set up the direction for each pin on the GPIO port by programming the GPIODIR register. More ...
- Enable GPIO pins as digital I/Os
- To enable GPIO pins as digital I/Os, set the appropriate DEN bit in the GPIODEN register. More ...
- Setup the drive strength for each pin
- Optionally, you can setup the drive strength for each pin through the GPIODR2R, GPIODR4R, or GPIODR8R registers. More ...
- Configure each pad in the port to have pull-up, pull-down, or open drain function
- Optionally, you can configure each pad in the port to have pull-up, pull-down, or open drain function through the GPIOPUR, GPIOPDR , or GPIOODR register. More ...
- Set up the type, event, and mask of the interrupts for each port
- Optionally, you can configure the GPIOIS, GPIOIBE, GPIOEV, and GPIOIM registers to set up the type, event, and mask of the interrupts for each port if interrupts are used for the port. More ...
- Lock the configurations of the NMI and JTAG/SWD pins on the GPIO port
- Optionally, you can lock the configurations of the NMI and JTAG/SWD pins on the GPIO port pins by setting the LOCK bits in the GPIOLOCK register. More ...
- Read/Write Data Register
- The GPIODATA register is the data register. More ...
EK-TM4C123GXL Blinky Program
/*
Toggling LEDs using special function registers by their names defined in the TivaWare header file
Runs on TI EK-TM4C123GXl LaunchPad Board
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
void delayMs(int n);
int main(void)
{
SYSCTL_RCGC2_R |= 0x00000020; /* enable clock to GPIOF at clock gating control register */
GPIO_PORTF_DIR_R = 0x0E; /* enable the GPIO pins for the LED (PF3, 2, 1) as output */
GPIO_PORTF_DEN_R = 0x0E; /* enable the GPIO pins for digital function */
while(1) {
GPIO_PORTF_DATA_R = 0x0E; /* turn on all LEDs */
delayMs(500);
GPIO_PORTF_DATA_R = 0; /* turn off all LEDs */
delayMs(500);
}
}
/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(int n)
{
int i, j;
for(i = 0 ; i < n; i++)
for(j = 0; j < 3180; j++) {} /* do nothing for 1 ms */
}
See Also
Switch Inputs and LED Outputs
There are four ways to interface a switch to the microcontroller as shown in the following Figure:
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 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.
- Enable the clock to PortF
- Set the Direction register PF4 as input, and PF3 as output
- Enable the digital I/O feature of PortF
- Enable the pull up resistor option in PUR register since the switch circuit does not have pull-up resistor
- Read SW1 on PortF
- Invert the value since the switch is active low and the LED is active high
- Shift right the switch bit (PF4) to green LED bit(PF3) of the value
- Write the value to green LED of PortF
- Repeat steps 5 to 8
Source Code
/* Read a switch and write it to the LED */
/* This program reads SW1 of Tiva LaunchPad and writes the inverse of the value to the green
LED. SW1 is low when pressed (Normally High). LED is ON when high.
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
int main(void)
{
unsigned int value;
SYSCTL_RCGC2_R |= 0x00000020;; /* enable clock to GPIOF */
GPIO_PORTF_DIR_R = 0x08; /* set PORTF3 pin as output (LED) pin */
/* and PORTF4 as input, SW1 is on PORTF4 */
GPIO_PORTF_DEN_R = 0x18; /* set PORTF pins 4-3 as digital pins */
GPIO_PORTF_PUR_R = 0x10; /* enable pull up for pin 4 */
While(1) {
value = GPIO_PORTF_DATA_R; /* read data from PORTF */
value = ~value; /* switch is low active; LED is high active */
value = value >> 1; /* shift it right to display on green LED */
GPIO_PORTF_DATA_R = value; /* put it on the green LED */
}
}
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
/* Read a switch and write it to the LED */
/* This program reads SW2 of Tiva LaunchPad and write the inverse of the value to the red LED.
SW2 is low when pressed. LED is on when high. */
/* SW2 is connected to PORTF0, which is an NMI pin. */
/* In order to use this pin for any function other than NMI, the pin needs be unlocked first. */
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
int main(void)
{
unsigned int value;
SYSCTL_RCGC2_R |= 0x00000020; /* enable clock to GPIOF */
GPIO_PORTF_LOCK_R = 0x4C4F434B; /* unlock commit register */
GPIO_PORTF_CR_R = 0x01; /* make PORTF0 configurable */
GPIO_PORTF_DIR_R = 0x02; /* set PORTF1 pin as output (LED) pin */
/* and PORTF0 as input, SW2 is on PORTF0 */
GPIO_PORTF_DEN_R = 0x03; /* set PORTF pins 1-0 as digital pins */
GPIO_PORTF_PUR_R = 0x01; /* enable pull up for pin 0 */
while(1) {
value = GPIO_PORTF_DATA_R; /* read data from PORTF */
value = ~value; /* switch is low active; LED is high active */
value = value << 1; /* shift it left to display on red LED */
GPIO_PORTF_DATA_R = value; /* put it on red LED */
}
}
GPIO Tasks
- Write a program to light Blue LED when SW1 is pressed, light Green LED when SW2 is pressed, light white LED when both the switches are pressed and light RED LED when none is pressed .
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:
- Polling
- 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.
- 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 |
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.
Vector address |
Vector (Exception) Number |
Interrupt # (IRQ) |
Address (Priority Register) |
Interrupt Source | Priority Register (Tivaware Name) |
Priority Bits |
---|---|---|---|---|---|---|
0x00000038 | 0xE000.ED20 | PendSV | NVIC_SYS_PRI3_R | 23 - 21 | ||
0x0000003C | 0xE000.ED20 | SysTick | NVIC_SYS_PRI3_R | 31 - 29 | ||
0x00000040 | 0xE000.E400 | GPIO Port A | NVIC_PRI0_R | 7 - 5 | ||
0x00000044 | 0xE000.E400 | GPIO Port B | NVIC_PRI0_R | 15 - 13 | ||
0x00000048 | 0xE000.E400 | GPIO Port C | NVIC_PRI0_R | 23 - 21 | ||
0x0000004C | 0xE000.E400 | GPIO Port D | NVIC_PRI0_R | 31 - 29 | ||
0x00000050 | 0xE000.E404 | GPIO Port E | NVIC_PRI1_R | 7 - 5 | ||
0x00000054 | 0xE000.E404 | UART0, Rx Tx | NVIC_PRI1_R | 15 - 13 | ||
0x00000058 | 0xE000.E404 | UART1, Rx Tx | NVIC_PRI1_R | 23 - 21 | ||
0x0000005C | 0xE000.E404 | SSI0, Rx Tx | NVIC_PRI1_R | 31 - 29 | ||
0x00000060 | 0xE000.E408 | I2C0 | NVIC_PRI2_R | 7 - 5 | ||
0x00000064 | 0xE000.E408 | PWM Fault | NVIC_PRI2_R | 15 - 13 | ||
0x00000068 | 0xE000.E408 | PWM Gen 0 | NVIC_PRI2_R | 23 - 21 | ||
0x0000006C | 0xE000.E408 | PWM Gen 1 | NVIC_PRI2_R | 31 - 29 | ||
0x00000070 | 0xE000.E40C | PWM0 Gen 2 | NVIC_PRI3_R | 7 - 5 | ||
0x00000074 | 0xE000.E40C | Quad Encoder 0 | NVIC_PRI3_R | 15 - 13 | ||
0x00000078 | 0xE000.E40C | ADC Seq 0 | NVIC_PRI3_R | 23 - 21 | ||
0x0000007C | 0xE000.E40C | ADC Seq 1 | NVIC_PRI3_R | 31 - 29 | ||
0x00000080 | 0xE000.E410 | ADC Seq 2 | NVIC_PRI4_R | 7 - 5 | ||
0x00000084 | 0xE000.E410 | ADC Seq 3 | NVIC_PRI4_R | 15 - 13 | ||
0x00000088 | 0xE000.E400 | Watchdog | NVIC_PRI4_R | 23 - 21 | ||
0x0000008C | 0xE000.E410 | Timer 0A | NVIC_PRI4_R | 31 - 29 | ||
0x00000090 | 0xE000.E414 | Timer 0B | NVIC_PRI5_R | 7 - 5 | ||
0x00000094 | 0xE000.E414 | Timer 1A | NVIC_PRI5_R | 15 - 13 | ||
0x00000098 | 0xE000.E414 | Timer 1B | NVIC_PRI5_R | 23 - 21 | ||
0x0000009C | 0xE000.E414 | Timer 2A | NVIC_PRI5_R | 31 - 29 | ||
0x000000A0 | 0xE000.E418 | Timer 2B | NVIC_PRI6_R | 7 - 5 | ||
0x000000A4 | 0xE000.E418 | Comp 0 | NVIC_PRI6_R | 15 - 13 | ||
0x000000A8 | 0xE000.E418 | Comp 1 | NVIC_PRI6_R | 23 - 21 | ||
0x000000AC | 0xE000.E418 | Comp 2 | NVIC_PRI6_R | 31 - 29 | ||
0x000000B0 | 0xE000.E41C | System Control | NVIC_PRI7_R | 7 - 5 | ||
0x000000B4 | 0xE000.E41C | Flash Control | NVIC_PRI7_R | 15 - 13 | ||
0x000000B8 | 0xE000.E41C | GPIO Port F | NVIC_PRI7_R | 23 - 21 | ||
0x000000BC | 0xE000.E41C | GPIO Port G | NVIC_PRI7_R | 31 - 29 | ||
0x000000C0 | 0xE000.E420 | GPIO Port H | NVIC_PRI8_R | 7 - 5 | ||
0x000000C4 | 0xE000.E420 | UART2, Rx Tx | NVIC_PRI8_R | 15 - 13 | ||
0x000000C8 | 0xE000.E420 | SSI1, Rx Tx | NVIC_PRI8_R | 23 - -21 | ||
0x000000CC | 0xE000.E420 | Timer 3A | NVIC_PRI8_R | 31 - 29 | ||
0x000000D0 | 0xE000.E424 | Timer 3B | NVIC_PRI9_R | 7 - 5 | ||
0x000000D4 | 0xE000.E424 | I2C1 | NVIC_PRI9_R | 15 - 13 | ||
0x000000D8 | 0xE000.E424 | Quad Encoder 1 | NVIC_PRI9_R | 23 - 21 | ||
0x000000DC | 0xE000.E424 | CAN0 | NVIC_PRI9_R | 31 - 29 | ||
0x000000E0 | 0xE000.E428 | CAN1 | NVIC_PRI10_R | 7 - 5 | ||
0x000000E4 | 0xE000.E428 | CAN2 | NVIC_PRI10_R | 15 - 13 | ||
0x000000E8 | 0xE000.E428 | Ethernet | NVIC_PRI10_R | 23 - 21 | ||
0x000000EC | 0xE000.E428 | Hibernate | NVIC_PRI10_R | 31 - 29 | ||
0x000000F0 | 0xE000.E42C | USB0 | NVIC_PRI11_R | 7 - 5 | ||
0x000000F4 | 0xE000.E42C | PWM Gen 3 | NVIC_PRI11_R | 15 - 13 | ||
0x000000F8 | 0xE000.E42C | uDMA Soft Tfr | NVIC_PRI11_R | 23 - 21 | ||
0x000000FC | 0xE000.E42C | uDMA Error | NVIC_PRI11_R | 31 - 29 |
The following five conditions must be true for an interrupt to be generated:
- 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.
- 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).
- 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.
- 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.
- 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:
- Enable the interrupt for a specific peripheral module.
- Enable the interrupts at the NVIC module.
- 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. |
- 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)
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)
GPIOIS (interrupt sense) |
GPIOIEV (Interrupt Event) |
|
---|---|---|
|
|
Falling edge |
|
|
Rising edge |
|
|
Low level |
|
|
High level |
- 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)
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)
- 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)
- 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)
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)
Bit | Bit Name | Description |
---|---|---|
7-0 | |
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
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 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 |
- 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:
- Enable the interrupt for a specific peripheral module.
- This is done with the GPIO Interrupt Mask (GPIOIM) register.
- Enable the interrupts at the NVIC module.
- Enable the interrupt globally .
Interrupt enabling with all 3 levels
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_EN0_R | PORTA | PORTB | PORTC | PORTD | PORTE | UART0 | ... | PORTF | PORTG | 0xE000E100 |
NVIC_EN1_R | PORTH | UART2 | SSI1 | Timer3A | Timer3B | I2C1 | ... | UART6 | UART7 | 0xE000E104 |
NVIC_EN2_R | ... | ... | ... | ... | I2C2 | I2C3 | ... | WTimer0A | WTimer0B | 0xE000E108 |
NVIC_EN3_R | WT1A | WT1B | WT2A | WT2B | WT3A | WT3B | ... | GPIOQ2 | GPIOQ3 | 0xE000E10C |
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:
- Find the IRQ number for this interrupt from Table 7.3. The IRQ number for the GPIO PortF is 30.
- 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.
- 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.
- 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.
- 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.
- 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:
GPIO_PORTF_ICR_R = 0x10;
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.
sw1_int.c
/* sw1_int.c
*
* Runs on EK-TM4C123GXL LaunchPad
*
* GPIO PORTF interrupt that will toggle the on board Blue LED
* on rising edge whenever a user switch (SW1) is pressed.
*
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
void DisableInterrupts(void);
void EnableInterrupts(void);
void WaitForInterrupt(void);
void GPIOPortF_Init(void);
void GPIOPortF_Handler(void);
void GPIOPortF_Init(void)
{
SYSCTL_RCGC2_R |= 0x00000020; /* 1) activate clock for Port F */
GPIO_PORTF_LOCK_R = 0x4C4F434B; /* 2) unlock GPIO Port F */
GPIO_PORTF_CR_R = 0x1F; /* allow changes to PF4-0 */
GPIO_PORTF_AMSEL_R = 0x00; /* 3) disable analog on PF */
GPIO_PORTF_PCTL_R = 0x00000000; /* 4) PCTL GPIO on PF4-0 */
GPIO_PORTF_DIR_R = 0x0E; /* 5) PF4,PF0 in, PF3-1 out */
GPIO_PORTF_AFSEL_R = 0x00; /* 6) disable alt funct on PF7-0 */
GPIO_PORTF_PUR_R = 0x11; /* enable pull-up on PF0 and PF4 */
GPIO_PORTF_DEN_R = 0x1F; /* 7) enable digital I/O on PF4-0 */
GPIO_PORTF_IS_R &= ~0x10; /* PF4 is edge-sensitive */
GPIO_PORTF_IBE_R &= ~0x10; /* PF4 is not both edges */
GPIO_PORTF_IEV_R &= ~0x10; /* PF4 falling edge event */
GPIO_PORTF_ICR_R = 0x10; /* Clear flag4 */
GPIO_PORTF_IM_R |= 0x10; /* arm interrupt on PF4 */
NVIC_PRI7_R = (NVIC_PRI7_R & 0xFF1FFFFF) | 0x00A00000; /* priority 5 */
NVIC_EN0_R = 0x40000000; /* Enable interrupt 30 in NVIC */
EnableInterrupts(); /* Enable global Interrupt flag (I) */
}
void GPIOPortF_Handler(void)
{
volatile int readback;
GPIO_PORTF_ICR_R = 0x10; /* clear PF4 int */
GPIO_PORTF_DATA_R ^= (1<<2); /* toggle Blue LED */
readback = GPIO_PORTF_ICR_R; /* a read to force clearing of interrupt flag */
readback = readback;
}
int main(void)
{
GPIOPortF_Init(); /* initialize GPIO Port F interrupt */
while(1) {
WaitForInterrupt();
}
}
/*********** DisableInterrupts ***************
*
* disable interrupts
*
* inputs: none
* outputs: none
*/
void DisableInterrupts(void)
{
__asm (" CPSID I\n");
}
/*********** EnableInterrupts ***************
*
* emable interrupts
*
* inputs: none
* outputs: none
*/
void EnableInterrupts(void)
{
__asm (" CPSIE I\n");
}
/*********** WaitForInterrupt ************************
*
* go to low power mode while waiting for the next interrupt
*
* inputs: none
* outputs: none
*/
void WaitForInterrupt(void)
{
__asm (" WFI\n");
}
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
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:
- Software delay
- 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.
void delay_1sec(void)
{
for( unsigned long i = 0; i <= 3000000; i++ )
;
}
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.
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.
- Systick Reload value (STRELOAD)
- Systick Control and Status Register (STCTRL)
- 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.
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
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 |
---|---|---|---|---|---|---|---|---|
$E000E010 | o | 0 | COUNT | 0 | CLK_SRC | INTEN | ENABLE | NVIC_ST_CTRL_R |
$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.
- Clear the ENABLE (NVIC_ST_CTRL_R) bit to turn off SysTick during initialization.
- Set the RELOAD (NVIC_ST_RELOAD_R) register.
- Write to the NVIC_ST_CURRENT_R value to clear the counter.
- 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
/* delay is in 62.5ns units */
void SysTick_Wait(uint32_t delay)
{
NVIC_ST_CTRL_R = 0; /* (1) disable SysTick during setup */
NVIC_ST_RELOAD_R = delay-1; /* (2) number of counts to wait */
NVIC_ST_CURRENT_R = 0; /* (3) any value written to CURRENT clears */
NVIC_ST_CTRL_R |= 0x5; /* (4) enable SysTick with core clock */
while((NVIC_ST_CTRL_R&0x00010000)==0) {
; /* wait for COUNT flag */
}
}
One Second Delay
void One_Second_Delay(void)
{
NVIC_ST_CTRL_R = 0; /* disable SysTick during setup */
NVIC_ST_RELOAD_R = 15999999; /* Reload Value goes here */
NVIC_ST_CTRL_R |= 0x5; /* enable SysTick with core clock */
while( (NVIC_ST_CTRL_R & (1<<16) ) == 0)
; /* Monitoring bit 16 to be set */
NVIC_ST_CTRL_R = 0; /* Disabling SysTick Timer */
}
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
/* systick_int.c
*
* Toggle the red LED using the SysTick interrupt
*
* This program sets up the SysTick to interrupt at 1 Hz.
* The system clock is running at 16 MHz.
* 1sec/62.5ns = 16,000,000 for RELOAD register.
* In the interrupt handler, the red LED is toggled.
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
void enable_irq(void);
int main (void)
{
/* enable clock to GPIOF at clock gating control register */
SYSCTL_RCGC2_R |= 0x20;
/* enable the GPIO pins for the LED (PF3, 2, 1) as output */
GPIO_PORTF_DIR_R = 0x0E;
/* enable the GPIO pins for digital function */
GPIO_PORTF_DEN_R = 0x0E;
/* Configure SysTick */
NVIC_ST_RELOAD_R = 16000000-1; /* reload with number of clocks per second */
NVIC_ST_CTRL_R = 7; /* enable SysTick interrupt, use system clock */
enable_irq(); /* global enable interrupt */
while(1) {
;
}
}
void SysTick_Handler(void)
{
GPIO_PORTF_DATA_R ^= 2; /* toggle the red LED */
}
/* global enable interrupts */
void inline enable_irq(void)
{
__asm (" CPSIE I\n");
}
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
- We used Software delay in our blinky, read_sw1, and read_sw2 programs. Replace Software Delay with SysTick Timer Delay.
- 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.
- If we activate the PLL and change the bus clock to 50 MHz (20ns), what is the longest elapsed time we could measure?
Using PLL
TM4C123GH6PM MCU has four different clock sources:
- Precision Internal Oscillator (PIOSC) : 16 MHz.
- Main Oscillator (MOSC) : It can use an external clock source or an external crystal.
- Low-Frequency Internal Oscillator (LFIOSC) : An on-chip internal 30 kHz Oscillator used for Deep-Sleep power-saving modes.
- Hibernate RTC Oscillator (RTCOSC) : It can be configured to be the 32.768 KHz external oscillator source from the Hibernation (HIB) module or the HIB Low-Frequency clock source (HIB LFIOSC), which is intended to provide the system with a real-time clock source.
The system clock is generated by a PLL that can be driven by any crystals or oscillators running between 5 and 25 MHz. The output frequency of the PLL is always 400 MHz, and it is independent on the input clock sources.
Two clock sources, Main OSC (MOSC) and Precision Internal osc (PIOSC) 16 MHz, can work as a clock source for the PLL, and this source can be selected via the MUX. Two multiplexers (MUXs) are used to select different clock sources, and two ways can be used to create a system clock to be used by the CPU:
- One way is to use the Phase-Locked Loop (PLL) clock generator that needs a clock source as the input source to create this system clock.
- Another way is to directly use any one of four clock sources, and this can be selected via a MUX. An easy way is to use Precision Internal OSC (16 MHz) divided by 4 to get a 4-MHz system clock.
When using the PLL, the output frequency of 400 MHz is pre-divided by 2 (becomes 200 MHz) before the user’s divisor is applied. Users can modify this 200-MHz system clock by adding different dividing factors in the SYSDIV in their program to use a lower-frequency system clock. The selected clock source can avoid the SYSDIV and USESYSDIV dividing operations via BYPASS for both MUXs and can be directly sent out as the system clock.
Two registers, Run-Mode Clock Configuration (RCC) register and Run-Mode Clock Configuration 2 (RCC2) register, provide controls for the system clock. The RCC2 register is used to provide additional control parameters that offer additional encodings over the RCC register. These registers control the following clock functionality:
- Source of clocks in sleep and deep-sleep modes
- System clock derived from PLL or other clock source
- Enable or disable the oscillators and PLL
- Clock divisors
- Crystal input selection
Run-Mode Clock Configuration
Bit Number |
Bit Name |
Bit Function |
---|---|---|
0 | MOSCDIS | 0: The main oscillator is enabled 1: The main oscillator is disabled—Default |
5-4 | OSCSRC | Oscillator Source Selection: 0x0: Main Oscillator (MOSC) 0x1: Precision Internal Oscillator (PIOSC)—Default 0x2: Precision Internal Oscillator/4 (PIOSC/4) 0x3: Low-Frequency Internal Oscillator (LFIOSC) |
11 | BYPASS | 0: The system clock uses the PLL output clock divided by the divisor specified by SYSDIV (bits 26–23 in this register) 1: The system clock uses the OSC source and divided by the divisor specified by SYSDIV (bits 26–23 in this register) |
13 | PWRDN | 0: The PLL is operating normally 1: The PLL is powered down. Make sure that another clock source is functioning and that the BYPASS bit is set before setting this bit |
22 | USESYSDIV | 0: The system clock is used undivided 1: The system clock divider is used for the system clock. The system clock divider is forced to be used when the PLL is selected as the source |
26-23 | SYSDIV | System Clock Divisor Specifies which divisor is used to generate the system clock from either the PLL output or from the oscillator source, depending on how the BYPASS bit in this register is configured. |
27 | ACG | 0: The Run-Mode Clock Gating Control (RCGCn) registers are used when the microcontroller enters a sleep mode 1: The SCGCn or DCGCn registers are used to control the clocks distributed to the peripherals when the microcontroller is in a sleep mode. |
In the RCC register, the SYSDIV field specifies which divisor is used to generate the system clock from either the PLL output or the oscillator source. When using the PLL, the VCO frequency of 400 MHz is pre-divided by 2 before the divisor is applied.
SYSDIV | DIVISOR | Frequency BYPASS=0 |
Frequency BYPASS=1 |
Symbolic Definition (Tivaware) |
---|---|---|---|---|
0x0 | ÷1 | Reserved | Clock source frequency/1 | SYSCTL_SYSDIV_1 |
0x1 | ÷2 | Reserved | Clock source frequency/2 | SYSCTL_SYSDIV_2 |
0x2 | ÷3 | 66.67 MHz | Clock source frequency/3 | SYSCTL_SYSDIV_3 |
0x3 | ÷4 | 50 MHz | Clock source frequency/4 | SYSCTL_SYSDIV_4 |
0x4 | ÷5 | 40 MHz | Clock source frequency/5 | SYSCTL_SYSDIV_5 |
0x5 | ÷6 | 33.33 MHz | Clock source frequency/6 | SYSCTL_SYSDIV_6 |
0x6 | ÷7 | 28.57 MHz | Clock source frequency/7 | SYSCTL_SYSDIV_7 |
0x7 | ÷8 | 25 MHz | Clock source frequency/8 | SYSCTL_SYSDIV_8 |
0x8 | ÷9 | 22.22 MHz | Clock source frequency/9 | SYSCTL_SYSDIV_9 |
0x9 | ÷10 | 20 MHz | Clock source frequency/10 | SYSCTL_SYSDIV_10 |
0xA | ÷11 | 18.18 | Clock source frequency/11 | SYSCTL_SYSDIV_11 |
0xB | ÷12 | 16.67 MHz | Clock source frequency/12 | SYSCTL_SYSDIV_12 |
0xC | ÷13 | 15.38 MHz | Clock source frequency/13 | SYSCTL_SYSDIV_13 |
0xD | ÷14 | 14.29 MHz | Clock source frequency/14 | SYSCTL_SYSDIV_14 |
0xE | ÷15 | 13.33 MHz | Clock source frequency/15 | SYSCTL_SYSDIV_15 |
0xF | ÷16 | 12.5 MHz Default |
Clock source frequency/16 | SYSCTL_SYSDIV_16 |
The XTAL (bits 6-10) field specifies Crystal Frequency and is given in the following table.
XTAL | Crystal Freq (MHz) | XTAL | Crystal Freq (MHz) | XTAL | Crystal Freq (MHz) |
---|---|---|---|---|---|
0x4 | 3.579545 MHz | 0xC | 6.144 MHz | 0x14 | 14.31818 MHz |
0x5 | 3.6864 MHz | 0xD | 7.3278 MHz | 0x15 | 16.0 MHz |
0x06 | 4 MHz | 0xE | 8 MHz | 0x16 | 16.384 MHz |
0x7 | 4.096 MHz | 0xF | 8.192 MHz | 0x17 | 18.0 MHz |
0x8 | 4.9152 MHz | 0x10 | 10.0 MHz | 0x18 | 20.0 MHz |
0x9 | 5 MHz | 0x11 | 12.0 MHz | 0x19 | 24.0 MHz |
0xA | 5.12 MHz | 0x12 | 12.288 MHz | 0x1A | 25.0 MHz |
0xB | 6 MHz (reset value) | 0x13 | 13.56 MHz | others | reserved |
Run-Mode Clock Configuration 2
Bit Number |
Bit Name |
Bit(s) Function |
---|---|---|
6-4 | OSCSRC2 | Oscillator Source 2 (Selects the input source for the OSC) 0x0: MOSC (Main oscillator) 0x1: PIOSC (Precision internal oscillator) 0x2: PIOSC/4 (Precision internal oscillator / 4) 0x3: LFIOSC (Low-frequency internal oscillator) 0x4 - 0x6: Reserved 0x7: 32.768 kHz (32.768-kHz external oscillator) |
11 | BYPASS2 | PLL Bypass 2 0: The system clock is the PLL output clock divided by the divisor specified by SYSDIV2. 1: The system clock is derived from the OSC source and divided by the divisor specified by SYSDIV2. |
13 | PWRDN2 | Power-Down PLL 2 0: The PLL operates normally 1: The PLL is powered down. |
22 | SYSDIV2LSB | Additional LSB for SYSDIV2 When DIV400 is set, this bit becomes the LSB of SYSDIV2. If DIV400 is clear, this bit is not used. This bit can only be set or cleared when DIV400 is set. |
28-23 | SYSDIV2 | System Clock Divisor 2 Specifies which divisor is used to generate the system clock from either the PLL output or the oscillator source. SYSDIV2 is used for the divisor when both the USESYSDIV bit in the RCC register and the USERCC2 bit in this register are set. |
30 | DIV400 | Divide PLL as 400 MHz versus 200 MHz This bit, along with the SYSDIV2LSB bit, allows additional frequency choices. 0: Use SYSDIV2 as is and apply to 200 MHz predivided PLL output. 1: Append the SYSDIV2LSB bit to the SYSDIV2 field to create a 7 bit divisor using the 400 MHz PLL output. |
31 | USERCC2 | Use RCC2 0: The RCC register fields are used, and the fields in RCC2 are ignored. 1: The RCC2 register fields override the RCC register fields. |
The RCC2 provides extend fields and functions compared with the RCC register. The SYSDIV2 field in the RCC2 register is 2 bits wider than the SYSDIV field in the RCC register so that additional larger divisors up to 64 are available to allow a lower system clock frequency to be used to improve the system Deep Sleep power consumption. The following Table shows how the SYSDIV2 encoding affects the system clock frequency, depending on whether the PLL is used (BYPASS2=0) or another clock source is used (BYPASS2=1).
When the RCC2 is used, it will override the functions provided by the RCC register.
SYSDIV2 | DIVISOR | Frequency BYPASS2=0 |
Frequency BYPASS2=1 |
Symbolic Definition (Tivaware) |
---|---|---|---|---|
0x0 | ÷1 | Reserved | Clock source frequency/1 | SYSCTL_SYSDIV_1 |
0x1 | ÷2 | Reserved | Clock source frequency/2 | SYSCTL_SYSDIV_2 |
0x2 | ÷3 | 66.67 MHz | Clock source frequency/3 | SYSCTL_SYSDIV_3 |
0x3 | ÷4 | 50 MHz | Clock source frequency/4 | SYSCTL_SYSDIV_4 |
0x4 | ÷5 | 40 MHz | Clock source frequency/5 | SYSCTL_SYSDIV_5 |
... | ... | ... | .... | ... |
0x9 | ÷10 | 20 MHz | Clock source frequency/10 | SYSCTL_SYSDIV_10 |
... | ... | ... | ... | ... |
0x3F | ÷64 | 3.125 MHz | Clock source frequency/64 | SYSCTL_SYSDIV_64 |
Raw Interrupt Status (RIS)
Programming the PLL
The Main Oscillator (MOSC) for the TM4C on the evaluation board is 16 MHz. This means the reference clock (Ref Clk) input to the phase/frequency detector will be 16 MHz. For a 16 MHz crystal, we set the XTAL bits to 10101 (0x15). We use RCC2 because it provides more options
- Configure the system to use RCC2 (SYSCTL_RCC2_R)for advanced features. If the RCC2 register is being used, the USERCC2 bit must be set and the appropriate RCC2 bit/field is used.
- Set BYPASS2 (bit 11), thereby configuring the microcontroller to run off a "raw" clock source and following for the new PLL configuration to be validated before switching the system clock to the PLL.
- Specify the crystal frequency in the four XTAL bits. The OSCSRC2 bits are cleared to select the main oscillator as the oscillator clock source.
- Clear PWRDN2 (bit 13) to activate the PLL.
- Configure and enable the clock divider using the 7-bit SYSDIV2 field. If the 7-bit SYSDIV2 is n, then the clock will be divided by n+1. To get the desired 80 MHz from the 400 MHz PLL, we need to divide by 5. So, we place a 4 into the SYSDIV2 field.
- Wait for the PLL to stabilize by waiting for PLLRIS (bit 6) in the SYSCTL_RIS_R to become high.
- Connect the PLL by clearing the BYPASS2 bit.
PLL Sample Program
/* Configure the system to get its clock from the PLL */
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
#define SYSCTL_RIS_PLLLRIS 0x00000040 /* PLL Lock Raw Interrupt Status */
#define SYSCTL_RCC_XTAL_M 0x000007C0 /* Crystal Value */
#define SYSCTL_RCC_XTAL_6MHZ 0x000002C0 /* 6 MHz Crystal */
#define SYSCTL_RCC_XTAL_8MHZ 0x00000380 /* 8 MHz Crystal */
#define SYSCTL_RCC_XTAL_16MHZ 0x00000540 /* 16 MHz Crystal */
#define SYSCTL_RCC2_USERCC2 0x80000000 /* Use RCC2 */
#define SYSCTL_RCC2_DIV400 0x40000000 /* Divide PLL as 400 MHz vs. 200 MHz */
#define SYSCTL_RCC2_SYSDIV2_M 0x1F800000 /* System Clock Divisor 2 */
#define SYSCTL_RCC2_SYSDIV2LSB 0x00400000 /* Additional LSB for SYSDIV2 */
#define SYSCTL_RCC2_PWRDN2 0x00002000 /* Power-Down PLL 2 */
#define SYSCTL_RCC2_BYPASS2 0x00000800 /* PLL Bypass 2 */
#define SYSCTL_RCC2_OSCSRC2_M 0x00000070 /* Oscillator Source 2 */
#define SYSCTL_RCC2_OSCSRC2_MO 0x00000000 /* MOSC */
#define Bus80MHz 4
void PLL_Init(void)
{
/* 1) configure the system to use RCC2 for advanced features
such as 400 MHz PLL and non-integer System Clock Divisor */
SYSCTL_RCC2_R |= SYSCTL_RCC2_USERCC2;
/* 2) bypass PLL while initializing */
SYSCTL_RCC2_R |= SYSCTL_RCC2_BYPASS2;
/* 3) select the crystal value and oscillator source */
SYSCTL_RCC_R &= ~SYSCTL_RCC_XTAL_M; /* clear XTAL field */
SYSCTL_RCC_R += SYSCTL_RCC_XTAL_16MHZ; /* configure for 16 MHz crystal */
SYSCTL_RCC2_R &= ~SYSCTL_RCC2_OSCSRC2_M; /* clear oscillator source field */
SYSCTL_RCC2_R += SYSCTL_RCC2_OSCSRC2_MO; /* configure for main oscillator source */
/* 4) activate PLL by clearing PWRDN */
SYSCTL_RCC2_R &= ~SYSCTL_RCC2_PWRDN2;
/* 5) set the desired system divider and the system divider least significant bit */
SYSCTL_RCC2_R |= SYSCTL_RCC2_DIV400; /* use 400 MHz PLL */
SYSCTL_RCC2_R = (SYSCTL_RCC2_R&~0x1FC00000) /* clear system clock divider field */
+ (Bus80MHz<<22); /* configure for 80 MHz clock */
/* 6) wait for the PLL to lock by polling PLLLRIS */
while((SYSCTL_RIS_R&SYSCTL_RIS_PLLLRIS)==0){
;
}
/* 7) enable use of PLL by clearing BYPASS */
SYSCTL_RCC2_R &= ~SYSCTL_RCC2_BYPASS2;
}
# Activate the PLL and change the bus clock to 50 MHz.
UART in TM4C123GH6PM Launchpad
A serial port programming is a method of transferring data serially by the means of a few wire, unlike a parallel port which requires many wires for data transfer and limited to a short distance, serial port programming can be used for transferring the data to a larger distance. The advantage of using this serial port is that it is very cheap as compare to the parallel port since it requires a single data line for data transmission making the hardware configuration easy and simple.
Serial data communication uses two methods, asynchronous and synchronous. The synchronous method transfers a block of data (characters) at a time while the asynchronous transfers a single byte at a time.
In this article we are going to use a UART module for serial port communication that uses two pins i.e. RX and TX for data transfer but before proceeding you should have the knowledge of following terms related to the UART.
Half and full-duplex transmission
In data transmission, a duplex transmission is one in which the data can be transmitted and received. This is in contrast to a simplex transmissions such as printers, in which the computer only sends data. Duplex transmissions can be half or full duplex. If data is transmitted one way at a time, it is referred to as half duplex. If the data can go both ways at the same time, it is full duplex. Of course, full duplex requires two wire conductors for the data lines (in addition to ground), one for transmission and one for reception, in order to transfer and receive data simultaneously.
Asynchronous serial communication and data framing
The data coming in at the receiving end of the data line in a serial data transfer is all 0s and 1s; it is difficult to make sense of the data unless the sender and receiver agree on a set of rules, a protocol, on how the data is packed, how many bits constitute a character, and when the data begins and ends.
Start and stop bits
Asynchronous serial data communication is widely used for character-oriented transmissions. In the asynchronous method, each character, such as ASCII characters, is packed between start and stop bits. This is called framing. The start bit is always one bit but the stop bit can be one or two bits. The start bit is always a 0 (low) and the stop bit(s) is 1 (high). For example, look at the following figure where the ASCII character "A", binary 0100 0001, is framed between the start bit and 2 stop bits. Notice that the LSB is sent out first.
In the above figure, when there is no data transfer, the signal stays 1 (high), which is referred to as MARK. The 0 (low) is referred to as SPACE. Notice that the transmission begins with a start bit followed by D0, the LSB, then the rest of the bits until the MSB (D7), and finally, the 2 stop bits indicating the end of the character "A".
In asynchronous serial communications, peripheral chips can be programmed for data that is 5, 6, 7, or 8 bits wide. While in older systems ASCII characters were 7-bit, the modern systems usually send non-ASCII 8-bit data. The old Baud code uses 5- or 6-bit characters but they are rarely seen these days even though most of the hardware still supporting them. In some older systems, due to the slowness of the receiving mechanical device, 2 stop bits were used to give the device sufficient time to organize itself before transmission of the next byte. However, in modern PCs the use of 1 stop bit is common. Assuming that we are transferring a text file of ASCII characters using 1 stop bit, we have a total of 10 bits for each character since 8 bits are for the ASCII code, and 1 and 2 bits are for start and stop bits, respectively. Therefore, for each 8-bit character there are an extra 2 bits, or 25% overhead. (2/8x100=25%)
Parity bit
In some systems in order to maintain data integrity, the parity bit of the character byte is included in the data frame. This means that for each character (7- or 8-bit, depending on the system) we have a single parity bit in addition to start and stop bits. The parity bit may be odd or even. In the case of an odd-parity the number of data bits, including the parity bit, has an odd number of 1s. Similarly, in an even-parity the total number of bits, including the parity bit, is even. For example, the ASCII character "A", binary 0100 0001, has 0 for the even-parity bit. UART chips allow programming of the parity bit for odd-, even-, and no-parity options, as we will see in the next section. If a system requires the parity, the parity bit is transmitted after the MSB, and is followed by the stop bit.
Data transfer rate
The rate of data transfer in serial data communication is stated in bps (bits per second). Another widely used terminology for bps is Baud rate. However, the baud and bps rates are not necessarily equal. This is due to the fact that baud rate is defined as number of signal changes per second. In modems, it is possible for each signal to transfer multiple bits of data. As far as the conductor wire is concerned, the baud rate and bps are the same.
Baud Rate
The baud rate is defined as the rate of data transfer in serial communication, we can choose the desired baud rate for our data transmission as specified in the data sheet, later we will show you how to select this baud rate for our micro-controller.
RS232 and other serial I/O standards
To allow compatibility among data communication equipment made by various manufacturers, an interfacing standard called RS232 was set by the Electronics Industries Association (EIA) in 1960.
Today, RS232 is the most widely used serial I/O interfacing standard. However, since the standard was set long before the advent of the TTL logic family, the input and output voltage levels are not TTL compatible. In the RS232 at the receiver, a 1 is represented by –3 to –25 V, while the 0 bit is +3 to +25 V, making –3 to +3 undefined.
For this reason, to connect any RS232 to a TTL-level chip (microprocessor or UART) we must use voltage converters such as MAX232 or MAX233 to convert the TTL logic levels to the RS232 voltage level, and vice versa. MAX232 and MAX233 IC chips are commonly referred to as line drivers.
RS232 pins
The table below provides the pins and their labels for the RS232, commonly referred to as the DB-9 connector.
Pin | Description |
---|---|
1 | Data carrier detect (DCD) |
2 | Received data (RxD) |
3 | Transmitted data (TxD) |
4 | Data terminal ready (DTR) |
5 | Signal ground (GND) |
6 | Data set ready (DSR) |
7 | Request to send (RTS) |
8 | Clear to send (CTS) |
9 | Ring indicator (RI) |
Data communication classification
Current terminology classifies data communication equipment as DTE (data terminal equipment) or DCE (data communication equipment). DTE refers to terminals and computers that send and receive data, while DCE refers to communication equipment, such as modems, that is responsible for transferring the data. Notice that all the RS232 pin function definitions of from the above table are from the DTE point of view.
The simplest connection between two PCs (DTE and DTE) requires a minimum of three pins, TxD, RxD, and ground. Notice that the connection between two DTE devices, such as two PCs, requires pins 2 and 3 to be interchanged. In looking at the following figure, keep in mind that the RS232 signal definitions are from the point of view of DTE.
RS232 handshaking signals
To ensure fast and reliable data transmission between two devices, the data transfer must be coordinated. Some of the pins of the RS-232 are used for handshaking signals. They are described below. Due to the fact that in serial data communication the receiving device may have no room for the data there must be a way to inform the sender to stop sending data. So some of these handshaking lines may be used for flow control tool
- DTR (data terminal ready)
- When the terminal (or a PC COM port) is turned on, after going through a self-test, it sends out signal DTR to indicate that it is ready for communication. If there is something wrong with the COM port, this signal will not be activated. This is an active-low signal and can be used to inform the modem that the computer is alive and kicking. This is an output pin from DTE (PC COM port) and an input to the modem.
- DSR (data set ready)
- When a DCE (modem) is turned on and has gone through the self-test, it asserts DSR to indicate that it is ready to communicate. Therefore, it is an output from the modem (DCE) and an input to the PC (DTE). This is an active-low signal. If for any reason the modem cannot make a connection to the telephone, this signal remains inactive, indicating to the PC (or terminal) that it cannot accept or send data.
- RTS (request to send)
- When the DTE device (such as a PC) has a byte to transmit, it asserts RTS to signal the modem that it has a byte of data to transmit. RTS is an active-low output from the DTE and an input to the modem.
- CTS (clear to send)
- In response to RTS, when the modem has room for storing the data it is to receive, it sends out signal CTS to the DTE (PC) to indicate that it can receive the data now. This input signal to the DTE is used by the DTE to start transmission.
- CD (carrier detect, or DCD, data carrier detect)
- The modem asserts signal DCD to inform the DTE (PC) that a valid carrier has been detected and that contact between it and the other modem is established. Therefore, DCD is an output from the modem and an input to the PC (DTE).
- RI (ring indicator)
- An output from the modem (DCE) and an input to a PC (DTE) indicates that the telephone is ringing. It goes on and off in synchronization with the ringing sound. Of the six handshake signals, this is the least often used, due to the fact that modems take care of answering the phone. However, if in a given system the PC is in charge of answering the phone, this signal can be used.
From the above description, PC and modem communication can be summarized as follows:
While signals DTR and DSR are used by the PC and modem, respectively, to indicate that they are alive and well, it is RTS and CTS that actually control the flow of data. When the PC wants to send data it asserts RTS, and in response, if the modem is ready (has room) to accept the data, it sends back CTS. If, for lack of room, the modem does not activate CTS, the PC will deassert DTR and try again. RTS and CTS are also referred to as hardware control flow signals.
EK-TM4C123GXL Launchpad UART Programming
Many of the TI ARM chips come with up to eight on-chip UART ports. They are designated as UART0 - UART7. In the EK-TM4C123GXL LaunchPad board, the UART0 port is connected to the ICDI (In-Circuit Debug Interface), which is connected to a USB connector. This ICDI USB connection contains three distinct functions:
- Programming (downloading) using LM Flash Programming software
- Debugging using JTAG (Stellaris In-Circuit Debug Interface)
- To use as a virtual COM port
Virtual COM Port
- When the USB cable connects the EK-TM4C123GXL LaunchPad board, the device driver at the host PC establishes a virtual connection between the PC and the UART0 of the TM4C123GH6PM micro controller.
- On the EK-TM4C123GXL LaunchPad, the connection appears as UART0 (PA0-PA1).
- On the host PC, it appears as a COM (/dev/ttyACM0) port and will work with communication software on the PC such as a terminal emulator (gtkterm, PuTTY, minicom, etc).
- It is called a virtual connection because there is no need for an additional cable to make this connection.
UART Base Addresses
TI TM4C123GH6PM micro controller can have up to 8 UART ports. They are designated as UART0 to UART7. The following shows their Base addresses in the memory map
- UART0 base: 0x4000.C000
- UART1 base: 0x4000.D000
- UART2 base: 0x4000.E000
- UART3 base: 0x4000.F000
- UART4 base: 0x4001.0000
- UART5 base: 0x4001.1000
- UART6 base: 0x4001.2000
- UART7 base: 0x4001.3000
UART Register Map
Name | Offset | Tivaware Name | Description |
---|---|---|---|
UARTDIR | 0x000 | UARTn_DR_R | UART Data |
UARTRSR USRTECR |
0x004 | UARTn_RSR_R UARTn_ECR_R |
UART Receive Status UARTError Clear |
UARTFR | 0x018 | UARTn_FR_R | UART Flag |
UARTILPR | 0x020 | UARTn_ILPR_R | UART IrDA Low-Power Register |
UARTIBRD | 0x024 | UARTn_IBRD_R | UART Integer Baud-Rate Divisor |
UARTFBRD | 0x028 | UARTn_FBRD_R | UART Fractional Baud-Rate Divisor |
UARTLCRH | 0x02C | UARTn_LCRH_R | UART Line Control |
UARTCTL | 0x030 | UARTn_CTL_R | UART Control |
UARTIFLS | 0x034 | UARTn_IFLS_R | UART Interrupt FIFO Level Select |
UARTIM | 0x038 | UARTn_IM_R | UART Interrupt Mask |
UARTRIS | 0x03C | UARTn_RIS_R | UART Raw Interrupt Status |
UARTMIS | 0x040 | UARTn_MIS_R | UART Masked Interrupt Status |
UARTICR | 0x044 | UARTn_ICR_R | UART Interrupt Clear |
UARTDMACTL | 0x048 | UARTn_DMACTL_R | UART DMA Control |
UART9BITADDR | 0x0A4 | UARTn_9BITADDR_R | UART 9-Bit Self Address |
UART9BITAMASK | 0x0A8 | UARTn_9BITAMASK_R | UART DMA Control |
UARTPP | 0xFC0 | UARTn_PP_R | UART Peripheral Properties |
UARTCC | 0xFC8 | UARTn_CC_R | UART Clock Configuration |
where n = 0 to 7 |
UART Registers Setup
There are many special function registers associated with each of the above UARTs. We will be using the UART0 as an example since a virtual connection is available on the TI Tiva LaunchPad.
Baudrate Generator (UARTIBRD)
Two registers are used to set the baud rate:
- They are UART Integer Baud-Rate Divisor (UARTIBRD) and UART Fractional Baud-Rate Divisor (UARTFBRD).
- Of the 32-bit of the UARTIBRD, only lower 16 bits are used and of the 32-bit of the UARTFBRD, only the lower 6 bits are used.
- That gives us total of 22 bits (16 bits integer + 6 bits of fraction).
- To reduce the rate of error and use the standard baud rate, we should use both of the above divisor registers when we program the baud rate.
Baudrate Generator (UARTFBRD)
Baudrate Calculation
The baud rate can be calculated using the following formula:
Desired Baud Rate = SysClk/(16 — ClkDiv)
where the SysCLK is the working system clock connected to the UART and ClkDiv is the values we load into divisor registers. The system clock fed to the CPU can come from XTAL, PLL, or an internal RC. For TI Tiva LaunchPad, the default system clock is 16 MHz. So we can rearrange the above formula as:
Desired Baud Rate = 16MHz/(16 - ClkDiv) = 1MHz/ClkDiv
The ClkDiv value gives us both the integer and fractional values needed for the UARTIBRD and UARTFBRD registers. While the integer portion of the divisor is easy to calculate using a simple calculator, the calculation of the fraction part requires some mathematical manipulation. One way would be, to multiply the fraction by 64 and round it by adding 0.5 to it.
Baudrate Calculation Example
Assume SysCLK Frequency = 16MHz. Find the values for the divisor registers of UARTIBRD and UARTFBRD for the following standard baud rates:
(a) 4800 (b) 9600 (c) 57,600 (d) 115,200
By default, 16 MHz System Clock is divided by 16 before it is fed to the UART.
Therefore, we have 16MHz/16 = 1MHz and ClkDiv = 1MHz.
(a) 1MHz/4800 = 208.3333, UARTIBRD = 208 and UARTFBRD = (0.3333 * 64) + 0.5 = 21.8312 = 21 (b) 1MHz/9600 = 104.166666, UARTIBRD = 104 and UARTFBRD = (0.16666 * 64) + 0.5 = 11 (c) 1MHz/57600 = 17.361, UARTIBRD = 17 and UARTFBRD = (0.361* 64) + 0.5 = 23 (d) 1MHz/115200 = 8.680,UARTIBRD = 8 and UARTFBRD = (0.680 * 64) + 0.5 = 44
In the above Example, it must be noted that default clock is divide-by-16. We can change it to divide-by-8 by setting to 1 the HSE bit of the UART Control (UARTCTL) register.
UART Control (UARTCTL)
The next important register in UART is the control register. It is a 32-bit register and many of the bits are unused. For us the most important bits are RXE, TXE, HSE, and UARTEN.
- UARTEN (D0) UART enable
- This allows one to enable or disable the UART. During the initialization we must disable it while modifying some of the UART registers.
- HSE (D5) High Speed enable
- For the baud rate divisor, by default the system clock is divided by 16 before it is fed to the UART. We can change it to divide-by-8 by setting the HSE = 1 to run higher Baud rate with a low system clock frequency.
- RXE (D8) Receive enable
- We must enable this bit to receive data.
- TXE (D9) Transmit Enable
- We must enable this bit to transmit data.
- The other bits of this register are used for MODEM signals such as CTS (clear to send), RTS (request to send), parity bit, and so on.
UART Line Control (UARTLCRH)
This is the register we use to set the number of bits per character (data length) in a frame and number of stop bits among other things.
- STP2 (D3) stop bit2:
- The stop bit can be 1 or 2. The default is 1 stop bit at the end of each frame. If the receiving device is slow, we can use 2 stop bits by making the STP2 = 1.
- WLEN (D6 -D5) Word Length
- The number of bits per character data in each frame can be 5, 6, 7, or 8. Generally, we use 8 bits for each character data frame. Notice that default is 5 bits and we must change it to 8 bits as shown below:
D6 | D5 | |
---|---|---|
0 | 0 | 5 bits |
0 | 1 | 6 bits |
1 | 0 | 7 bits |
1 | 1 | 8 bits |
- FEN (D4) FIFO enable
- TI Tiva UART has an internal 16-byte FIFO (first in first out) buffer to store data for transmission. There is also another 16-byte FIFO buffer to save all the received bytes. By enabling FEN bit, we can write up to 16 bytes of data block into its transmission FIFO buffer and let it transfer one byte at a time.
- The programmer may set up a threshold for the UART to notify the CPU when the level of the FIFO passes the threshold.
- There is also a 16 byte FIFO for the receiver to buffer the incoming data.
- Upon Reset, the default for FIFO buffer size is 1 byte (character mode).
UART Data (UARTDR)
To transmit a byte of data we must place it in UART Data register. Although it is a 32-bit register, only the lower 8 bits (D0 - D7) are used. It must be noted that "A write to this register initiates a transmission from the UART." In the same way, the received byte is placed in this register and must be retrieved by reading it before it is lost. For transmit, only the lower 8 bits are used. For the receive, the lower 8 bits holds the received byte and other extra 4 bits of D8 - D11 are used for error detection such as framing error, parity error, and so on. There is another register called UARTRSR/UARTRCR (UART Receive Status Error/Error Clear) that can be used to check the source of error.
UART Flag Register (UARTFR)
- TXFE (D7) TX FIFO empty
- When we write a byte to Data register to be transmitted, the byte is placed in transmission FIFO buffer. When the transmitter is not busy, it loads one byte from the FIFO buffer and the FIFO becomes empty and the TXFE is raised. The transmitter then frames the byte and sends it out via TxD pin bit-by-bit serially.
- RXFF (D6) RX FIFO full
- When a byte of data is received, this byte is placed in Data register and RXFF (RX FIFO full) flag bit is raised. In other words, when RXFF is HIGH, it means a byte has been received.
- TXFF (D5) TX FIFOI full
- When we write a byte to Data register to be transmitted, the byte is placed in transmission FIFO buffer. When the transmitter is not busy, it loads one byte from the FIFO buffer and the FIFO is not full anymore and the TXFF is lowered. The transmitter then frames the byte and sends it out via TxD pin bit by bit serially. This is essentially the opposite of TXFE flag bit when FIFO buffer is disabled. We can monitor TXFF flag and upon going LOW we can write another byte to the Data register while the last byte is being transmitted.
- RXFE (D4) RX FIFO empty
- The received byte of data comes in serially into a Receive buffer (first bit coming in is the start bit, 8-bit data, and stop bit). After the last bit (stop bit) has been received by the receiver, the UART circuitry discards the start and stop bits and delivers a byte of data to the Data register and lowers the RXFE bit (RX FIFO empty). This is essentially the opposite of RXFF flag bit. We can monitor RXFE flag and upon going LOW (buffer not empty) we should read (retrieve) the data from Data register.
- Busy (D3)
- When a byte is written to the Data register the Busy flag goes High. While UART busy transmitting data this bit remains high until the stop bit is gone. Upon transmission of the last bit (stop bit), it goes low indicating the transmission is completed. This is only used when it is essential to complete the transmission before starting the next task.
Enabling Clock to UART (RCGCUART)
The RCGCUART register is used to enable the clock to the UART. In this register, there is a bit for each of the UART0 to UART7 modules. If a given UART is not used, we should disable the clock to it to save power. To use UART0, we set to high the D0 of this register.
The GPIO pins used for UART (TxD & RxD)
In addition to the UART registers setup, we must also configure the I/O pins used for UART for their alternate functions. In the case of UART, we need to set up GPIO pins for the alternate functions of TxD and RxD signals. In the previous examples, we used GPIO as simple I/O. We showed the minimum configuration for each port as Digital I/O and providing the Clock to it. When GPIO pins are used for their alternate peripheral functions such as UART, Timer, and ADC, we need to configure five more registers. They are PORTx Run Mode Clock Gating Control, PORTx Digital Enable, PORTx ADC Mode Selection, PORTx Alternate Selection and PORTx Port Control registers.
GPIOAMSEL (GPIO Analog Mode Select)
The TI ARM comes with on-chip ADC (analog to digital converter). Assume a given pins can be used for both ADC and UART and we want to use it for UART. In this case, we must isolate the ADC function since it is not used for analog operation. This is done with GPIOAMSEL (GPIO Analog Mode Selection). Although each port has its own PORTxAMSEL register, not all the pins of each port has ADC capability. Upon Reset, the PORTxAMSEL register has all 0s which means ADC function for the pin, if there is one, is disabled.
GPIO Alternate Function Select (GPIOAFSEL)
In addition to Digital I/O and Clock enable, we must also configure the alternate function select register. Upon Reset, the GPIO pins are configured for simple I/O. To use the alternate function (e.g. UART) of given pin of a given Port, there are two steps to be taken. First we must set the corresponding bits in the alternate function select HIGH. In the case of UART0, PA0 and PA1 pins are used for RxD and TxD signals, respectively. Therefore, we must set both of them high in order to be used for the alternate functions of RxD and TxD. A one in the alternate function select register tells the port pin to perform a function other than simple I/O. GPIO Alternate Function Select Register (GPIOAFSEL) needs to work with the GPIO Port Control Register (GPIOPCTL) together to determine the operational mode and related peripheral functions for the selected GPIO pins.
GPIOPCTL Register
The alternate function is selected by the Port Control Register. Each 32-bit GPIOPCTL register defines the alternate function for the eight pins of that port, 4 bits for each pin. If we wished to specify PA5-2 as SSI0, we would set Port A PCTL bits 23-16 to 0x2222 like this:
GPIO_PORTA_PCTL_R = (GPIO_PORTA_PCTL_R & 0xFF0000FF) + 0x00222200;
For most of the pins, UART function select number is 1. The only exceptions are PC4 and PC5. PC4 and PC5 may be used for either UART4 or UART1. When they are used for UART4, the select number is 1. When they are used for UART1, the select number is 2.
More information on GPIO Alternate functions is available here.
UART Interrupt Programming
Examining the UARTIM (UART Interrupt Mask) register, we see bit 4 allows us to enable the receive interrupt. If the receive interrupt for UART is enabled, when a byte is received, the receive flag is directed to NVIC and that causes the interrupt handler associated with the UART to be executed. In the UART handler we must read the received byte and clear the interrupt flag.
UART Interrupt Mask Register
bit | Name | Description |
---|---|---|
1 | CTSIM | UART Clear to Send Modem Interrupt Mask 1: An interrupt is sent to the interrupt controller when the CTSRIS bit in the UARTRIS register is set. 0: The CTSRIS interrupt is suppressed and not sent to the interrupt controller. |
4 | RXIM | UART Receive Interrupt Mask 1: An interrupt is sent to the interrupt controller when the RXRIS bit in the UARTRIS register is set. 0: The RXRIS interrupt is suppressed and not sent to the interrupt controller. |
5 | TXIM | UART Transmit Interrupt Mask 1: An interrupt is sent to the interrupt controller when the TXRIS bit in the UARTRIS register is set. 0: The TXRIS interrupt is suppressed and not sent to the interrupt controller. |
6 | RTIM | UART Receive Time-Out Interrupt Mask 1: An interrupt is sent to the interrupt controller when the RTRIS bit in the UARTRIS register is set 0: The RTRIS interrupt is suppressed and not sent to the interrupt controller. |
7 | FEIM | UART Framing Error Interrupt Mask 1: An interrupt is sent to the interrupt controller when the RTRIS bit in the UARTRIS register is set. 0: The FERIS interrupt is suppressed and not sent to the interrupt controller. |
8 | PEIM | UART Parity Error Interrupt Mask 1: An interrupt is sent to the interrupt controller when the PERIS bit in the UARTRIS register is set. 0: The PERIS interrupt is suppressed and not sent to the interrupt controller. |
9 | BEIM | UART Break Error Interrupt Mask 1: An interrupt is sent to the interrupt controller when the BERIS bit in the UARTRIS register is set. 0: The BERIS interrupt is suppressed and not sent to the interrupt controller. |
10 | OEIM | UART Overrun Error Interrupt Mask 1: An interrupt is sent to the interrupt controller when the OERIS bit in the UARTRIS register is set. 0: The OERIS interrupt is suppressed and not sent to the interrupt controller. |
12 | 9BITIM | 9-Bit Mode Interrupt Mask 1: An interrupt is sent to the interrupt controller when the 9BITRIS bit in the UARTRIS register is set. 0: The 9BITRIS interrupt is suppressed and not sent to the interrupt controller. |
UART Masked Interrupt Status (UARTMIS)
bit | Name | Value Description |
---|---|---|
1 | CTSMIS | UART Clear to Send Modem Masked Interrupt Status 1: An unmasked interrupt was signaled due to Clear to Send. 0: An interrupt has not occurred or is masked. |
4 | RXMIS | UART Receive Masked Interrupt Status 1: An unmasked interrupt was signaled due to passing through the specified receive FIFO level. 0: An interrupt has not occurred or is masked. |
5 | TXMIS | UART Transmit Masked Interrupt Status 1: An unmasked interrupt was signaled due to passing through the specified transmit FIFO level (if the EOT bit is clear) or due to the transmission of the last data bit (if the EOT bit is set). 0: An interrupt has not occurred or is masked. |
6 | RTMIS | UART Receive Time-Out Masked Interrupt Status 1: An unmasked interrupt was signaled due to a receive time out. 0: An interrupt has not occurred or is masked. |
7 | FEMIS | UART Framing Error Masked Interrupt Status 1: An unmasked interrupt was signaled due to a framing error. 0: An interrupt has not occurred or is masked. |
8 | PEMIS | UART Parity Error Masked Interrupt Status 1: An unmasked interrupt was signaled due to a parity error. 0: An interrupt has not occurred or is masked. |
9 | BEMIS | UART Break Error Masked Interrupt Status 1: An unmasked interrupt was signaled due to a break error. 0: An interrupt has not occurred or is masked. |
10 | OEMIS | UART Overrun Error Masked Interrupt Status 1: An unmasked interrupt was signaled due to an overrun error. 0: An interrupt has not occurred or is masked. |
12 | 9BITMIS | 9-Bit Mode Masked Interrupt Status 1: An unmasked interrupt was signaled due to a receive address match. 0: An interrupt has not occurred or is masked. |
UART Raw Interrupt Status (UARTRIS)
bit | Name | Description |
---|---|---|
1 | CTSRIS | UART Clear to Send Modem Raw Interrupt Status 1: Clear to Send used for software flow control. 0: No interrupt |
4 | RXRIS | UART Receive Raw Interrupt Status 1: The receive FIFO level has passed through the condition defined in the UARTIFLS register. 0: No interrupt |
5 | TXRIS | UART Transmit Raw Interrupt Status 1: If the EOT bit in the UARTCTL register is clear, the transmit FIFO level has passed through the condition defined in the UARTIFLS register. If the EOT bit is set, the last bit of all transmitted data and flags has left the serializer. 0: No interrupt |
6 | RTRIS | UART Receive Time-Out Raw Interrupt Status 1: A receive time out has occurred. 0: No interrupt |
7 | FERIS | UART Framing Error Raw Interrupt Status 1: A framing error has occurred. 0: No Interrupt |
8 | PERIS | UART Parity Error Raw Interrupt Status 1: A parity error has occurred. 0: No Interrupt. |
9 | BERIS | UART Break Error Raw Interrupt Status 1: A break error has occurred. 0: No Interrupt. |
10 | OERIS | UART Overrun Error Raw Interrupt Status 1: An overrun error has occurred. 0: No Interrupt. |
12 | 9BITRIS | 9-Bit Mode Raw Interrupt Status 1: A receive address match has occurred. 0: No Interrupt. |
UART Interrupt Clear (UARTICR)
bit | Name | Description |
---|---|---|
1 | CTSMIC | UART Clear to Send Modem Interrupt Clear Writing a 1 to this bit clears the CTSRIS bit in the UARTRIS register and the CTSMIS bit in the UARTMIS register. This bit is implemented only on UART1 and is reserved for UART0 and UART2. |
4 | RXIC | Receive Interrupt Clear Writing a 1 to this bit clears the RXRIS bit in the UARTRIS register and the RXMIS bit in the UARTMIS register. |
5 | TXIC | Transmit Interrupt Clear Writing a 1 to this bit clears the TXRIS bit in the UARTRIS register and the TXMIS bit in the UARTMIS register. |
6 | RTIC | Receive Time-Out Interrupt Clear Writing a 1 to this bit clears the RTRIS bit in the UARTRIS register and the RTMIS bit in the UARTMIS register. |
7 | FEIC | Framing Error Interrupt Clear Writing a 1 to this bit clears the FERIS bit in the UARTRIS register and the FEMIS bit in the UARTMIS register. |
8 | RTIC | Parity Error Interrupt Clear Writing a 1 to this bit clears the PERIS bit in the UARTRIS register and the PEMIS bit in the UARTMIS register. |
9 | BEIC | Break Error Interrupt Clear Writing a 1 to this bit clears the BERIS bit in the UARTRIS register and the BEMIS bit in the UARTMIS register. |
10 | OEIC | Overrun Error Interrupt Clear Writing a 1 to this bit clears the OERIS bit in the UARTRIS register and the OEMIS bit in the UARTMIS register. |
12 | 9BITIC | 9-Bit Mode Interrupt Clear Writing a 1 to this bit clears the 9BITRIS bit in the UARTRIS register and the 9BITMIS bit in the UARTMIS register. |
TM4C123G LaunchPad UART Tasks
Steps for transmitting & Receiving data
Here are the steps to configure the UART0 to transmit/recive a byte of data for TI Tiva TM4C123GH6PM on the LaunchPad:
- Provide clock to UART0 by writing a 1 to RCGCUART (SYSCTL_RCGCUART_R) register.
- Provide clock to PORTA by writing a 1 to RCGCGPIO (SYSCTL_RCGCGPIO_R) register.
- Disable the UART0 by writing 0 to UARTCTL (UART0_CTL_R) register of UART0.
- Write the integer portion of the Baud rate to the UARTIBRD (UART0_IBRD_R) register of UART0.
- Write the fractional portion of the Baud rate to the UARTFBRD (UART0_FBRD_R) register of UART0.
- Configure the line control value for 1 stop bit, no FIFO, no interrupt, no parity, and 8-bit data size. That gives us 0x60 for the UARTLCRH (UART0_LCRH_R) register of UART0.
- Set UARTEN bit in UARTCTL (UART0_CTL_R) register to enable the UART0.
- Set TxE and RxE bits in UARTCTL register to enable the transmitter and receiver of UART0.
- Make PA0 and PA1 pins to be used as Digital I/O (GPIO_PORTA_DEN_R).
- Select the alternate functions of PA0 (RxD) and PA1 (TxD) pins using the GPIOAFSEL (GPIO_PORTA_AFSEL_R).
- Configure PA0 and PA1 pins for UART function (GPIO_PORTA_PCTL_R).
- Disable analog functionality on PortA0-1 (GPIO_PORTA_AMSEL_R)
- Monitor the RXFE flag bit in UART Flag register (UART0_FR_R) and when it goes LOW (buffer not empty), read the received byte from Data register (UART0_DR_R) and save it.
- Monitor the TXFF flag bit in UART Flag register (UART0_FR_R) and when it goes LOW (buffer not full), write received byte to Data register (UART0_DR_R) to be transmitted.
Before starting the UART echo program, we need a serial communication software in order to receive/transmit the data at the PC end, we can use putty software for this purpose, you can download it from here.
So just install it and run putty software, now click on "serial", it will show COM1 in "serial line" and 9600 baud in the "speed" fields.
In order to get the COM Port Number of connected launchpad, launch device manager (PC), click on ports, “Stellaris Virtual Serial Port” will appear and so its COM Port number in brackets, here you will find the COM port number, in our case it is COM3. Just go to the putty and enter this COM port and select the speed as 115200.
- Write a program to read and write characters (echo) to UART0 Port.
- Write an Echo (read and write) program using other than UART0 (UART1, UART2 ..... UART7)
- UART0 Echo Program showed how UART0 receives data by polling the RXFE status flag. The disadvantage with that program is that it ties down the CPU polling the status flag. Modify it to make it an interrupt driven program.
Programming General-Purpose Timer Module (GPTM)
In TI Tiva micro controllers, the timers are called General-Purpose Timer Module (GPTM). The General-Purpose Timer Module (GPTM) contains six 16/32-bit GPTM blocks and six 32/64-bit Wide GPTM blocks. In otherwords, there are 12 Timer Blocks in the TI Tiva TM4C123G, 6 of them are 16/32-bit timers and the other 6 are 32/64-bit. Each 16/32-bit GPTM block can provide two 16-bit (half-width) timers/counters that are referred to as Timer A and Timer B. These timers/counters can be further configured to operate independently as timers or event counters, or concatenated together to operate as one 32-bit (full-width) timer or one 32-bit Real-Time Clock (RTC).
Similarly, each 32/64-bit Wide GPTM block provides two 32-bit timers, also called Timer A and Timer B, and they can be concatenated together to form a 64-bit timer. Timers can also be used to trigger μDMA transfers. All timers have interrupt controls and separate interrupt vectors as well as separate interrupt handlers.
The 16/32-bit timer blocks are designated as Timer 0, Timer 1, ..., and Timer 5. The following shows the base addresses for the 16/32-bit Timer blocks:
- 16/32-bit Timer 0 base: 0x4003.0000
- 16/32-bit Timer 1 base: 0x4003.1000
- 16/32-bit Timer 2 base: 0x4003.2000
- 16/32-bit Timer 3 base: 0x4003.3000
- 16/32-bit Timer 4 base: 0x4003.4000
- 16/32-bit Timer 5 base: 0x4003.5000
- 32/64-bit Wide Timer 0: 0x4003.6000
- 32/64-bit Wide Timer 1: 0x4003.7000
- 32/64-bit Wide Timer 2: 0x4004.C000
- 32/64-bit Wide Timer 3: 0x4004.D000
- 32/64-bit Wide Timer 4: 0x4004.E000
- 32/64-bit Wide Timer 5: 0x4004.F000
Timer A and Timer B
Each of the Timer blocks contains two timers. They are called Timer A and Timer B. These two timers A and B can work independent of each other as two 16-bit timers or together as one single 32-bit timer. TimerA and TimerB each contains a counter. When the clock is fed to them, they keep counting up/down. We can read their contents as they count and we can load a new value in them. We will examine TimerA and give some examples. The discussion about TimerA also applies equally to TimerB. First, we must remember that, we need to enable the clock to the Timers before they can be used. This is done with the RCGCTimer register.
RCGCTimer

Bit | Name | Description |
---|---|---|
0 | R0 | Timer0 clock control (0: clock disabled, 1: clock enabled) |
1 | R1 | Timer1 clock control (0: clock disabled, 1: clock enabled) |
2 | R2 | Timer2 clock control (0: clock disabled, 1: clock enabled) |
3 | R3 | Timer3 clock control (0: clock disabled, 1: clock enabled) |
4 | R4 | Timer4 clock control (0: clock disabled, 1: clock enabled) |
5 | R5 | Timer5 clock control (0: clock disabled, 1: clock enabled) |
The RCGCTIMER is part of the System Control registers. We must enable the clock to Timer0 - Timer5 before we can use them. Notice, in RCGCTIMER registers, bit R0 is for Timer0 block, bit R1 is for Timer1 block, and so on.
Timers Register Map
Name | Offset | Tivaware Name | Description |
---|---|---|---|
GPTMCFG | 0x000 | TIMERn_CFG_R | GPTM Configuration |
GPTMTAMR | 0x004 | TIMERn_TAMR_R | GPTM Timer A Mode |
GPTMTBMR | 0x008 | TIMERn_TBMR_R | GPTM Timer B Mode |
GPTMCTL | 0x00C | TIMERn_CTL_R | GPTM Control |
GPTMSYNC | 0x010 | TIMERn_SYNC_R | GPTM Synchronize |
GPTMIMR | 0x018 | TIMERn_IMR_R | GPTM Interrupt Mask |
GPTMRIS | 0x01C | TIMERn_RIS_R | GPTM Raw Interrupt Status |
GPTMMIS | 0x020 | TIMERn_MIS_R | GPTM Masked Interrupt Status |
GPTMICR | 0x024 | TIMERn_ICR_R | GPTM Interrupt Clear |
GPTMTAILR | 0x028 | TIMERn_TAILR_R | GPTM Timer A Interval Load |
GPTMTBILR | 0x02C | TIMERn_TBILR_R | GPTM Timer B Interval Load |
GPTMTAMATCHR | 0x030 | TIMERn_TAMATCHR_R | GPTM Timer A Match |
GPTMTBMATCHR | 0x034 | TIMERn_TBMATCHR_R | GPTM Timer B Match |
GPTMTAPR | 0x038 | TIMERn_TAPR_R | GPTM Timer A Prescale |
GPTMTBPR | 0x03C | TIMERn_TBPR_R | GPTM Timer B Prescale |
GPTMTAPMR | 0x040 | TIMERn_TAPMR_R | GPTM TimerA Prescale Match |
GPTMTBPMR | 0x044 | TIMERn_TBPMR_R | GPTM TimerB Prescale Match |
GPTMTAR | 0x048 | TIMERn_TAR_R | GPTM Timer A |
GPTMTBR | 0x4C | TIMERn_TBR_R | GPTM Timer B |
GPTMTAV | 0x050 | TIMERn_TAV_R | GPTM Timer A Value |
GPTMTBV | 0x054 | TIMERn_TBV_R | GPTM Timer B Value |
GPTMRTCPD | 0x058 | TIMERn_RTCPD_R | GPTM RTC Predivide |
GPTMTAPS | 0x05C | TIMERn_TAPS_R | GPTM Timer A Prescale Snapshot |
GPTMTBPS | 0x060 | TIMERn_TBPS_R | GPTM Timer B Prescale Snapshot |
GPTMTAPV | 0x064 | TIMERn_TAPV_R | GPTM Timer A Prescale Value |
GPTMTBPV | 0x068 | TIMERn_TBPV_R | GPTM Timer B Prescale Value |
Where n = 0 to 5 |
Each GPTM block is composed of different control and status registers and these control and status registers can be divided into six groups based on their functions. We only discuss those registers used for the TimerA since the same registers are used for the TimerB. These registers can be divided into the following groups:
- TimerA Control Register group
- TimerA Status Register group
- Timers A and B Interrupt and Configuration Register group
- External Controls group
TimerA Control Register Group
Eight registers are used to configure and control the operations of the Timer A:
- GPTM Configuration Register (GPTMCFG)
- GPTM Control Register (GPTMCTL)
- GPTM Timer A Mode Register (GPTMTAMR)
- GPTM Timer A Interval Load Register (GPTMTAILR)
- GPTM Timer A Match Register (GPTMTAMATCHR)
- GPTM Timer A Prescale Register (GPTMTAPR)
- GPTM Timer A Prescale Match Register (GPTMTAPMR)
- GPTM Timer A Prescale Snapshot Register (GPTMTAPS)
GPTM Control Register (GPTMCTL)

Bit(s) | Name | Description |
---|---|---|
0 | TAEN | Timer A Enable 0: disabled 1: enabled |
1 | TASTALL | Timer A Stall (useful while debugging) 0: Timer A continues counting if the CPU is halted by the debugger, 1: Timer A Stalls (stops counting) while the CPU is halted by the debugger. |
2 & 3 | TAEVENT | Timer A Event Mode 0: positive edge 1: negative edge 2: reserved 3: both edges |
4 | RTCEN | RTC Stall Enable (useful while debugging) 0: RTC Stalls (stops counting) while the CPU is halted by the debugger 1: RTC continues counting if the CPU is halted by the debugger. |
5 | TAOTE | Timer A Output Trigger Enable 0: ADC trigger disabled 1: ADC trigger enabled |
6 | TAPWML | GPTM Timer A PWM Output Level 0: Output is unaffected 1: Output is inverted |
8 | TBEN | GPTM Timer B Enable 0: Timer B is disabled 1: Timer B is enabled. |
9 | TBSTALL | GPTM Timer B Stall Enable 0: Timer B continues counting while the processor is halted by the debugger 1: Timer B freezes counting while the processor is halted by the debug |
11 & 10 | TBEVENT | GPTM Timer B Event Mode 0x0: Positive going edge 0x1: Negative going edge 0x3: Both edges 0x2: Reserved. |
13 | TBOTE | GPTM Timer B Output Trigger Enable 0: The output Timer B ADC trigger is disabled 1: The output Timer B ADC trigger is enabled. |
14 | TBPWML | GPTM Timer B PWM Output Level 0: Output is unaffected 1: Output is inverted. |
31:15 | Reserved | Reserved |
Note:
- During the initialization of the Timers we must disable them. Modifying the configurations of a running timer may cause unpredictable results.
- We use bit D0 of GPTMCTL (GPTM Control) register to disable or enable the TimerA.
GPTM Configuration Register (GPTMCFG)
To configure TimerA as 16- or 32-bit, we must use GPTMCFG (GPTM Configuration) register. Bits D2, D1, and D0 are used to select either 16- or 32-bit option. To use the 16-bit option we need to have D2:D1:D0=0x4. As mentioned above, in 16-bit mode, TimerA and TimerB make two separate timers which work independently.
D2:D1:D0 | Mode |
---|---|
000 | 32-bit Mode |
001 | RTC Counter |
100 | 16-bit Mode |
TimerA Mode selection register (GPTMTAMR)
The mode selection such as periodic, count up/down selection for TimerA is done with GPTM TimerA Mode (GPTMTAMR) register.
Bit(s) | Name | Description |
---|---|---|
0 & 1 | TAMR | Timer A Mode |
2 | TACMR | Timer A Capture Mode (0: Edge Count, 1: Edge Time) |
3 | TAAMS | Timer A Alternate Mode Select (0: Capture or Compare Mode, 1: PWM Mode) |
4 | TACDIR | Timer A Count Direction (0: Count Down, 1: Count Up) |
5 | TAMIE | Timer A Match Interrupt Enable (0: the match interrupt is disabled, 1: enabled) |
6 | TAWOT | Timer A Wait-On-Trigger (0: It begins counting when enabled, 1: waits for trigger) |
7 | TASNAPS | Timer A Snap-Shot Mode (0: Snap Shot is disabled) |
8 | TAILD | Timer A Interval Load Write |
9 | TAPWMIE | Timer A PWM Interrupt Enable (0: Capture Event Interrupt is Disabled, 1: Enabled) |
10 | TAMRSU | Timer A Match Register Update |
11 | TAPLO | Timer A PWM Legacy Operation |
The mode selection is done with D1:D0 bits of GPTMTAMR, as shown below:
Mode | D1 | D0 | Mode Name |
---|---|---|---|
0 | 0 | 0 | Reserved |
1 | 0 | 1 | One-Shot Mode |
2 | 1 | 0 | Periodic Mode |
3 | 1 | 1 | Capture Mode |
The direction Count is done with D4 (TACDIR). Upon Reset, the default is down counter. By making D4=1, TimerA counts up.
GPTM Timer n Interval Load (GPTMTnILR)
When the timer is counting down (the timer counts down if the TACDIR bit of the GPTMTAMR register is 0), the timer counter begins counting from GPTMTnILR (GPTMTAILR / GPTMTBILR) and goes down until it reaches zero. Then, the timer counter is reloaded with the value from GPTMTnILR and the TnTORIS flag of the GPTMRIS register is set.
When the timer is counting up, the timer counter begins counting from 0 and goes up until it reaches the GPTMTnILR value. Then, the timer counter is cleared to zero and the TnTORIS flag of the GPTMRIS register is set.
Upon reset, all bits of the GPTMTnILR register are initialized to 1s which makes the biggest value and has no effect on the timer counting. But the software can change the value of GPTMTnILR. The smaller values for the register leads the timer timeout faster and the TnTORIS flag sets sooner. In other words, changes of delay can be made by setting the GPTMTnILR register and monitoring the TnTORIS flag.
Timer A Status Register Group
Three registers are used to monitor and detect the status of the Timer A:
- GPTM Timer A Register (GPTMTAR)
- GPTM Timer A Value Register (GPTMTAV)
- GPTM Timer A Prescale Value Register (GPTMTAPV).
GPTMTnV (GPTM Timer Value)
GPTMTAV and GPTMTBV are two 16-bit up/down counter registers. When a timer is in 16-bit mode, they work as 2 separate counters. When the timer is in 32-bit mode, they are cascaded to form a 32-bit counter. When read, this register shows the current, free-running value of Timer A in all modes. When written, the value written into this register is loaded into the GPTMTAR register on the next clock cycle.
Note: In 16-bit mode, only the lower 16-bits of the GPTMTAV register can be written with a new value. Writes to the prescaler bits have no effect.
GPTM TimerA Match Register (GPTMTAMATCHR)
The GPTM Timer A Match (GPTMTAMATCHR) register can be used to load a match value, and this value can be compared with the current timer value stored in the GPTM Timer A (GPTMTAR ) register to trigger a matching interrupt or set a flag to indicate that a time value matching has occurred if both values are equal. The GPTM Timer B has the similar registers and functions.
When TimerA keeps counting it is compared with the contents of this register. Whenever the contents of free-running TimerA counter and TimerA Match register are equal, the TAMRIS Flag goes up indicating there is a match. The D4 of GPTMRIS register belongs to this flag.
Note: Although the TimerA Match is a 32-bit register, only the lower 16 bits are used in the 16-bit configuration selection.
GPTM Timer A Register (GPTMTAR)
- This is a 32-bit register and all 32 bits are used to indicate the current value of the 16-bit free-running counter of the Timer A. This register shows the current value of the Timer A counter in all cases except for Input Edge Count and Input Edge Time modes.
- In the Input Edge Count mode, this register contains the number of edges that have occurred. In the Input Edge Time mode, this register contains the time at which the last edge event took place.
- In the 16-bit Input Edge Count, Input Edge Time, and PWM modes, bits 15:0 contain the value of the counter and bits 23:16 contain the value of the prescaler, which is the upper 8 bits of the count. Bits 31:24 always read as 0.
- When a 16/32-bit GPTM is configured to one of the 32-bit modes, GPTMTAR works as a 32-bit register with the upper 16 bits corresponding to the contents of the GPTMTBR register.
- To read the value of the prescaler in 16-bit One-Shot and Periodic modes, read bits [23:16] in the GPTMTAV register. To read the value of the prescalar in periodic snapshot mode, read the Timer A Prescale Snapshot (GPTMTAPS) register.
GPTM Raw Interrupt Status Register (GPTMRIS)
This 32-bit register only used the lower 16 bits to monitor and set a raw or internal interrupt if a GPTM-related raw interrupt occurred. These bits are set whether or not the interrupt is masked in the GPTMIMR register. However, whether these set raw interrupts can be sent to the interrupt controller to be further processed, it depends on whether the corresponding bits on the GPTMIMR register are set (enabled) or not (disabled). Only for those bits that have been set on the GPTMIMR register, they can be sent to the NVIC. The bit field and functions for this register are similar to those in the GPTMIMR register. If a GPTM-related raw interrupt is generated, the corresponding bit on this register is set to 1. Each bit can be cleared by writing a 1 to its corresponding bit in GPTMICR register.
Bit | Name | Description |
---|---|---|
0 | TATORIS | Timer A Time-out Raw interrupt (0: not occurred, 1: occurred) |
1 | CAMRIS | Timer A Capture Mode Match Raw Interrupt (0: not occurred, 1: occurred) |
2 | CAERIS | Timer A Capture Mode Event Raw Interrupt (0: not occurred, 1: occurred) |
3 | RTCRIS | RTC Raw Interrupt(0: not occurred, 1: occurred) |
4 | TAMRIS | Timer A Match Raw Interrupt |
8 | TBTORIS | Timer B Time-out Raw interrupt (0: not occurred, 1: occurred) |
9 | CBMRIS | Timer B Capture Mode Match Raw Interrupt (0: not occurred, 1: occurred) |
10 | CBERIS | Timer B Capture Mode Event Raw Interrupt (0: not occurred, 1: occurred) |
11 | TBMRIS | Timer B Match Raw Interrupt |
16 | WUERIS | 32/64-Bit Wide GPTM Write Update Error Raw Interrupt Status |
Periodic mode vs. one shot mode
The timer can be in 4 different modes including the periodic and one shot modes. In periodic mode the timer continues counting after each timeout. But in one shot mode, the timer stops counting after timeout is reached. For example, when it is in up counting and one shot modes, it counts from 0 to GPTMTnILR and then goes to zero just once and then the TnEN bit of GPTMCTL is cleared causing the timer to stop.
Each timer module has:
- A clock enable bit, SYSCTL_RCGCTIMER_R
- A control register, TIMERn_CTL_R
- A configuration register, TIMERn_CFG_R
- A mode register, TIMERn_TAMR_R
- A 32-bit reload register, TIMERn_TAILR_R
- A resolution (prescale) register, TIMERn_TAPR_R
- An interrupt clear register, TIMERn_ICR_R
- An interrupt arm bit, TIMERn_IM_R
- A flag bit, TIMERn_RIS_R
( where n = 0 to 5)
Initialization and Configuration for One-Shot/Periodic Timer Mode
Perform the following operational steps to complete the initialization and configuration process for this mode:
- Disable the selected timer by clearing the TnEN bit in the GPTMCTL register (TIMERn_CTL_R).
- Initialize the GPTMCFG register to set up timer(s) as 16/32-bit timers (TIMERn_CFG_R).
- Configure the TnMR field in the GPTMTnMR (TIMERn_TAMR_R) register by writing
- 0x1 for one-shot mode.
- 0x2 for periodic mode.
- Optionally configure the TnSNAPS, TnWOT, TnMTE, and TnCDIR bits in the GPTMTnMR (TIMERn_TAMR_R) register to select whether to capture the value of the free-running timer at timeout, use an external trigger to start counting, configure an additional trigger or interrupt, and count-up or count-down operational mode.
- Load the start value (time up value) into the GPTMTnILR (TIMERn_TAILR_R) and the GPTMTnPR (TIMERn_TAPR_R, if prescaler is used) registers for the count-down (count-up) operations.
- If interrupts are required, set the appropriate bits in the GPTMIMR (TIMERn_IMR_R) register to enable the selected interrupt source.
- After these initializations and configurations done, set the TnEN bit in the GPTMCTL (TIMERn_CTL_R) register to enable the timer and start counting.
- If no interrupt is used, one can poll the GPTMRIS (TIMERn_RIS_R) register to check the appropriate bits and wait for the time event to occur. If an interrupt is used, put appropriate codes inside the interrupt handler to process the interrupt. In both cases, the status flags are cleared by writing a 1 to the appropriate bit of the GPTMICR (TIMERn_ICR_R) register.
In One-Shot mode, the timer stops counting after the timeout event. If the timer is configured as a periodic mode, the timer reloads the start value (time up value) and continues counting after the timeout event.
Prescaler register for Timer A
In the above example, the largest time delay that we can create is:
65535 × (1 / 16MHz) = 65535 × 62.5 nsec = 4.096 msec.
One way to create a longer time delay is to use the prescaler register. TimerA in 16-bit mode has an 8-bit prescaler register whose value can go from 0x00 to 0xFF. The 8-bit prescaler extends the 16-bit timer to 24-bit. The prescaler register allows system frequency to be divided by a value between 1 and 256 before it is fed to the TimerA. Note the prescaler can yield proper division only when the timer is configured as a down counter. As shown in Figure 8.5, the clock is divided by GPTMTnPR + 1.
For CPU Freq=16MHz calculate the largest delay size using
- 16-bit TimerA without prescaler and
- 16-bit TimerA with prescaler.
Solution:
1/16MHz = 62.5 nsec is period of clock pulses fed to CPU.
- 65,536 x 62.5 nsec = 4.096 msec for TimerA 16-bit option .
- 65,536 x 256 x 62.5ns = 1.0485 second for TimerA 16-bit option with the Prescaler.
Use of Timers with the I/O Pins
If the timers are only used for the timing-base or timing-up indications, one does not need to use these Capture/Compare pins (CCP) since these pins are mainly connected to the external signals to detect the number of events that have occurred or the time period events that have been experienced.
In the previous session, we showed how to use timers to generate time delay. In this lab session, we will examine the use of timers with the I/O pins. There are five modes for each timer block. They are :
- One-shot/Periodic mode,
- Real-time clock mode,
- Input edge-time mode,
- Input edge-count mode and
- Pulse Width Modulation (PWM).
TimerA and TimerB CCP pins
There are TimerA and TimerB for each of the Timer block 0 to 5. There are one or two designated pins for each of the TimerA and TimerB. For example, TimerA of Timer block 4 is connected internally to pin PC0 of PortC and TimerB of Timer block 4 is connected to pin PC1 of PortC. Notice, TimerA pins are also called CCP0 and TimerB pins are called CCP1. Some of the timers have option to be connected to one of the two pins. For example, TimerA of Timer1 (T1CCP0) can use PB0 or PF2 pins. Table 9.1 shows the pin designations for 16/32-bit Timer block 0 to 5.
TimerA | Pins | TimerB | Pins | |
---|---|---|---|---|
Timer0 | T0CCP0 | PB6 or PF0 | T0CCP1 | PB7 or PF1 |
Timer1 | T1CCP0 | PB4 or PF2 | T1CCP1 | PB5 or PF3 |
Timer2 | T2CCP0 | PB0 or PF4 | T2CCP1 | PB1 |
Timer3 | T3CCP0 | PB2 | T3CCP1 | PB3 |
Timer4 | T4CCP0 | PC0 | T4CCP1 | PC1 |
Timer5 | T5CCP0 | PC2 | T5CCP1 | PC3 |
General-Purpose Timers CCP pins distributions
Timer Pin | I/O pin | Pin Function |
---|---|---|
T0CCP0 | PB6 | 16/32-Bit Timer 0 Capture/Compare/PWM 0 |
PF0 | ||
T0CCP1 | PB7 | 16/32-Bit Timer 0 Capture/Compare/PWM 1 |
PF1 | ||
T1CCP0 | PB4 | 16/32-Bit Timer 1 Capture/Compare/PWM 0 |
PF2 | ||
T1CCP1 | PB5 | 16/32-Bit Timer 1 Capture/Compare/PWM 1 |
PF3 | ||
T2CCP0 | PB0 | 16/32-Bit Timer 2 Capture/Compare/PWM 0 |
PF4 | ||
T2CCP1 | PB1 | 16/32-Bit Timer 2 Capture/Compare/PWM 1 |
T3CCP0 | PB2 | 16/32-Bit Timer 3 Capture/Compare/PWM 0 |
T3CCP1 | PB3 | 16/32-Bit Timer 3 Capture/Compare/PWM 1 |
T4CCP0 | PC0 | 16/32-Bit Timer 4 Capture/Compare/PWM 0 |
T4CCP1 | PC1 | 16/32-Bit Timer 4 Capture/Compare/PWM 1 |
T5CCP0 | PC2 | 16/32-Bit Timer 5 Capture/Compare/PWM 0 |
T5CCP1 | PC3 | 16/32-Bit Timer 5 Capture/Compare/PWM 1 |
WT0CCP0 | PC4 | 32/64-Bit Wide Timer 0 Capture/Compare/PWM 0 |
WT0CCP1 | PC5 | 32/64-Bit Wide Timer 0 Capture/Compare/PWM 1 |
WT1CCP0 | PC6 | 32/64-Bit Wide Timer 1 Capture/Compare/PWM 0 |
WT1CCP1 | PC7 | 32/64-Bit Wide Timer 1 Capture/Compare/PWM 1 |
WT2CCP0 | PD0 | 32/64-Bit Wide Timer 2 Capture/Compare/PWM 0 |
WT2CCP1 | PD1 | 32/64-Bit Wide Timer 2 Capture/Compare/PWM 1 |
WT3CCP0 | PD2 | 32/64-Bit Wide Timer 3 Capture/Compare/PWM 0 |
WT3CCP1 | PD3 | 32/64-Bit Wide Timer 3 Capture/Compare/PWM 1 |
WT4CCP0 | PD4 | 32/64-Bit Wide Timer 4 Capture/Compare/PWM 0 |
WT4CCP1 | PD5 | 32/64-Bit Wide Timer 4 Capture/Compare/PWM 1 |
WT5CCP0 | PD6 | 32/64-Bit Wide Timer 5 Capture/Compare/PWM 0 |
WT5CCP1 | PD7 | 32/64-Bit Wide Timer 5 Capture/Compare/PWM 1 |
Selecting alternate function for Timers pin
Upon Reset, the GPIOAFSEL register has all 0s meaning the I/O pins are used as simple I/O. To use an alternate function, we first must set the bit in the AFSEL register to 1 for that pin. For example, for the PB6, we need to write 0x40 to GPIO_PORTB_AFSL_R register. After that, the GPIOPCTL register must be configured for the desired function. To do that, we need to use the information in Table 23-5 (Page No.1351) of TI Tiva TM4C123GH6PM data sheet or refer here.
Table 9.2 provides the summary for the Timers pins.
Timer Pin | I/O pin | How to use peripheral function on the pin |
---|---|---|
T0CCP0 | PB6 | GPIO_PORTB_AFSEL_R = 0x40 |
PF0 | GPIO_PORTF_AFSEL_R = 0x01 | |
T0CCP1 | PB7 | GPIO_PORTB_AFSEL_R = 0x80 |
PF1 | GPIO_PORTF_AFSEL_R = 0x02 | |
T1CCP0 | PB4 | GPIO_PORTB_AFSEL_R = 0x10 |
PF2 | GPIO_PORTF_AFSEL_R = 0x04 | |
T1CCP1 | PB5 | GPIO_PORTB_AFSEL_R = 0x20 |
PF3 | GPIO_PORTF_AFSEL_R = 0x08 | |
T2CCP0 | PB0 | GPIO_PORTB_AFSEL_R = 0x40 |
PF4 | GPIO_PORTF_AFSEL_R = 0x10 | |
T2CCP1 | PB1 | GPIO_PORTB_AFSEL_R = 0x02 |
T3CCP0 | PB2 | GPIO_PORTB_AFSEL_R = 0x04 |
T3CCP1 | PB3 | GPIO_PORTB_AFSEL_R = 0x08 |
T4CCP0 | PC0 | GPIO_PORTC_AFSEL_R = 0x01 |
T4CCP1 | PC1 | GPIO_PORTC_AFSEL_R = 0x02 |
T5CCP0 | PC2 | GPIO_PORTC_AFSEL_R = 0x04 |
T5CCP1 | PC3 | GPIO_PORTC_AFSEL_R = 0x08 |
WT0CCP0 | PC4 | GPIO_PORTC_AFSEL_R = 0x10 |
WT0CCP1 | PC5 | GPIO_PORTC_AFSEL_R = 0x20 |
WT1CCP0 | PC6 | GPIO_PORTC_AFSEL_R = 0x40 |
WT1CCP1 | PC7 | GPIO_PORTC_AFSEL_R = 0x80 |
WT2CCP0 | PD0 | GPIO_PORTD_AFSEL_R = 0x01 |
WT2CCP1 | PD1 | GPIO_PORTD_AFSEL_R = 0x02 |
WT3CCP0 | PD2 | GPIO_PORTD_AFSEL_R = 0x04 |
WT3CCP1 | PD3 | GPIO_PORTD_AFSEL_R = 0x04 |
WT4CCP0 | PD4 | GPIO_PORTD_AFSEL_R = 0x10 |
WT4CCP1 | PD5 | GPIO_PORTD_AFSEL_R = 0x20 |
WT5CCP0 | PD6 | GPIO_PORTD_AFSEL_R = 0x40 |
WT5CCP1 | PD7 | GPIO_PORTD_AFSEL_R = 0x80 |
Timer Pin | I/O pin | How to select the timer function on the pin |
---|---|---|
T0CCP0 | PB6 | GPIO_PORTB_PCTL_R = 0x0700.0000 |
PF0 | GPIO_PORTF_PCTL_R = 0x0000.0007 | |
T0CCP1 | PB7 | GPIO_PORTB_PCTL_R = 0x7000.0000 |
PF1 | GPIO_PORTF_PCTL_R = 0x0000.0070 | |
T1CCP0 | PB4 | GPIO_PORTB_PCTL_R = 0x0007.0000 |
PF2 | GPIO_PORTF_PCTL_R = 0x0000.0700 | |
T1CCP1 | PB5 | GPIO_PORTB_PCTL_R = 0x0070.0000 |
PF3 | GPIO_PORTF_PCTL_R = 0x0000.7000 | |
T2CCP0 | PB0 | GPIO_PORTB_PCTL_R = 0x0000.0007 |
PF4 | GPIO_PORTF_PCTL_R = 0x0007.0000 | |
T2CCP1 | PB1 | GPIO_PORTB_PCTL_R = 0x0000.0070 |
T3CCP0 | PB2 | GPIO_PORTB_PCTL_R = 0x0000.0700 |
T3CCP1 | PB3 | GPIO_PORTB_PCTL_R = 0x0000.7000 |
T4CCP0 | PC0 | GPIO_PORTC_PCTL_R = 0x0000.0007 |
T4CCP1 | PC1 | GPIO_PORTC_PCTL_R = 0x0000.0070 |
T5CCP0 | PC2 | GPIO_PORTC_PCTL_R = 0x0000.0700 |
T5CCP1 | PC3 | GPIO_PORTC_PCTL_R = 0x0000.7000 |
WT0CCP0 | PC4 | GPIO_PORTC_PCTL_R = 0x0007.0000 |
WT0CCP1 | PC5 | GPIO_PORTC_PCTL_R = 0x0070.0000 |
WT1CCP0 | PC6 | GPIO_PORTC_PCTL_R = 0x0700.0000 |
WT1CCP1 | PC7 | GPIO_PORTC_PCTL_R = 0x7000.0000 |
WT2CCP0 | PD0 | GPIO_PORTD_PCTL_R = 0x0000.0007 |
WT2CCP1 | PD1 | GPIO_PORTD_PCTL_R = 0x0000.0070 |
WT3CCP0 | PD2 | GPIO_PORTD_PCTL_R = 0x0000.0700 |
WT3CCP1 | PD3 | GPIO_PORTD_PCTL_R = 0x0000.7000 |
WT4CCP0 | PD4 | GPIO_PORTD_PCTL_R = 0x0007.0000 |
WT4CCP1 | PD5 | GPIO_PORTD_PCTL_R = 0x0070.0000 |
WT5CCP0 | PD6 | GPIO_PORTD_PCTL_R = 0x0700.0000 |
WT5CCP1 | PD7 | GPIO_PORTD_PCTL_R = 0x7000.0000 |
Example 9.1: Write the code to provide the T0CCP1 function on PF1.
Solution: The I/O pin is used as a peripheral function pin by setting the AFSEL register of PORTF and the desired peripheral function is selected by setting the PCTL register of PORTF:
GPIO_PORTF_AFSEL_R |= 0x02; GPIO_PORTF_PCTL_R = 0x00000070;
Some Implementations on GPTM Modules
Timer system is a complex system with many different components and functions. We only discuss some very popular and important implementations. These include:
- Using timer capture functions to detect the number of input edges.
- Using timer detecting functions to measure the period for periodic signals.
- Using timer control functions to generate PWM signals to control motors.
Using Timer for input edge-time capturing
Input edge-time capture mode
In input edge-time mode, an I/O pin is used to capture the signal transition events. When an event occurs, the content of the timer counter is captured in another register while the counter keeps counting. The program can then read the counter value when the event occurs at a slightly later time.
To configure the timer to input edge time mode the TnMR and TnCMR bits of GPTMTnMR should be set to capture edge-time mode (TnMR = 3 and TnCMR = 1). See Table 8.5. In this mode, the timer counter value is stored in the GPTMTnR register whenever the input pin is triggered by an external event. See Figure 9.1
The timer can be configured to capture on the falling edge, rising edge, or both. To determine the type of edge that is captured, the TnEVENT bits of the GPTMCTL register should be initialized. See Table 8.4
Timer counting in input edge time mode
In edge time mode GPTMTnV and the optional prescaler are combined to make a 24-bit or 48-bit timer. The timer is 24-bit when 16-bit timers are used. The timer is 48-bit when 32-bit timers are used.
In up counting mode, the GPTMTnV and GPTMTnPV registers are initialized with 0s and they count up until they reach to GPTMTnILR and GPTMTnPR, respectively. Then, they are reset to 0s again.
In down counting mode, the GPTMTnV and GPTMTnPV registers are initialized with GPTMTnILR and GPTMTnPR, respectively and they count down until they reach 0. Then, they are reloaded with GPTMTnILR and GPTMTnPR, again. Notice that capturing has no effect on counting and the timer continues counting when the capturing event takes place.
Initialization and Configuration for Input Edge-Time Mode
Perform the following operational steps to complete the initialization and configuration process for this mode (n = A or B):
- Disable the selected timer by clearing the TnEN bit in the GPTMCTL register.
- Initialize the GPTMCFG register by writing 0x4 to setup all timers as 16-bit default timers.
- Configure the TnMR and TnCMR fields in the GPTMTnMR register by writing:
- TnMR = 0x3 for capture mode.
- TnCMR = 0x1 for edge time mode.
- Select a count direction by programming the TnCDIR bit (0=count-down; 1=count-up).
- Configure the event type (positive-going, negative-going, or both) that the timer captures by writing the TnEVENT field of the GPTMCTL register.
- If a prescaler is to be used, write the prescale value to the GPTMTnPR register.
- Load the timer start value into the GPTMTnILR register.
- If interrupts are required, set the CnEIM bit in the GPTMIMR register.
- After these initializations and configurations are done, set the TnEN bit in the GPTMCTL register to enable the timer and begin waiting for edge events.
- If no interrupt is used, one can poll the CnERIS bit in the GPTMRIS register to wait for the edge event to occur. If interrupt is used, put appropriate codes inside the interrupt handler to process the interrupt. In both cases, the status flags are cleared by writing a 1 to the CnECIR bit on the GPTMICR register. The time at which the event happened can be obtained by reading the GPTMTnR register.
In the Input Edge Timing mode, the timer continues running after an edge event has been detected, but the timer interval can be changed at any time by writing the GPTMTnILR register and clearing the TnILD bit in the GPTMTnMR register. The change takes effect at the next cycle after the write taking placing.
Using Timer As a Counter
As shown in Table 8.4, a timer works as a counter when the TAMR bits of the GPTMTnMR are configured to capture mode and the TACMR bit is cleared. In this situation, the timer counts whenever the input pin is triggered. See Figure 9.3. The pin can be configured to count on the falling edge, rising edge, or both. To determine the type of edge that is counted, the TnEVENT bits of the GPTMCTL register should be initialized. See Table 8.3 and Table 8.4.
Timer counting in input edge count mode
In this mode GPTMTnV and the prescaler are combined to make a 24-bit or 48-bit timer. The timer is 24-bit when TimerA and TimerB are used separately. Otherwise, they make a 48-bit timer.
In up counting mode, the GPTMTnV and GPTMTnPV registers are initialized with 0s and they count up until they reach to GPTMTnMATCHR and GPTMTnPMR, respectively. Then, they are reloaded with 0s again. See Figure 9.4.
Note: The value of GPTMTnPR and GPTMTnILR must be greater than the value of GPTMTnPMR and GPTMTnMATCHR.
In down counting, the GPTMTnV and GPTMTnPV registers are initialized with GPTMTnILR and GPTMTnPR, respectively and they count down until they reach to TnMATCHR and TnPMR, respectively. Then, they are reloaded with GPTMTnILR and GPTMTnPR and the CnMRIS flag of the GPTMRIS register is set. As a result, in cases that a task must be done after a specific number of events, the registers should be initialized so that (TnPR:TnILR – TnPMR:TnMATCHR = number of events to be counted); and then the CnMRIS flag is monitored to be set.
Initialization and Configuration for Input Edge-Count Mode
Perform the following operational steps to complete the initialization and configuration process for this mode (n = A or B):
- Disable the selected timer by clearing the TnEN bit in the GPTMCTL register.
- Initialize the GPTMCFG register by writing 0x4 to setup all timers as 16-bit default timers.
- Configure the TnMR and TnCMR fields in the GPTMTnMR register by writing:
- TnMR = 0x3 for capture mode.
- TnCMR = 0x0 for edge count mode.
- Configure the event type (positive-going, negative-going or both) that the timer captures by writing the TnEVENT field of the GPTMCTL register.
- Configure the following registers according to count direction:
- In count-down mode, the GPTMTnMATCHR and GPTMTnPMR registers are configured so that the difference between the value in the GPTMTnILR and GPTMTnPR registers and the GPTMTnMATCHR and GPTMTnPMR registers equals the number of edge events that must be counted. Make sure that the value in the GPTMTnILR and GPTMTnPR registers is greater than the value in the GPTMTnMATCHR and GPTMTnPMR registers.
- In count-up mode, the timer counts from 0x0 to the value in the GPTMTnMATCHR and GPTMTnPMR registers. Note that when executing a count-up, the value of the GPTMTnPR and GPTMTnILR must be greater than the value of GPTMTnPMR and GPTMTnMATCHR registers.
- If interrupts are required, set the CnMIM bit in the GPTMIMR register.
- After these initializations and configurations done, set the TnEN bit in the GPTMCTL register to enable the timer and begin waiting for edge events.
- If no interrupt is used, one can poll the CnMRIS bit in the GPTMRIS register to wait for the edge event to be occurred. If interrupt is used, put appropriate codes inside the interrupt handler to process the interrupt. In both cases, the status flags are cleared by writing a 1 to the CnMCIR bit on the GPTMICR register.
When counting down in the Input Edge-Count Mode, the timer stops after the programmed number of edge events has been detected. To re-enable the timer, ensure that the TnEN bit is cleared and repeat steps 4 through 8.
PWM Mode
- When working in this mode, each timer can be configured as a 24- or 48-bit count-down counter to generate PWM output signals. At start, the timer loads the start values stored in the GPTMTAILR and GPTMTAPR registers (if prescaler is used) and begins its count-down actions. When gets 0, the timer reloads the start value and begin the next cycle.
- The period and frequency of the PWM signal is controlled by the start values setup in the GPTMTAILR and GPTMTAPR registers, and the pulse width is controlled by the values set in the GPTMTAMATCHR and GPTMTAPMR registers (if prescaler is used). The PWM pulse is generated (outputs High) when the timer is at its start value and terminated (outputs Low) when the timer equals to the terminate values stored in the GPTMTAMATCHR and GPTMTAPMR registers.
- The timer can generate three types of interrupt: rising edge, falling edge, and both. The event is configured by the TAEVENT field of the GPTMCTL register, and the interrupt is enabled by setting the TAPWMIE bit in the GPTMTAMR register.
- In this mode, the GPTMTAR and GPTMTAV registers always have the same value, as do the GPTMTAPS and the GPTMTAPV registers.
The operational sequence of using the Timer A to generate a PWM signal is as follows:
- The start values are loaded into the GPTMTAILR and GPTMTAPR registers, and the terminate values are loaded into the GPTMTAMATCHR and GPTMTAPMR registers. The period and duty cycle of the PWM signal are determined based on these values.
- The PWM mode is enabled with the GPTMTAMR register by setting the TAAMS bit to 1, the TACMR bit to 0, and the TAMR field to 0x2.
- The timer is enabled and starts its count-down operation by setting the TAEN bit in the GPTMCTL register to 1. The counter begins counting down until it reaches the 0 state. Then it reloads the start values and continues for the next cycle until disabled by software clearing the TAEN bit in the GPTMCTL register. Alternatively, if the TAWOT bit is set in the GPTMTAMR register, once the TAEN bit is set, the timer waits for a trigger to begin counting.
- As the timer starts counting from its start values, the PWM pulse is generated with outputting High. During the counting-down process, when the value in the timer is equal to the terminate values set in the GPTMTAMATCHR and GPTMTAPMR registers, the pulse of the PWM signal is terminated with outputting Low.
- The output level of the PWM signal can be controlled by the software, it means that the software has the capability of inverting the output PWM signal by setting the TAPWML bit in the GPTMCTL register.
Timer Interrupt Programming
In the previous article, we showed how to program the timers. In those programming examples, we used polling to see if a timeout event occurred. In this section, we give interrupt-based version of those programs. Examine the earlier programs, we could run those programs only one at a time since we have to monitor the timer flag continuously. By using interrupt, we can run several of timer programs all at the same. To do that, we need to enable the timer interrupts using the GPTMIMR (GPTM Interrupt Mask) register.
Timers A and B Interrupt and Configuration Register Group
Six registers are used to control and handle interrupts of the Timers A and B:
- GPTM Interrupt Mask Register (GPTMIMR)
- GPTM Raw Interrupt Status Register (GPTMRIS)
- GPTM Masked Interrupt Status Register (GPTMMIS)
- GPTM Interrupt Clear Register (GPTMICR)
- GPTM Synchronize Register (GPTMSYNC)
- GPTM Peripheral Properties Register (GPTMPP)
GPTM Interrupt Mask Register (GPTMIMR)
Bit | Name | Description |
---|---|---|
0 | TATOIM | Timer A Time-out Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
1 | CAMIM | Timer A Capture Mode Match Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
2 | CAEIM | Timer A Capture Mode Event Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
3 | RTCIM | RTC Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
4 | TAMIM | Timer A Match Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
8 | TBTOIM | Timer B Time-out Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
9 | CBMIM | Timer B Capture Mode Match Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
10 | CBEIM | Timer B Capture Mode Event Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
11 | TBMIM | Timer B Match Interrupt Mask (0: interrupt is disabled, 1: interrupt is enabled) |
16 | WUEIM | 32/64-Bit Wide GPTM Write Update Error Interrupt Mask (0: disabled, 1: enabled) |
IRQ21 is assigned to Timer1A and IRQ23 to Timer2A. The following will enable these timers in NVIC:
NVIC_EN0_R |= 0x00200000; /* enable IRQ21 */ NVIC_EN0_R |= 0x00800000; /* enable IRQ23 */
GPTM Raw Interrupt Status Register (GPTMRIS)
This 32-bit register only used the lower 10 bits to monitor and set a raw or internal interrupt if a GPTM-related raw interrupt occurred. These bits are set whether or not the interrupt is masked in the GPTMIMR register. However, whether these set raw interrupts can be sent to the interrupt controller to be further processed, it depends on whether the corresponding bits on the GPTMIMR register are set (enabled) or not (disabled). Only for those bits that have been set on the GPTMIMR register, they can be sent to the NVIC. The bit field and functions for this register are similar to those in the GPTMIMR register. If a GPTM-related raw interrupt is generated, the corresponding bit on this register is set to 1. Each bit can be cleared by writing a 1 to its corresponding bit in GPTMICR register.
Bit | Name | Description |
---|---|---|
0 | TATORIS | Timer A Time-out Raw interrupt (0: not occurred, 1: occurred) |
1 | CAMRIS | Timer A Capture Mode Match Raw Interrupt (0: not occurred, 1: occurred) |
2 | CAERIS | Timer A Capture Mode Event Raw Interrupt (0: not occurred, 1: occurred) |
3 | RTCRIS | RTC Raw Interrupt(0: not occurred, 1: occurred) |
4 | TAMRIS | Timer A Match Raw Interrupt |
8 | TBTORIS | Timer B Time-out Raw interrupt (0: not occurred, 1: occurred) |
9 | CBMRIS | Timer B Capture Mode Match Raw Interrupt (0: not occurred, 1: occurred) |
10 | CBERIS | Timer B Capture Mode Event Raw Interrupt (0: not occurred, 1: occurred) |
11 | TBMRIS | Timer B Match Raw Interrupt |
16 | WUERIS | 32/64-Bit Wide GPTM Write Update Error Raw Interrupt Status |
GPTM Masked Interrupt Status Register (GPTMMIS)
Similar to the GPTMIMR register, this 32-bit register only used the lower 10 bits to monitor and make a responded interrupt if a GPTM related interrupt is occurred and has been responded. The bit field and functions for this register is similar to those in the GPTMIMR register. A value of 1 on a bit in this register indicates that the corresponding interrupt has occurred and has received a response. All bits are cleared by writing a 1 to the corresponding bit in GPTMICR register.
GPTM Interrupt Clear Register (GPTMICR)
Similar to GPTMIMR register, this 32-bit register only used the lower 10 bits to clear related responded interrupts if the GPTM-related interrupts have received a response and have been processed. This register is used to clear the status bits in the GPTMRIS and GPTMMIS registers. Writing a 1 to a bit clears the corresponding bit in the GPTMRIS and GPTMMIS registers. All processed or responded interrupts must be cleared by using this register. Otherwise the responded interrupt cannot be generated again in the future if it is not cleared. The bit field and functions for this register is similar to those in the GPTMIMR register.
GPTM Sample Programs
1. Build a General Purpose Timer Project
Use GPTM block 0 Timer A (Timer0A) as a 16-bit count-down counter to periodically generate a timeout interrupt to turn on three LEDs, PF3∼PF1, via GPIO Port F. The input clock to the timer is the 16 MHz system clock, and the period to be counted in the Timer0A counter is 65.536 ms.
To perform this periodic interrupt for each 65.536 ms, one needs to:
- Enable and clock the Timer0A for GPTM Block 0.
- Enable and clock the GPIO Port F.
- Disable the Timer0A module before any configuration can be performed.
- Configure the Timer0A to work as a 16-bit count-down periodic counter.
- Load 65535 (65536 – 1) into the GPTMTAILR register as the start value since we want to get the maximum period of time, which is 65.536 ms, for each period.
- Load 15 (16 – 1) into the GPTMTAPR register as a prescale value. After this 16 MHz system clock is divided by this prescaler (16), the working clock for this counter is 1 MHz with a 1-μs period.
- Clear any previous timeout interrupt for Timer0A by writing 1 to an appropriate bit in the GPTMICR register.
- Enable Timer0A timeout interrupt by writing 1 to an appropriate bit in the GPTMIMR register.
- Use NVIC_PRI4_R (Bit 31 - 29 ) to set the interrupt priority level as 3 for the Timer0A.
- Use NVIC_EN0_R (IRQ19) to enable the timeout interrupt for the Timer0A.
- Enable the Timer0A module after these configuration and the Timer0A begins to count.
- Use EnableInterrupts() function to globally enable all interrupts.
- Use an infinitive while() loop to wait for any interrupt coming.
In addition to the main program, one also needs to build the Timer0A interrupt handler:
- Clear the timeout interrupt for Timer0A by writing 1 to an appropriate bit in the GPTMICR register to enable it to be generated in the future.
- Turn on the related LED via GPIO Port F.
2. Use Timer1A and Timer2A timeout events to trigger interrupts
- Configure Timer1A to timeout once every second. In the interrupt handler, toggle the red LED.
- Configure Timer2A to timeout at 10 Hz. In the interrupt handler, toggle the green LED.
3. Measure pulse width using interrupts with a precision of 24 bits and a resolution of 12.5 ns
- The pulse width measurement is performed from rising edge to falling edge.
- The resolution is 12.5 ns, determined by the system bus clock.
- The range is about 25 ns to 209ms with no overflow checking.
Hints:
- The digital-level input signal is connected to two input capture pins, CCP0 and CCP1.
- The bus clock is selected to be 80 MHz so the measurement resolution will be 12.5 ns.
- The rising edge time will be measured by Timer0B without the need of an interrupt and the falling edge interrupts will be handled by Timer0A.
- The pulse width is calculated as the difference in TIMER0_TBR_R - TIMER0_TAR_R latch values.
4. Measure the period of a square wave input signal
Hint: To measure the period of a signal we must measure the time between two falling or rising edges.
5. Generate PWM
- Generate an output PWM with a 1-ms period and a 66% duty cycle (TAPWML = 0) assuming a 50 MHz input clock. The duty cycle could be 33% if the TAPWML = 1.
- Hint:
- The start value is GPTMTAILR = 0xC350 and the match value in GPTMTAMATCHR is 0x411A.
- Use PLL
- Use Timer A
Pulse-Width Modulation — What is it?
The good definition of Pulse-Width Modulation (PWM) is already in the name itself. It means modulating/varying the width of the pulse only, not the frequency. In the digital control system, PWM is used to control the amount of power sent to a device.
The speed of the motor depends on three factors: (a) load, (b) voltage, and (c) current. For a given fixed load we can maintain a steady speed by using a method called pulse width modulation (PWM). By varying (modulating) the width of the pulse applied to the DC motor we can increase or decrease the amount of power provided to the motor, thereby increasing or decreasing the motor speed. Notice that, although the voltage has a fixed amplitude, it has a variable duty cycle. That means the wider the pulse, the higher the speed.
The ability to control the speed of the DC motor using PWM is one reason that DC motors are preferable over AC motors. AC motor speed is dictated by the AC frequency of the voltage applied to the motor and the frequency is generally fixed. As a result, we cannot control the speed of the AC motor when the load is increased.
Principle
A PWM signal consists of two main components that define its behavior: a duty cycle and a frequency.
- Duty Cycle
- The duty cycle describes the amount of time the signal is in a high (ON) state as a percentage of the total time it takes to complete one cycle. A lower duty cycle corresponds to lower power, because the power is OFF for most of the time. Duty cycle is expressed in percent, 100% duty cycle would be fully ON as same as setting the signal to Vcc (high); 0% duty cycle would be the same as grounding the signal.
- Frequency
- The frequency determines how fast the PWM completes a cycle, ie. 100Hz would be 100 cycles per second. In another words, it shows how fast the PWM switches between high and low states. In the digital system, PWM is the method to produce variable voltage using digital means. Typically, digital system only has two output voltages, the high (5V, 3.3V … etc.) or low (0V).
Types of PWM
There are a couple of types of PWM, and it can be classified in different ways. There are two kinds of PWM signals: Symmetric and Asymmetric.
Symmetric PWM
- The pulses of a symmetric PWM signal are always symmetric with respect to the center of each PWM period.
- Symmetric PWM are often used for three-phase AC induction and brushless DC motors, due to the lower harmonic distortion that is generated on phase currents in comparsion to asymmetric PWM methods.
Asymmetric PWM
- The pulses of an asymmetric PWM signal always have the same side aligned with one end of each PWM period.
- Asymmetric PWM can be used for stepper motors and other variable-reluctance motors.
Classifying PWM Signal by Signal-Alignment
The PWM signal can be classified by signal-alignment in four different types:
- Center-aligned PWM:
- Symmetric PWM
- Center-aligned PWMs are most often used in AC motor control to maintain phase alignment
- Left-aligned PWM
- Asymmetric PWM
- Left-aligned PWMs are used for most general-purpose PWM uses
- Right-aligned PWM
- Asymmetric PWM
- Right-aligned PWMs are typically only used in special cases that require alignment opposite of left-aligned PWMs
- Dual-edge PWM
- Dual-edged PWMs are optimized for power conversion where phase alignment must be adjusted
PWM Modules in the TM4C123GH6PM MCU System
The TM4C123GH6PM MCU contains two PWM modules, PWM0 and PWM1. Each module has four PWM generator blocks and a control block, and each generator block can create two PWM output signals, therefore a total of 16 PWM output signals can be generated by these two modules. The control block determines the polarity of the PWM signals, and it also determines which signals are passed through to the output pins.
Following are the basic facts about the PWM features of TI Tiva LaunchPad:
- There are two PWM modules, PWM0 and PWM1.
- Each PWM module has four Generators.
- Each Generator has a Counter (Timer).
- Each Generator has two Compare registers CMPA and CMPB.
- Each Generator has two output pins, which means there are a total of 8 PWM pins per module.
- The Counter of the Generator can be programmed to count-up or count-up/down.
- As the Counter counts , it may change the output pin when the counter value matches the:
- Compare register(s),
- reaches zero, or is
- reloaded.
The options for output pin change are:
- toggle,
- driven LOW, or
- driven HIGH.
The PWM Generator Block
In the TM4C123GH6PM MCU system, each PWM generator block is composed of two components:
- One 16-bit Counter or Timer and
- Two PWM Comparators, cmpA and cmpB.
The counter provides a timing base for two comparators, and it is mainly used to perform count-down or count-up/down functions to provide a comparison source for two comparators.
The PWM Counter (Timer)
The 16-bit counter or timer in each PWM generator runs in one of two modes, Count-Down mode or Count-Up/Down mode:
- In Count-Down mode, the timer counts from the LOAD value to 0, and then it goes back to the LOAD value and continues to perform the counting down.
- In Count-Up/Down mode, the timer counts from 0 up to the LOAD value and then counts down back to 0, back up to the LOAD value, and so on. Generally, Count-Down mode is used for generating left-aligned or right-aligned PWM signals, and the Count-Up/Down mode is used to generate center-aligned PWM signals.
The timers output three signals that can be used in the PWM generation process:
- The direction signal.
- This signal is always LOW in Count-Down mode, but alternates between LOW and HIGH in Count-Up/Down mode.
- A single-clock-cycle-width High pulse when the counter gets zero.
- A single-clock-cycle-width High pulse when the counter is equal to the LOAD value.
The PWM Comparators
Each PWM generator has two comparators that monitor the value of the counter and output a single-clock-cycle-width HIGH when either comparator’s value is equal to the counting value in the counter. These outputs are labeled cmpA and cmpB. In the Count-Up/Down mode, these comparators match both when counting up and when counting down and thus are qualified by the counter direction signal. If either comparator match-value is greater than the counter load value, that comparator never outputs a High pulse.
Figure 14.2(a) shows the behavior of the counter and the relationship of these pulses when the counter is in Count-Down mode.
Figure 14.2(b) shows the behavior of the counter and the relationship of these pulses when the counter is in Count-Up/Down mode.
The PWM Output Signals Generator
Each PWM generator uses the load, zero, cmpA, and cmpB pulses and the dir signal to generate two internal PWM signals, pwmA and pwmB, as shown in Figure 14.3.
In Count-Down mode, four events can affect these signals: zero, load, match A down, and match B down. In Count-Up/Down mode, six events can affect these signals: zero, load, match A down, match A up, match B down, and match B up. The match A or match B events are ignored when they coincide with the zero or load events. If the match A and match B events coincide, the first signal, pwmA, is generated based only on the match A event, and the second signal, pwmB, is generated based only on the match B event.
Each event can affect each output PWM signal by programming to make the output to be:
- Left alone (ignoring the event)
- Toggled
- Driven Low or High
These actions can be used to generate a pair of PWM signals of various positions and duty cycles, which do or do not overlap. Figure 14.3 shows the use of Count-Up/Down mode to generate a pair of center-aligned, overlapped PWM signals that have different duty cycles. The pwmA and pwmB signals shown in this figure are before they have passed through the dead-band generator.
In Figure 14.3, the first PWM generator is set to output High on match A up (COMPA), output Low on match A down (COMPA), and ignore the other four events. The second PWM generator is set to output High on match B up (COMPB ), output Low on match B down (COMPB), and ignore the other four events. Changing the value of comparator A (COMPA) changes the duty cycle of the pwmA signal, and changing the value of comparator B (COMPB) changes the duty cycle of the pwmB signal.
In addition to normal PWM outputs, these two PWM output signals can be combined together to form a so-called Dead-Band output to drive a half-H bridge circuit for some motors.
The Dead-Band Generator
The pwmA and pwmB signals generated by each PWM generator can be passed to the dead-band generator. If the dead-band generator is disabled, these PWM signals can be passed through to the pwmA’ and pwmB’ signals without any modification. If the dead-band generator is enabled, the pwmB signal is ignored and only the pwmA signal is used to generate two PWM signals, pwmA’ and pwmB’.
The first output PWM signal, pwmA’, is the pwmA signal with the rising edge delayed by a programmable amount. The second output PWM signal, pwmB’, is just the inversion of the pwmA signal with a programmable delay added between the falling edge of the pwmA signal and the rising edge of the pwmB’ signal, as shown in Figure 14.4.
The resulting signals are therefore suitable for driving a half-H bridge, with the dead-band delays preventing shoot-through current from damaging the power electronics. These resulting pwmA’ and pwmB’ signals will be transmitted to the output control block.
Programming PWM in TI Tiva LaunchPad
PWM Clock source
The Clock source to the PWM module in TI Tiva LaunchPad is enabled via R1 or R0 bits of RCGCPWM register. The RCGCPWM register is part of the System Control registers and located at the physical address of 0x400F.E000 with offset 0x640. See Figure 15.21.
We enable the clock source to PWM0 module with SYSCTL_RCGCPWM_R |= 1; and to PWM1 module with SYSCTL_RCGCPWM_R |= 2;
SYSCTL_RCGCPWM_R |= 1; /* enable clock source to PWM0 module */ SYSCTL_RCGCPWM_R |= 2; /* enable clock source to PWM1 module */
Run-Mode Clock Configuration (RCC)
This register is used to pre-divide the system clock before feeding it to the PWM modules. The clock source to the PWM module may come directly from the system clock or after going through a divider. See Figures 15.22 and 15.23.
bit | Name | Description | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
19-17 | PWMDIV | PWM Unit Clock: The System Clock is divided by 2PWMDIV-1 when the USEPWMDIV bit is set one.
| ||||||||||||||||||
20 | USEPWMDIV | Enable PWM Clock Divisor (Use PWM clock Divisor) 0: The PWM clock divider is by passed. 1: The PWM clock divider is used as the PWM clock source. |
The D20 bit (USEPWMDIV) of RCC register allows us to make this selection. With Bit D20 = 1, we have the choices of dividing the system clock by 2, 4, 8, 16, 32, and 64 (default) before it is fed to the PWM modules. The D19-D17 bits of RCC register determines the value of the divisor. Notice that the default is divide by 64 if the pre-divider is enabled by D20 bit.
Example:
Find the value of bit 20-17(PWMDIV) of the RCC register for PWM Module clock frequencies of (a) 8MHz, (b) 2MHz, (c) 1MHz, and (d) 250KHz. Assume the system clock frequency is 16MHz.
Solution:
With D20 = 1, we have the following:
(a) D19-D17 = 000 gives us 16MHz/2=8MHz for PWM Module Frequency.
(b) D19-D17 = 010 gives us 16MHz/8=2MHz for PWM Module Frequency.
(c) D19-D17 = 011 gives us 16MHz/16=1MHz for PWM Module Frequency.
(d) D19-D17 = 111 gives us 16MHz/64=250KHz for PWM Module Frequency (default).
Enabling PWMx Generator (Counter)
In TI Tiva LaunchPad, we have two PWM modules PWM0 and PWM1. Each PWM module has four Generators (Counters) as shown in Figure 14.1. Figure 15.24 shows the simplified structure of a Generator.
We use PWMxCTL (PWMx Control) register to enable the Generator (Counter). Notice that we use the terms Counter and Generator interchangeably since there is one Counter per Generator and all the programming of the Generator surrounds the Counter. Also notice that we have a PWMxCTL for each Generator. See Figure 15.25.
The most important bit of the PWMxCTL is D0 (Enable). This bit is used to start or stop the Counter. The next important bit is D1 (MODE). The counter has two modes:
- Count Down: counts down from the load value until it reaches 0. Upon reaching zero, the load value is placed in the counter and the countdown starts again. This is the default option.
- Count Up-Down: counts up from 0 until it reaches the load value. After reaching the load value, it turns around and counts down to 0 and upon reaching 0 it repeats the process. See Figure 15-26.
In both modes, the load value is in a register called LOAD (PWMxLOAD) register. Notice that we must disable a given Generator (Counter) before the initialization. After the initialization is done, we must enable it to start counting.
Table 15-21 shows the offset address for Control register of PWM0 Module. We also have four Control registers for PWM1 Module. The offset addresses are the same except the Base address for the PWM1 module is different, as we mentioned earlier.
Register | Offset |
---|---|
PWM0_0_CTL (Control for Counter0) | 0x040 |
PWM0_1_CTL (Control for Counter1) | 0x080 |
PWM0_2_CTL (Control for Counter2) | 0x0C0 |
PWM0_3_CTL (Control for Counter3) | 0x100 |
The LOAD register
To set the maximum value for the Counter, we have to load register called PWMxLOAD. Since each of PWM0 and PWM1 has four Generators (Counters), we also have four PWMxLOAD registers. The PWMxLOAD register is 16-bit wide and it determines the maximum count and hence the PWM output frequency.
Figure 15.27 shows the LOAD registers of PWM0 Module. We also have four LOAD registers for PWM1 Module. The offset addresses are the same except the base address for the PWM1 module is different, as we mentioned earlier.
Register | Offset |
---|---|
PWM0_0_LOAD | 0x050 |
PWM0_1_LOAD | 0x090 |
PWM0_2_LOAD | 0x0D0 |
PWM0_3_LOAD | 0x110 |
Reading the Current Counter Value
The current value of the Counter (Generator) can be read from the PWMxCOUNT register. As the Counter (Generator) counts up or counts down, we can read the value of this register at any time. Notice, this is a Read-Only (RO) register. Since the Counter is 16-bit, the PWMxCOUNT gives us the current value of the counter which can be between 0 to 0xFFFF. We can set a maximum value for the Counter using the LOAD register. Figure 15.28 shows the Current Count registers of PWMn Module.
Register | Offset |
---|---|
PWM0_0_COUNT | 0x054 |
PWM0_1_COUNT | 0x094 |
PWM0_2_COUNT | 0x0D4 |
PWM0_3_COUNT | 0x114 |
We also have four Current Count registers for PWM1 Module. The offset addresses are the same except the Base address for the PWM1 module is different, as we mentioned earlier.
Compare A and Compare B
Figure 15-29 shows the offset addresses for CompareA registers of PWM0 Module. We also have four CompareA registers for PWM1 Module. The offset addresses are the same except the Base address for the PWM1 module is different, as we mentioned earlier.
Register | Offset |
---|---|
PWM0_0_CMPA | 0x058 |
PWM0_1_CMPA | 0x098 |
PWM0_2_CMPA | 0x0D8 |
PWM0_3_CMPA | 0x118 |
Figure 15-30 shows the offset addresses for CompareB registers of PWM0 Module. We also have four CompareB registers for PWM1 Module. The offset addresses are the same except the Base address for the PWM1 module is different, as we mentioned earlier.
Register | Offset |
---|---|
PWM0_0_CMPB | 0x05C |
PWM0_1_CMPB | 0x09C |
PWM0_2_CMPB | 0x0DC |
PWM0_3_CMPB | 0x11C |
PWM generators
ACTCMPBD: Action when the counter matches comparator B while counting down
ACTCMPBU: Action when the counter matches comparator B while counting up
ACTCMPAD: Action when the counter matches comparator A while counting down
ACTCMPAU: Action when the counter matches comparator A while counting up
ACTLOAD: Action when the counter is reloaded
ACTZERO: Action when the counter reaches zero
For each of the above events the following actions can be choosen:
Value | Description |
---|---|
0x00 | Do nothing |
0x01 | Invert pwmA |
0x02 | Drive pwmA Low |
0x03 | Drive pwmA High |
Figure 15.32 shows the offset addresses for GeneratorA and GeneratorB Control registers of PWM0 Module. We also have four GeneratorA and GeneratorB Control registers for PWM1 Module. The offset addresses are the same except the Base address for the PWM1 module is different.
Register | Offset | Resgister | Offset |
---|---|---|---|
PWM0_0_GENA | 0X060 | PWM0_0_GENB | 0X064 |
PWM0_1_GENA | 0X0A0 | PWM0_1_GENB | 0X0A4 |
PWM0_2_GENA | 0X0E0 | PWM0_2_2GENB | 0X0E4 |
PWM0_3_GENA | 0X120 | PWM0_3_GENB | 0X124 |
Generating Square waves using PWM generators
In TI Tiva LaunchPad, each of the Generator has two Compare registers. They are called PWMxCMPA (PWMx Compare A) and PWMxCMPB (PWMx Compare B). As the Counter counts down (or up), its value is compared with the PWMxCMPx register and upon a match, a PWM output pin will do one of the following:
- do nothing,
- toggle,
- driven HIGH,
- driven LOW
These output actions are not limited to the compare register, you may choose one of these actions when the counter reaches zero and when the counter reloads. The selections of these actions are made in the PWM generator (PWMxGENx) register. Each register is associated with an output pin and has six actions you may specify:
- action when the counter matches comparator B while counting down.
- action when the counter matches comparator B while counting up.
- action when the counter matches comparator A while counting down.
- action when the counter matches comparator A while counting up.
- action when the counter is reloaded.
- action when the counter reaches zero.
These options allow us to generate some elaborate output waveforms. Each wave generator has 2 outputs: PwmA and PwmB; Using PWMxGENA and PWMxGENB registers the action of the outputs are chosen as was shown in Figure 15-24. The details of PWMxGENx register is shown in Figure 15.31. Each event may cause an action specified by two bits.
Generating periodic square wave using down counting mode
For now, we will start with a simple periodic square wave. To do that, a PWMxGENx register is configured so that the output is driven high when the counter is reloaded and the output is driven low when the counter matches a comparator register while counting down. See Figure 15-42.
The PWM Output Frequency in Count Down mode
To calculate the value for the PWMxLOAD register for a desired PWM output frequency, we divide the period of the desired PWM output by the period of the PWM module clock. Examples 15.41 and 15.42 show how to calculate the LOAD register value.
Example 15.41: Assume the PWM3 Module clock frequency is 8MHz. Find the value of the PWM3LOAD register if we want the PWM3 output Frequency of (a) 5KHz, (b) 10KHz, and (c) 25KHz.
Solution: The clock period for PWM3 Module is 1/8MHz = 0.125µs (micro second).
(a) The PWM3 output period is 1/5KHz = 200µs. Now, PWM3LOAD = 200µs/0.125µs = 1600 or 0x640.
(b) The PWM3 output period is 1/10KHz=100µs. Now, PWM3LOAD = 100µs/0.125µs = 800 or 0x320.
(c) The PWM3 output period is 1/25KHz=40µs. Now, PWM3LOAD = 40µs/0.125µs = 320 or 0x140.
Example 15.42:In a given PWM application, we need the PWM output frequency of 60Hz. Using the PWM Module frequencies of Example 15.41, find out the value of the PWMxLOAD register.
Solution: The period for the PWM output is 1/60Hz = 16.6ms
In Example 15.41, we have the following cases:
(a) The PWM Module clock period is 1/8MHz=0.125µs(micro second). PWM×LOAD=16.6ms/0.125µs = 132,800. This is not acceptable since it is larger than 65535, the maximum value the PWM×LOAD register can hold.
(b) The PWM Module clock period is 1/2MHz = 0.5 µs. PWM×LOAD = 16.6ms/0.5 µs = 33,333.
(c) The PWM Module clock period is 1/1MHz = 1 µs. PWM×LOAD = 16.6ms/1µs = 16,600.
(d) The PWM Module clock period is 1/250KHz = 4 µs. PWM×LOAD = 16.6ms/4µs = 4,150.
The PWM output duty cycle in Count Down mode
In this configuration, the duty cycle (the percentage of the time the output is high) is determined by the ratio between PWMxCMPx and PWMxLOAD registers and is shown below:
PWMxCMPx = (100% - DutyCycle%) x PWMxLOAD
Example 15.43: Assume the PWM0 Module System clock frequency is 16MHz. Find the value of the PWMxLOAD and PWMxCMPA registers for the following PWM output frequencies and duty cycles: (a) 1KHz with 25%, (b) 5KHz with 60%, (c) 20KHz with 80%, and (d) 2KHz of 50%.
Solution: The System Clock period for PWM0 Module is 1/16MHz = 62.5ns (nano sec).
(a) The PWM output period is 1 / 1KHz = 1msec. Now, PWM0LOAD = 1ms / 62.5ns = 16000
PWMxCMPA = (100% – Duty Cycle) × PWMxLOAD = (100% – 25%) × 16000 = 75% × 16000 = 12000
(b) The PWM output period is 1/5KHz = 0.2msec. Now, PWM0LOAD = 2ms / 62.5ns = 3200
PWMxCMPA = (100% – Duty Cycle) × PWMxLOAD = (100% – 60%) × 3200 = 40% × 3200 = 1280
(c) The PWM output period is 1/20KHz = 0.05msec. Now, PWM0LOAD = 0.05ms / 62.5ns = 800
PWMxCMPA = (100% – Duty Cycle) × PWMxLOAD = (100% – 80%) × 800 = 20% × 800 = 160
(d) The PWM output period is 1/2KHz = 0.5msec. Now, PWM0LOAD = 0.5ms / 62.5ns = 8000
PWMxCMPA = (100% – Duty Cycle) × PWMxLOAD = (100% – 50%) × 8000 = 50% × 8000 = 4000
Enable the PWM output to the output pin
To provide an easy way to turn off the PWM output, each PWM generator has an enable register PWMENABLE. Since many of the PWM registers are shared, this allows us to disable a given PWM signal from going to the output pin without disturbing the rest of the PWMs. The lower 8 bits of the PWMENABLE register are used for enabling or disabling the PWM0 to PWM7. When the bit is enabled, the signal generated by the PWM generator is connected to the output pin. When the bit is disabled, there will be no output but the PWM generator still runs without any change.
There are total of 8 PWM pins supported by each PWM module. The PWM pins for each of PWM module are designated as PWM0, PWM1, PWM2, ..., PWM7 as shown in Figure 14.1.
We use PWM0_ENABLE_R and PWM1_ENABLE_R to control the outputs of PWM0 and PWM1, respectively. The offset addresses are the same except the base address is different, as we mentioned earlier. See Figure 16.1.
bit | Name | Description |
---|---|---|
0 | PWM0EN | MnPWM0 Output Enable 0: The MnPWM0 signal has zero value. 1: The generated pwm signal is passed to the MnPWM0 pin. |
1 | PWM1EN | MnPWM1 Output Enable 0: The MnPWMx signal has zero value. 1: The generated pwm signal is passed to the MnPWM1 pin |
The role of the PWMENABLE register is shown in the PWM Output Control part of Figure 16.1.
Selecting alternate function for PWMx pin
Upon reset, the GPIOAFSEL register has all 0s meaning the I/O pins are used as simple I/O. To use the alternate function, we first must set to 1 the bit in the AFSEL register for that pin. For example, for the PB6, we need to write 0x40 (01000000 in binary) to GPIO_PORTB_AFSL register. See Tables 16.1 and 16.2. After that, the GPIOPCTL register must be configured for the desired function, as shown in Tables 16.1 and 16.2. The alternate functions for each I/O pin are listed in Table 23-5 of TI Tiva LaunchPad manual. Tables 16.1 through 16.2 provide the summary for the M0PWMx and M1PWMx pins. See Example 16.1.
M0PWMx | PIN | Value for GPIOAFSL register | Value for GPIOPCTL register |
---|---|---|---|
M0PWM0 | PB6 | GPIO_PORTB_AFSEL_R = 0x40 | GPIO_PORTB_PCTL_R=0x04000000 |
M0PWM1 | PB7 | GPIO_PORTB_AFSEL_R = 0x80 | GPIO_PORTB_PCTL_R=0x40000000 |
M0PWM2 | PB4 | GPIO_PORTB_AFSEL_R = 0x10 | GPIO_PORTB_PCTL_R=0x00040000 |
M0PWM3 | PB5 | GPIO_PORTB_AFSEL_R = 0x20 | GPIO_PORTB_PCTL_R=0x00400000 |
M0PWM4 | PE4 | GPIO_PORTE_AFSEL_R = 0x10 | GPIO_PORTE_PCTL_R=0x00040000 |
M0PWM5 | PE5 | GPIO_PORTE_AFSEL_R = 0x20 | GPIO_PORTE_PCTL_R=0x00400000 |
M0PWM6 | PC4 | GPIO_PORTC_AFSEL_R = 0x10 | GPIO_PORTC_PCTL_R=0x00040000 |
PD0 | GPIO_PORTD_AFSEL_R = 0x01 | GPIO_PORTD_PCTL_R=0x00000004 | |
M0PWM7 | PC5 | GPIO_PORTC_AFSEL_R = 0x20 | GPIO_PORTC_PCTL_R=0x00400000 |
PD1 | GPIO_PORTD_AFSEL_R = 0x02 | GPIO_PORTD_PCTL_R=0x00000040 |
M1PWMx | PIN | GPIOAFSL register | Value for GPIOPCTL |
---|---|---|---|
M1PWM0 | PD0 | GPIO_PORTD_AFSEL_R = 0x01 | GPIO_PORTD_PCTL_R= 0x00000005 |
M1PWM1 | PD1 | GPIO_PORTD_AFSEL_R = 0x02 | GPIO_PORTD_PCTL_R=0x00000050 |
M1PWM2 | PA6 | GPIO_PORTA_AFSEL_R = 0x40 | GPIO_PORTA_PCTL_R=0x05000000 |
PE4 | GPIO_PORTE_AFSEL_R = 0x10 | GPIO_PORTE_PCTL_R=0x00050000 | |
M1PWM3 | PA7 | GPIO_PORTA_AFSEL_R = 0x80 | GPIO_PORTA_PCTL_R=0x50000000 |
PE5 | GPIO_PORTE_AFSEL_R = 0x20 | GPIO_PORTE_PCTL_R=0x00500000 | |
M1PWM4 | PF0 | GPIO_PORTF_AFSEL_R = 0x01 | GPIO_PORTF_PCTL_R=0x00000005 |
M1PWM5 | PF1 | GPIO_PORTF_AFSEL_R = 0x02 | GPIO_PORTF_PCTL_R=0x00000050 |
M1PWM6 | PF2 | GPIO_PORTF_AFSEL_R = 0x04 | GPIO_PORTF_PCTL_R=0x00000500 |
M1PWM7 | PF3 | GPIO_PORTF_AFSEL_R = 0x08 | GPIO_PORTF_PCTL_R=0x00005000 |
Example: Show how to select the alternative function for (a) M0PWM0, (b) M0PWM1, and (c) M1PWM2 pins.
Solution:
(a) From Tables 16.1 and 16.2, we see M0PWM0 is the alternate function for pin PB6.
GPIO_PORTB_AFSEL_R |= 0x40; /* PB6 alternative function for M0PWM0 */
GPIO_PORTB_PCTL_R &= ~0x0F000000; /* clear alternate function for PB6 */
GPIO_PORTB_PCTL_R |= 0x04000000; /* clear alternate function of PB6 for M0PWM0 */
(b) From Table 16.1 and 16.2, we see M0PWM1 is the alternate function for pin PB7.
GPIO_PORTB_AFSEL_R |= 0x80; /* PB7 alternative function for M0PWM1 */
GPIO_PORTB_PCTL_R &= ~0xF0000000; /* clear alternate function for PB7 */
GPIO_PORTB_PCTL |= 0x40000000; /* clear alternate function of PB7 for M0PWM1 */
(c) From Table 16.3 and 16.4, we see M1PWM2 is the alternate function for pin PE4.
GPIO_PORTE_AFSEL_R |= 0x10; /* PE4 alternative function for M1PWM2 */
GPIO_PORTE_PCTL_R &= ~0x000F0000; /* clear alternate function for PE4 */
GPIO_PORTE_PCTL_R |= 0x00040000; /* clear alternate function of PE4 for M1PWM2 */
Generating PWM with Microcontroller using Timer/Counter
The basic idea to generate PWM signal is using a counter (or timer), a CMP (compare) value, and a digital output pin. The counter continuously counts to up or down, and is compared with CMP value. The digital output (PWM) will be changed when the counter matches the CMP value, or when counter resets. It can be implemented by software or hardware. Most of microcontrollers already have hardware modules that can generate PWM signal after initialize the registers.
PWM Timer
The PWM timer in the microcontroller runs in one of two modes: Count-Down mode or Count-Up/Down mode.
In Count-Down mode, the timer counts from the Period (LOAD) value to zero, goes back to the Period (LOAD) value, and continues counting down. In Count-Up/Dow mode, the timer counts from zero up to the Period (LOAD) value, back down to zer, back up to the Period (LOAD) value, and so on. Generally, Count-Down mode is used for generating left- or right-aligned PWM signals, while the Count-Up/Down mode is used for generating center-aligned PWM signals.
Center-Aligned PWM
A center-aligned PWM implements the PWM differently from all of the other modes. The PWM timer is configured counting-up and -down mode. The counter starts at zero and count up to the Period (LOAD) vlaue, and when the Period (LOAD) value is reached, the counter starts counting back down to zero. In this mode, the Period (LOAD) value is actually half of the period of the final PWM output.
- A single compare (CMP) value, which ciontains the duty cycle value, is constantly compared with the PWM timer (COUNTER) value. When the timer (COUNTER) value is less than the CMP value, the PWM output signal is deasserted.
- When the timer (COUNTER) value exceeds or equal to the CMP value, the PWM output signal is asserted. When the timer (COUNTER) value reaches the Period (LOAD) value, the timer starts counting down to zero.
- When the timer (COUNTER) value is less than or equal to the CMP value, the PWM output signal is deasserted, and the process repeats.
Left-Aligned PWM
To create the Left-Aligned PWM, a PWM timer counts downward from a specified maxmimum value, called Period (LOAD) value, to zero. When the timer counts to zero, the Period (LOAD) value will be reloaded to the timer and continue to count down..
- When the timer (COUNTER) value is greater than the CMP value, the PWM output signal is asserted.
- When the timer (COUNTER) value is less than or equal to the CMP value, the PWM output signal is deasserted.
- When the timer counts to zero, the timer will reload the value from Period (LOAD) value.
Right-Aligned PWM
To create the Right-Aligned PWM, the PWM timer still runs on counting-down mode
- When the timer (COUNTER) value is greater than the CMP value, the PWM output signal is deasserted.
- When the timer (COUNTER) value is less than or equal to the CMP value, the PWM output signal is asserted.
- When the timer counts to zero, the timer will reload the value from Period (LOAD) value, and the process repeats.
Dual-Edge PWM
A dual-edge PWM uses two different aligned PWM, one is right-aligned and another is left-aligned. Those two PWMs are connected to an AND gate, the output is the dual-edge PWM signal.
PWM Examples
Configuring GPIO pin for PWM
In using PWM, we must configure the GPIO pins for PWM output. In this regard, it is same as all other peripherals. The steps are as follows:
- Enable the clock to GPIO pin by using RCGCGPIO.
- Set the GPIOAFSEL (GPIO alternate function) for PWM output pins.
- Enable digital pins in the GPIODEN (GPIO Digital enable) register.
- Assign the PWM signals to specific pins using GPIOCTL register (See Tables 16.1 through 16.4).
Configuring PWM generator to create pulses
After the GPIO configuration, we need to take the following steps to configure the PWM:
- Disable the generator using PWM×CTL register.
- Configure PWM×GENA (or PWM×GENB).
- Load the value into PWM×LOAD register to set the desired output frequency.
- Load the value into PWM×CMPA (or PWMxCMPB) register to set the desired duty cycle.
- Start the PWM generator using PWMxCTL.
- Configure PWM×ENABLE register to direct the PWMx to output pin.
Source Code
/* Use PWM to control LED intensity */
/* In the infinite loop, the value of CMPA register is decremented
* by 100 every 20 ms. The decreasing CMPA value causes the duty cycle
* to lengthen.
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
void delayMs(int n);
int main(void)
{
int x = 15999;
/* Enable Peripheral Clocks */
SYSCTL_RCGCPWM_R |= 2; /* enable clock to PWM1 */
SYSCTL_RCGCGPIO_R |= 0x20; /* enable clock to PORTF */
SYSCTL_RCC_R &= ~0x00100000; /* no pre-divide for PWM clock */
/* Enable port PF3 for PWM1 M1PWM7 */
GPIO_PORTF_AFSEL_R = 8; /* PF3 uses alternate function */
GPIO_PORTF_PCTL_R &= ~0x0000F000; /* make PF3 PWM output pin */
GPIO_PORTF_PCTL_R |= 0x00005000;
GPIO_PORTF_DEN_R |= 8; /* pin digital */
PWM1_3_CTL_R = 0; /* stop counter */
PWM1_3_GENB_R = 0x0000008C; /* M1PWM7 output set when reload, */
/* clear when match PWMCMPA */
PWM1_3_LOAD_R = 16000; /* set load value for 1kHz (16MHz/16000) */
PWM1_3_CMPA_R = 15999; /* set duty cycle to min */
PWM1_3_CTL_R = 1; /* start timer */
PWM1_ENABLE_R = 0x80; /* start PWM1 ch7 */
for(;;) {
x = x - 100;
if (x <= 0)
x = 16000;
PWM1_3_CMPA_R = x;
delayMs(20);
}
}
/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(int n)
{
int i, j;
for(i = 0 ; i < n; i++)
for(j = 0; j < 3180; j++)
{} /* do nothing for 1 ms */
}
- Write a program to generate programmable pulse-width modulated outputs.
- Use PWM0A/PB6, PLL
- Using M1PWM7, write a program to create 1-Khz frequency with 50% duty cycle on PF3 pin (green LED). Use System Clock of 16MHz without division. Set the options of PWMGENB register to set the output when reload and clear the output when PWMCMPA match.
- Generate two square waves with 50% duty cycle and 180 degrees out of phase using PWM0/PB6 and PWM1/PB7.
ADC devices
Analog-to-digital converters are among the most widely used devices for data acquisition. Digital computers use binary (discrete) values, but in the physical world everything is analog (continuous). Temperature, pressure (wind or liquid), humidity, and velocity are a few examples of physical quantities that we deal with every day.
A physical quantity is converted to electrical (voltage, current) signals using a device called a transducer. Transducers used to generate electrical outputs are also referred to as sensors. Sensors for temperature, velocity, pressure, light, and many other natural physical quantities produce an output that is voltage (or current).
Therefore, we need an analog-to-digital (ADC) converter to translate the analog signals to digital numbers so that the micro controller can read and process the numbers.
Major characteristics of the ADC
Resolution
The ADC has n-bit resolution, where n can be 8, 10, 12, 16, or even 24 bits. Higher-resolution ADCs provide a smaller step size, where step size is the smallest change that can be discerned by an ADC. Some widely used resolutions for ADCs are shown in Table 11.1. Although the resolution of an ADC chip is decided at the time of its design and cannot be changed, we can control the step size with the help of what is called Vref.
n-bit | Number of steps | Step size |
---|---|---|
8 | 256 | 5V/256 = 19.53 mV |
10 | 1024 | 5V/1024 = 4.88 mV |
12 | 4096 | 5V/4096 = 1.2 mV |
16 | 65,536 | 5V/65,536 = 0.076 mV |
Vref
Vref is an input voltage used for the reference voltage. The voltage connected to this pin, along with the resolution of the ADC chip, determine the step size. For an 8-bit ADC, the step size is Vref / 256 because it is an 8-bit ADC, and 2 to the power of 8 gives us 256 steps. See Table 11.1.
- If the analog input range needs to be 0 to 4 volts, Vref is connected to 4 volts. That gives 4 V / 256 = 15.62 mV for the step size of an 8-bit ADC.
- If we need a step size of 10 mV for an 8-bit ADC, then Vref = 2.56 V, because 2.56 V / 256 = 10 mV.
- For the 10-bit ADC, if the Vref = 5V, then the step size is 4.88 mV as shown in Table 11.1. Tables 11.2 and 11.3 show the relationship between the Vref and step size for the 8- and 10-bit ADCs, respectively.
- In some applications, we need the differential reference voltage where Vref = Vref (+) – Vref (–). Often the Vref (–) pin is connected to ground and the Vref (+) pin is used as the Vref.
Vref (V) | Vin Range (V) | Step Size (mV) |
---|---|---|
5.00 | 0 to 5 | 5 / 256 = 19.53 |
4.00 | 0 to 4 | 4 / 256 = 15.62 |
3.00 | 0 to 3 | 3 / 256 = 11.71 |
2.56 | 0 to 2.56 | 2.56 / 256 = 10 |
2.00 | 0 to 2 | 2 / 256 = 7.81 |
1.28 | 0 to 1.28 | 1.28 / 256 = 5 |
1.00 | 0 to 1 | 1 / 256 = 3.90 |
Vref (V) | Vin Range (V) | Step Size (mV) |
---|---|---|
5.00 | 0 to 5 | 5 / 1024 = 4.88 |
4.96 | 0 to 4.096 | 4.096 / 1024 = 4 |
3.00 | 0 to 3 | 3 / 1024 = 2.93 |
2.56 | 0 to 2.56 | 2.56 / 1024 = 2.5 |
2.00 | 0 to 2 | 2 / 1024 = 2 |
1.28 | 0 to 1.28 | 1.28 / 1024 = 1.25 |
1.024 | 0 to 1.024 | 1.024 / 1024 = 1 |
Conversion time
Conversion time is defined as the time it takes the ADC to convert the analog input to a digital number. The conversion time is dictated by the clock source connected to the ADC in addition to the method used for data conversion and technology used in the fabrication of the ADC.
Digital data output
In an 8-bit ADC we have an 8-bit digital data output of D0–D7, while in the 10-bit ADC the data output is D0–D9. To calculate the output voltage, we use the following formula:
DOUT = VIN / Step size
where Dout = digital data output (in decimal), Vin = analog input voltage, and step size (resolution) is the smallest change, which is Vref/256 for an 8-bit ADC.
Parallel versus Serial ADC
The ADC chips are either parallel or serial. In parallel ADC, we have 8 or more pins dedicated to bringing out the binary data, but in serial ADC we have only one pin for data out. The D0–D7 data pins of the 8-bit ADC provide an 8-bit parallel data path between the ADC chip and the CPU. In the case of the 16-bit parallel ADC chip, we need 16 pins for the data path.
In order to save pins, many 12- and 16-bit ADCs use pins D0–D7 to send out the upper and lower bytes of the binary data. In recent years, for many applications where space is a critical issue, using such a large number of pins for data is not feasible. For this reason, serial devices such as the serial ADC are becoming widely used. While the serial ADCs use fewer pins and their smaller packages take much less space on the printed circuit board, more CPU time is needed to get the converted data from the ADC because the CPU must get data one bit at a time, instead of in one single read operation as with the parallel ADC.
Analog input channels
Many data acquisition applications need more than one analog input for ADC. For this reason, we see ADC chips with 2, 4, 8, or even 16 channels on a single chip. Multiplexing of analog inputs is widely used allowing us to monitor multiple quantities such as temperature, pressure, flow, and so on. Nowadays, some ARM microcontroller chips come with 16-channel on-chip ADC.
Start conversion and end-of-conversion signals
For the conversion to be controlled by the CPU, there are needs for start conversion (SC) and end-of-conversion (EOC) signals. When SC is activated, the ADC starts converting the analog input value of Vin to a digital number. The amount of time it takes to convert varies depending on the conversion method. When the data conversion is complete, the end-of-conversion signal notifies the CPU that the converted data is ready to be picked up.
ADC Modules in the TM4C123GH6PM MCU System
The TM4C123GH6PM microcontroller contains two identical Analog-to-Digital Converter modules. These two modules, ADC0 and ADC1, provide 12-bit conversion precision and share the same 12 analog input channels. Each ADC module operates independently and can therefore execute different sample sequences, sample any of the analog input channels at any time, and generate different interrupts and triggers.
These two ADC modules provide the following features:
- 12 shared analog input channels
- 12-bit precision ADC
- Single-ended and differential-input configurations
- On-chip internal temperature sensor
- Maximum sample rate of one million samples/second (MSPS)
- Optional phase shift in sample time programmable
- Four programmable sample conversion sequencers from one to eight entries long, with corresponding conversion result FIFOs
- Five flexible trigger controls
- Software Trigger Controller (default)
- Timers
- Analog Comparators
- PWM
- GPIO
- Hardware averaging of up to 64 samples
- 2 Analog Comparators
ADC Programming with the Tiva TM4C123G
To program these two modules, ADC0 and ADC1, we need to understand some of the major registers. Figure 11.21 shows a simplified block diagram of a Tiva ADC module.
Enabling Clock to ADC
First thing we need to do is to enable the clock to the ADC0 or ADC1. Bit 0 and bit 1 of RCGCADC register are used to enable the clock to ADC0 and ADC1 modules, respectively. The RCGCADC is part of the System Control register and is located at base address of 0x400F.E000 with offset 0x638. That means, the RCGCADC is located at physical address of 0x400FE638 (0x400FE000 + 0x638 = 0x400FE638) in memory map. See Figure 11.22.
bit | Name | Description |
---|---|---|
0 | R0 | 0: ADC Module 0 is disabled, 1: Enable and provide a clock to ADC module 0 |
1 | R1 | 0: ADC Module 1 is disabled, 1: Enable and provide a clock to ADC module 1 |
The Sample Sequencer
The Sample Sequencer is a part of the ADC module that moves the conversion result of the ADC to one of the FIFOs. There are 4 Sample Sequencers. They are called SS3, SS2, SS1, and SS0. Each one of them is associated with a FIFO. The FIFOs have different sizes so the sample sequencers have different lengths of sequences. The longest sequence has 8 samples and the shortest has only one. Table 11.10 shows the relation between the sample sequencers and FIFO sizes.
Sequencer | Number of Samples |
Depth of FIFO |
---|---|---|
SS0 | 8 | 8 |
SS1 | 4 | 4 |
SS2 | 4 | 4 |
SS3 | 1 | 1 |
We will use the SS3 with a single sample. We use ADCACTSS (ADC Active Sample Sequencer) to enable the SS3. When bit 3 (ASEN3) is set to 1 the SS3 is enabled. We must disable the SS3 before configuring the sample sequencer so that no erroneous events occur during initialization. After the initialization is done, we must enable it to use it.
Bit | Name | Description |
---|---|---|
16 | BUSY | ADC Busy 0: ADC is idle, 1: ADC is busy |
3 | ASEN3 | ADC SS3 Enable 0: Sample Sequencer 3 is disabled, 1: Sample Sequencer 3 is enabled. |
2 | ASEN2 | ADC SS2 Enable 0: Sample Sequencer 2 is disabled, 1: Sample Sequencer 2 is enabled. |
1 | ASEN1 | ADC SS1 Enable 0: Sample Sequencer 1 is disabled, 1: Sample Sequencer 1 is enabled. |
0 | ASEN0 | ADC SS0 Enable 0: Sample Sequencer 0 is disabled, 1: Sample Sequencer 0 is enabled. |
Start Conversion trigger options
There are many start-conversion (trigger) options. Among them are using Timer, PWM, Analog Comparator, External signal from GPIO, and Software. The selection of Trigger for SS3 is done via the bits 15-12 of ADCEMUX register. The default is Software and that is what we use in this section.
EMx bits select the trigger source for Sample Sequencer x. By default the field is 0x0 which means the ADC conversion begins when the SSn bit of the ADCPSSI register is set by software. The following table shows the available choices for trigger.
EMx Value | Trigger source |
---|---|
0x0 | Processor (default) |
0x1 | Analog Comparator 0 |
0x2 | Analog Comparator 1 |
0x3 | reserved |
0x4 | External (GPIO Pins) |
0x5 | Timer |
0x6 | PWM Generator 0 |
0x7 | PWM Generator 1 |
0x8 | PWM Generator 2 |
0x9 | PWM Generator 3 |
0xF | Always (continuously sample) |
After we select the software option bit (which is the default) in the ADCEMUX, we must use bit D3 of ADCPSSI register to start a conversion every time we want a new reading from the ADC input channel.
SSx Value | Trigger source |
---|---|
0 | No Effect |
1 | Begin sampling on sample sequence x |
Notice that we can trigger the SS3 option only if we have enabled the SS3 in the ADCACTSS register.
Choosing Vin input channel
The channel selection is done through the ADCSSMUXn (n=0, 1, 2, 3) registers. For the SS3, the ADCSSMUX3 is used. Since SS3 only handles single conversion, bits 3 – 0 are used to specify the analog channel to be converted. The number of available channels in the TI Tiva TM4C123G varies among the family members. In the case of TI Tiva TM4C123GH6PM, there are 12 channels. They are designated as AIN0 (analog input 0) to AIN11 (analog input 11). Their designated pins are shown in Table 11.12.
Pin Name |
Description | Pin | Pin Number |
---|---|---|---|
AIN0 | ADC input 0 | PE3 | 6 |
AIN1 | ADC input 1 | PE2 | 7 |
AIN2 | ADC input 2 | PE1 | 8 |
AIN3 | ADC input 3 | PE0 | 9 |
AIN4 | ADC input 4 | PD3 | 64 |
AIN5 | ADC input 5 | PD2 | 63 |
AIN6 | ADC input 6 | PD1 | 62 |
AIN7 | ADC input 7 | PD0 | 61 |
AIN8 | ADC input 8 | PE5 | 60 |
AIN9 | ADC input 9 | PE4 | 59 |
AIN10 | ADC input 10 | PB4 | 58 |
AIN11 | ADC input 11 | PB5 | 57 |
bit | Name | Desription |
---|---|---|
0-3 | MUX0 | Sample Input Select |
Polling or interrupt
The end-of-conversion is indicated by a flag bit in the ADCRIS (ADC Raw Interrupt) register. Upon the completion of conversion for the SS3, the D3 bit (INR3) flag goes high. By polling this flag, we know if the conversion is complete and we can read the value in ADCSSFIFO3 register. We can also use an interrupt to inform us that the conversion is complete but that will require us to set up the interrupt mask ADCIM. By default, the interrupts are not enabled.
bit | Name | Discription |
---|---|---|
0 | INR0 | SSO Raw Interrupt Status 0:An interrupt has not occured 1: A sample has completed conversion and respective ADCSSCTL0 len bit is set, enabling a raw interrupt. Note: This bit is cleared by writing a 1 to the IN0 bit in the ADCISC register. |
1 | INR1 | SS1 Raw Interrupt Status |
2 | INR2 | SS2 Raw Interrupt Status |
3 | INR3 | SS3 Raw Interrupt Status |
16 | INRDC | Digital Comparator Raw Interrupt Status 0: All bits in the ADCDCISC register are clear. 1: At least one bit in the ADCDCISC register is set, meaning that a digital comparator interrupt has occurred. |
ADC Data result
Upon the completion of conversion, the binary result is placed in the ADCSSFIFOn register. Since we are using SS3, we need to read the result from ADCSSFIFO3 register. This is 32-bit register but only the lower 12 bits are used.
bit | Name | Description |
---|---|---|
0-11 | DATA | Conversion Result Data |
Clearing end-of-conversion flag
After reading the data from the ADCSSFIFOx register, we must clear the INR3 flag bit in ADCRIS register so that we may detect another conversion complete. The raw interrupt flag in ADCRIS is cleared by writing to ADCISC (ADC Interrupt Status and Clear) register. By writing a 1 to bit 3 (IN3 bit) of ADCISC, the interrupt flag is cleared and we can do another conversion again.
bit | Name | Description |
---|---|---|
0-3 | INx | SSx Interrupt Status and Clear 0: No Interrupt has occurred or the interrupt is masked 1: A sample has completed conversion (the INRx bit in the ADCRIS register is set) and the MASKx bit in the ADCIM register is set, providing an interrupt to the interrupt controller. Note: This bit is cleared by writing a 1. Clearing the bit also clears the INRx bit in the ADCRIS register. |
16 to 19 |
DCINSSx | Digital Comparator Interrupt Status on SSx 0:No Interrupt has occurred or the interrupt is masked 1: Both the INRDC bit in the ADCRIS register and the DCONSSx bit in the ADCIM register are set providing an interrupt to the interrupt controller. Note: This bit is cleared by writing a 1. Clearing the bit also clears the INRDC bit in the ADCRIS register. |
Differential versus Single-Ended
In some applications, our interest is in the differences between two analog signal voltages (the differential voltages). Rather than converting two channels and calculate the differences between them, the Tiva TM4C123G has the option of converting the differential voltages of two analog channels. The bit 0 of ADCSSCTL3 (ADC Sample sequence Control 3) register allows us to enable the differential option. Upon Reset, the default is the single-ended input. The pairing of the analog inputs for differential is hardwired. Table 11.13 shows the pairing of the ADC input channels for differential option.
Differential Pair | Analog Inputs |
---|---|
0 | 0 and 1 |
1 | 2 and 3 |
2 | 4 and 5 |
3 | 6 and 7 |
4 | 8 and 9 |
5 | 10 and 11 |
The other bits in ADCSSCTL3 register are bit 1 (END0), bit 2 (IE0), and bit 3 (TS) bits. On some of the ADC inputs, we have an internal temperature sensor embedded into the chip. Making bit 3 = 1, reads the temperature sensor value inside the chip itself. Since we are using one sample in the SSE3, we must enable the bit 1 (END0) to let ADC know that first sample is the only sample and there is no sample coming after that. Bit 2 (IE0) is the Interrupt enable. It causes the raw interrupt flag to set when this sample conversion is completed. However, in order to redirect the end-of-conversion to the NVIC interrupt controller, we must also enable the bit in ADCIM register, as we will see soon. It must be noted, even if we do not want to use interrupt, we must still set IE0 = 1, in order to post the raw interrupt flag for polling the conversion complete.
bit | Name | Description |
---|---|---|
0 | D0 | Sample Differential Input Select 0: The analog inputs are not differentially sampled 1: The analog inputs are differentially sampled. The corresponding ADCSSMUX nibble must be set to the pair number "i", where the paired inputs are 2i and 2i+1. Note: Because the temperature sensor doesn't have a differential option this bit must not be set when the TS0 bit is set. |
1 | END0 | End of Sequence: This bit must be set before initiating a single sample sequence. 0: Sampling and conversion continues 1: This is the end of the sequence. |
2 | IE0 | Sample Interrupt Enable 0: The raw interrupt is not asserted to the interrupt controller 1: The raw interrupt signal (INR0 bit) is asserted at the end of this sample's conversion. If the MASK0 bit in the ADCIM register is set, the interrupt is promoted to the interrupt controller. It is legal to have multiple samples with in a sequence generate interrupts. |
3 | TS0 | 1st Sample Temperature Sensor Select 0: The input pin specified by the ADCSSMUXn register is read during the first sample of the sample sequence. 1: The temperature sensor is read during the first sample sequence. |
Masking interrupt for SS3
Since we are using polling for the end-of-conversion, we must mask the interrupt option for the SS3 to prevent it from interrupting us via NVIC. This is done with bit 3 of ADCIM (ADC Interrupt Mask) register. Upon Reset, the default for the bit 3 (MASK3) is 0. With the MASK3 = 0, it disables the interrupt and we will leave it like that. However, if we like to handle end-of-conversion by interrupt, we need to set this bit to 1 and write an interrupt handler to read the conversion result.
bit | Name | Description |
---|---|---|
0-3 | MASKx | SSx Interrupt Mask 0: The Status of Sample Sequencer x doesn't affect the SSx Interrupt Status. 1. The raw interrupt signal from Sample Sequencer x (ADCRIS register INRx bit) is sent to the interrupt controller. |
16 to 19 |
DCONSSx | Digital Comparator Interrupt on SSx 0: The status of the digital comparators doesn't affect the SSx interrupt status. 1: The raw Interrupt signal from digital comparators (INRDC bit in the ADCRIS register) is sent to the interrupt controller on the SSx interrupt line. |
Vref in Tiva LaunchPad
In the TI ARM Tiva chip series, the pin for Vref (+) is called VDDA (VDD analog) and Vref (-) pin is called GNDA (Ground Analog). In the TI Tiva LaunchPad, the VDDA pin is connected to 3.3V, the same supply voltage as the digital part of the chip.
Even if we connect the VDDA to a separate power source other than the VDD of the chip, it cannot go beyond the VDD voltage. With Vref=3.3V, we have the step size of 3.3V / 4096 = 0.8057 mV since the ADC resolution is 12 bits.
Operational Procedure for ADC
Each ADC module contains four sample sequencers, SS0 - SS3. Each sample can be obtained from different input sources (different channels). These sample sequencers are under the control of some control registers in the Control/Status block. The operational procedure of each ADC module includes:
- Each ADC module must be clocked by configuring the ADC Clock Configuration (ADCCC) register before it works since all ADC modules share the same clock source to facilitate the synchronization of data samples between conversion units.
- Before the ADC can start its conversion, the sample sequencers must be configured by related registers in each sample sequencer block, such as ADC Sample Sequence Input Multiplexer Select n (ADCSSMUXn) and ADC Sample Sequence Control n (ADCSSCTLn) registers.
- Then the sample sequencers must be enabled by configuring the ADC Active Sample Sequencer (ADCACTSS) register. If multiple triggering events were used, the ADC Sample Sequencer Priority (ADCSSPRI) register must also be configured.
- The trigger source or trigger event must be determined by configuring the ADC Event Multiplexer Select (ADCEMUX) register, and the sampling process must be initiated by setting up the ADC Processor Sample Sequence Initiate (ADCPSSI) register.
- Optionally, one can use the ADC Peripheral Property (ADCPP) register to set up the resolution, include the temperature sensor, and set the ADC sample rate. Generally, you do not need to touch this register and only use the default settings of this register.
- The ADC Trigger Source Select (ADCTSSEL) register is used to select a PWM generator as the trigger source when the PWM generator is used.
- After the analog input channel and sample property have been determined by ADCSSMUXn, ADCSSCTLn registers, the ADC conversion starts. After the ADC conversion is complete, the conversion results can be obtained from the ADC Sample Sequence Result FIFO (ADCSSFIFOn) registers.
- If interrupts are used for any ADC conversion, some registers in the Interrupt Control block should also be configured.
The Hardware Averager is used to make the ADC conversion results smoother by averaging some continuous samples. The Digital Comparator is used to compare the ADC conversion results with some predefined values to monitor and control the external signals.
Using ADC0 to convert input from AIN0
Configuring GPIO for ADC input
In using ADC, we must also configure the GPIO pins to allow the connection of an analog signal through the input pin. In this regard, it is the same as all other peripherals. The steps are as follows:
- Enable the clock to GPIO pin by using RCGCGPIO.
- Set the GPIOAFSEL (GPIO alternate function) bit for ADC input pin to 1.
- Configure AINx signal to be used as analog input by clearing the bit in the GPIODEN (GPIO Digital enable) register.
- Disable the Analog isolation circuit for ADC input pins by writing a 1 to the GPIOAMSEL register.
Configuring ADC and reading ADC channel
After the GPIO configuration, we need to take the following steps to configure the ADC for Sample Sequencer 3 (SS3):
- Enable the clock to ADC0 or ADC1 modules using RCGCADC register of System Registers.
- The SYSCTL_RCGCADC_R |= 3 will enable the clock to both ADC0 and ADC1 modules. For ADC0 module use SYSCTL_RCGCADC_R |= 1
- Disable the Sample Sequencer using the ADCACTSS register before changing the configuration of the sequencer.
- ADC0_ACTSS_R &= ~8 disables the SS3.
- Choose the software trigger using the ADCEMUX register.
- Use ADC0_EMUX_R &= ~0xF000 for software trigger.
- Select the ADC input channel using the ADCSSMUXn register. In the case of SS3 we use the ADCSSMUX3.
- For example, The ADC0_SSMUX3_R = 0 selects AIN0 channel on pin PE3.
- Select the single-ended option, one-conversion per sample, and raw interrupt bit for end-of-conversion using the ADCSSCTL3 register.
- Use ADC0_SSCTL3_R |= 6 for single-ended, one-conversion, and raw interrupt.
- Enable the Sample Sequencer SS3 using ADCACTSS register.
- ADC0_ACTSS_R |= 8 enables the SS3.
- Use ADCPSSI register to start a new conversion.
- Use ADC0_PSSI_R |= 8 to start a conversion by Sample Sequencer 3.
- Keep monitoring the end-of-conversion INR3 flag in ADCRIS (ADC0_RIS_R) register.
- When the INR3 goes HIGH, read the ADC result from the ADCSSFIFO3 (ADC0_SSFIFO3_R) and save it.
- After reading the ADC result in step 9, clear the INR3 flag in ADCRIS register to allow for the next conversion.
- To clear INR3 flag, write to ADCICS register with ADC0_ISC_R = 8.
- Repeat steps 7 through 10 for the next conversion.
Source Code
/* A to D conversion
*
* This program converts the analog input from AIN0 (J3.9 of LaunchPad)
* using sample sequencer 3 and software trigger continuously.
*
* Note: AIN0 channel is on PE3 pin.
*
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
int main(void)
{
volatile int result;
/* enable clocks */
SYSCTL_RCGCGPIO_R |= 0x10; /* enable clock to PE (AIN0 is on PE3) */
SYSCTL_RCGCADC_R |= 1; /* enable clock to ADC0 */
/* initialize PE3 for AIN0 input */
GPIO_PORTE_AFSEL_R |= 8; /* enable alternate function */
GPIO_PORTE_DEN_R &= ~8; /* disable digital function */
GPIO_PORTE_AMSEL_R |= 8; /* enable analog function */
/* initialize ADC0 */
ADC0_ACTSS_R &= ~8; /* disable SS3 during configuration */
ADC0_EMUX_R &= ~0xF000; /* software trigger conversion */
ADC0_SSMUX3_R = 0; /* get input from channel 0 */
ADC0_SSCTL3_R |= 6; /* take one sample at a time, set flag at 1st sample */
ADC0_ACTSS_R |= 8; /* enable ADC0 sequencer 3 */
while(1) {
ADC0_PSSI_R |= 8; /* start a conversion sequence 3 */
while((ADC0_RIS_R & 8) == 0)
; /* wait for conversion complete */
result = ADC0_SSFIFO3_R; /* read conversion result */
ADC0_ISC_R = 8; /* clear completion flag */
}
}
Note
- Short 3 & 4 of J7 on the EduARM4 Trainer Board
- To change the input voltage to the ADC, Vary the RV2 potentiometer.
Timer Trigger ADC conversion
For digital signal processing, not only the precision of the ADC conversion result is important, the precision of the time that the analog input is sampled is also important. Using software trigger conversion does not yield precision timing especially when there are other software tasks running at the same time. One way of getting precision sampling timing interval is to use the timer to trigger the conversion. To do so:
- Configure the ADC to use timer trigger
- The four bits of ADCEMUX register for the sample sequencer used should be loaded with the value of 5 for timer trigger.
- Configure a timer to generate periodic timeout
- The periodic timer function is covered here.
- Connect timer to ADC trigger.
- To connect the timer to ADC trigger, the TAOTE or TBOTE bit of the GPTMCTL should be set to 1.
The most accurate sampling method is timer-triggered sampling (EM3=0x5). On the TM4C123, the MUX fields are 4 bits wide, allowing us to specify channels 0 to 11. The following are the steps to configure the ADC to sample a single channel at a periodic rate:
- Enable the ADC clock in the SYSCTL_RCGCADC_R register.
- Bits 3 – 0 of the ADC0_PC_R register specify the maximum sampling rate of the ADC. Set the maximum sampling rate at 125 kHz. This will require less power and produce a longer sampling time, creating a more accurate conversion.
- Set the priority of each of the four sequencers. Use just one sequencer, so the priorities are irrelevant, except for the fact that no two sequencers should have the same priority. The default configuration has Sample Sequencer 0 with the highest priority, and Sample Sequencer 3 as the lowest priority.
- Configure the timer to run at the desired sampling frequency. Enable the Timer0 clock by setting bit 0 of the SYSCTL_RCGCTIMER_R register. First set bit 5 of the TIMER0_CTL_R register to activate TAOTE, which is the Timer A output trigger enable. Secondly, do not arm any Timer0 interrupts. The rate at which the timer rolls over determines the sampling frequency. Let prescale be the value loaded into TIMER0_TAPR_R, and let period be the value loaded into TIMER0_TAILR_R. If the period of the bus clock frequency is t, then the ADC sampling period will be t *(prescale + 1)*(period + 1). The fastest sampling rate is determined by the speed of the processor handling the ADC interrupts and by the speed of the main program consuming the data from the FIFO. If the bus clock is 80 MHz, the slowest possible sampling rate for this example is 80MHz/232, which is about 0.018 Hz, which is every 55.56 seconds.
- Before configuring the sequencer, we need to disable it. To disable sequencer 3, we write a 0 to bit 3 (ASEN3) in the ADC0_ACTSS_R register. Disabling the sequencer during programming prevents erroneous execution if a trigger event were to occur during the configuration process.
- Configure the trigger event for the sample sequencer in the ADC0_EMUX_R register. For this example, we write a 0101 to bits 15–12 (EM3) specifying timer trigger mode for sequencer 3.
- For each sample in the sample sequence, configure the corresponding input source in the ADC0_SSMUXn register. In this example, we write the channel number (0, 1, 2, or 3) to bits 3–0 in the ADC0_SSMUX3_R register.
- For each sample in the sample sequence, Configure the sample control bits in the corresponding nibble in the ADC0_SSCTLn register. When programming the last nibble, ensure that the END bit is set. Failure to set the END bit causes unpredictable behavior. Sequencer 3 has only one sample, so we write a 0110 to the ADC0_SSCTL3_R register. Bit 3 is the TS0 bit, which we clear because we are not measuring temperature. Bit 2 is the IE0 bit, which we set because we want to request an interrupt when the sample is complete. Bit 1 is the END0 bit, which is set because this is the last (and only) sample in the sequence. Bit 0 is the D0 bit, which we clear because we do not wish to use differential mode.
- If interrupts are to be used, write a 1 to the corresponding mask bit in the ADC0_IM_R register. We want an interrupt to occur when the conversion is complete (set bit 3, MASK3).
- We enable the sample sequencer logic by writing a 1 to the corresponding ASENn. To enable sequencer 3, we write a 1 to bit 3 (ASEN3) in the ADC0_ACTSS_R register.
- The priority of the ADC0 sequencer 3 interrupts are in bits 13–15 of the NVIC_PRI4_R register.
- Since we are requesting interrupts, we need to enable interrupts in the NVIC. ADC sequencer 3 interrupts are enabled by setting bit 17 in the NVIC_EN0_R register.
- Lastly, we must enable interrupts in the PRIMASK register.
The timer starts the conversion at a regular rate. Bit 3 (INR3) in the ADC0_RIS_R register will be set when the conversion is done. This bit is armed and enabled for interrupting, so conversion complete will trigger an interrupt. The IN3 bit in the ADC0_ISC_R register triggers the interrupt. The ISR acknowledges the interrupt by writing a 1 to bit 3 (IN3). The 12-bit result is read from the ADC0_SSFIFO3_R register.
Temperature sensor
There is an internal temperature sensor embedded into the TI ARM Tiva chip. This gives us the temperature inside the ARM chip itself. The temperature sensor does not have any separate enable control, because it also contains the band-gap reference and must always be enabled. The reference is also supplied to other analog modules, not just the ADC. In addition, the temperature sensor has a second power-down input in the 3.3V domain which provides control by the Hibernation module.
The internal temperature sensor converts a temperature measurement into a voltage. This voltage value, VTSENS, is given by the following equation (where TEMP is the temperature in °C):
VTSENS = 2.7 - ((TEMP + 55)/75)
The temperature sensor reading can be sampled in a sample sequencer by setting the TSn bit in the ADCSSCTLn register. The temperature reading from the temperature sensor can also be given as a function of the ADC value. The following formula calculates temperature (TEMP in °C) based on the ADC reading that is an ADC_output given as an unsigned decimal number from 0 to 4095 and the maximum ADC voltage range (Vref(+) Vref(–)):
Temp = 147.5 – ((75 × Vref(+) – Vref(–)) × ADC_output)) / 4096
Since the Vref = 3.3 V in TI Tiva Launchpad, we have:
Temp = 147.5 – ((75 × 3.3V) × ADC_output) / 4096 Temp = 147.5 – (247.5 × ADC_output) / 4096
Example: Find the temperature inside the ARM chip for the TI Tiva Launchpad if the reading of ADC output is = 0x7D0.
Solution:
The 0x7D0 is 2000 in decimal. Now, we have
Temp = 147.5 – (247.5x2000) / 4096 = 147.5 – 120.85 = 26.6 Celsius
Source Code
/* Convert on-chip temperature
*
* Runs on TM4C123 LaunchPad
*
* This program converts the on-chip temperature sensor output using
* sample sequencer 3 and timer trigger at 1 Hz.
*
*/
#include <stdint.h>
#include "inc/tm4c123gh6pm.h"
int main(void)
{
volatile int temperature;
/* enable clocks */
SYSCTL_RCGCADC_R |= 1; /* enable clock to ADC0 */
SYSCTL_RCGCWTIMER_R |= 1; /* enable clock to WTimer Block 0 */
/* initialize ADC0 */
ADC0_ACTSS_R &= ~8; /* disable SS3 during configuration */
ADC0_EMUX_R &= ~0xF000;
ADC0_EMUX_R |= 0x5000; /* timer trigger conversion seq 0 */
ADC0_SSMUX3_R = 0; /* get input from channel 0 */
ADC0_SSCTL3_R |= 0x0E; /* take chip temperature, set flag at 1st sample */
ADC0_ACTSS_R |= 8; /* enable ADC0 sequencer 3 */
/* initialize wtimer 0 to trigger ADC at 1 sample/sec */
WTIMER0_CTL_R = 0; /* disable WTimer before initialization */
WTIMER0_CFG_R = 0x04; /* 32-bit option */
WTIMER0_TAMR_R = 0x02; /* periodic mode and down-counter */
WTIMER0_TAILR_R = 16000000; /* WTimer A interval load value reg (1 s) */
WTIMER0_CTL_R |= 0x20; /* timer triggers ADC */
WTIMER0_CTL_R |= 0x01; /* enable WTimer A after initialization */
while(1) {
while((ADC0_RIS_R & 8) == 0)
; /* wait for conversion complete */
temperature = 147 - (247 * ADC0_SSFIFO3_R) / 4096;
ADC0_ISC_R = 8; /* clear completion flag */
}
}
- Write software to sample a single channel at a periodic rate
- Set the maximum sampling rate at 125K samples/sec
- Write software to sample ADC channels 4 and 5 at 1 Khz
- Channel 4 on the TM4C123 is PD3 and channel 5 is PD2.