HomeTM4C123G TutorialsHow to Use ADC on TM4C123G LaunchPad: Complete Step-By-Step Guide

TM4C123G ADC Tutorial: Simple Guide to Configure and Read ADC Values

Working with analog sensors is one of the first steps when learning embedded systems. The TM4C123G LaunchPad makes this task simple with its powerful 12-bit ADC module. Whether you want to read a potentiometer, monitor temperature, or measure light intensity, the ADC lets your microcontroller understand real-world analog signals and convert them into digital values.

In this guide, we will walk through the complete process of using the ADC on the TM4C123G. You will learn how the ADC works, how to choose the right input pin, and how to configure the ADC module step-by-step. We will write clean and simple C code, explain each line in small sections, and make the output visible on the serial monitor.

The goal of this tutorial is to keep things simple and clear. Every section is short. Every explanation is easy to understand. Even beginners will be able to follow the steps without confusion.

By the end of this tutorial, you will know how to:

  • Initialize the ADC peripheral correctly
  • Read ADC samples from any analog pin
  • Convert the ADC reading into a real voltage
  • Display the result on the serial monitor through UART

This makes the TM4C123G ADC perfect for projects like sensor data logging, real-time monitoring, and learning embedded programming.

TM4C123G ADC Tutorial: Simple Guide to Configure and Read ADC Values

Understanding the ADC on TM4C123G

Before writing any code, it is important to understand how the ADC block works on the TM4C123G microcontroller. This section explains the basic idea behind ADCs, the features offered by the TM4C123G, and the key concepts you must know before initialization. With this knowledge, setting up and using the ADC becomes much easier.

What Is ADC and Why We Use It

ADC stands for Analog-to-Digital Converter. It converts real-world analog signals, such as voltage from sensors, into digital numbers that the microcontroller can process.

For example:

  • A potentiometer outputs a varying voltage.
  • An LDR changes resistance with light.
  • A temperature sensor changes voltage based on temperature.

The microcontroller cannot understand these continuous analog values directly. The ADC takes the analog input and converts it into a number between 0 and 4095 (for 12-bit resolution).
This digital number represents the voltage level.


Key ADC Features in TM4C123G

The TM4C123G LaunchPad includes a powerful ADC module with many useful features.
Here are the most important ones:

  • Two independent ADC modules (ADC0 and ADC1)
  • 12-bit resolution, giving values from 0 to 4095
  • Up to 1 million samples per second (1 MSPS)
  • Four sequencers (SS0–SS3) for flexible sampling
  • Support for single-ended and differential inputs
  • Hardware oversampling for stable readings
  • Up to 12 analog input channels (AIN0–AIN11)
  • Programmable trigger sources (processor, timer, PWM, comparator, GPIO, etc.)

These features make the ADC very flexible. You can read a single sample, multiple samples, or even trigger conversions automatically using timers or interrupts.


ADC Clock, Sampling Speed and Resolution

The ADC module needs a stable clock to work correctly. On the TM4C123G, the ADC clock typically comes from the PLL or the system clock.

A few important points:

  • The ADC operates best when its internal clock is 125 kHz to 16 MHz.
  • The TM4C123G can sample up to 1 MSPS (1 million samples per second).
  • It uses a 12-bit resolution, which means:
    • 0 = 0V
    • 4095 = maximum reference voltage (usually 3.3V)

The formula to convert ADC reading to voltage is:

Voltage = (ADC_Value / 4095.0) * 3.3

The higher the resolution, the more precise the reading. With 12 bits, you get 4096 possible values (2^12), giving good accuracy for sensor applications.


Understanding ADC Sequencers (SS0–SS3)

The ADC on the TM4C123G uses four sequencers. Each sequencer decides how many samples to take and from which channels.

Here is the summary:

SequencerNumber of SamplesPriorityCommon Use
SS0Up to 8 samples / stepsLowestMulti-sensor sampling
SS1Up to 4 samples / stepsMediumSmall sensor arrays
SS2Up to 4 samples / stepsMediumRegular ADC tasks
SS31 sample / stepHighestSimple, single-channel sampling

SS3 is the most commonly used sequencer because:

  • It is simple
  • It takes only one sample
  • It has the highest priority
  • Ideal for reading a single sensor like a potentiometer

Each sequencer can be triggered by:

  • Software (processor)
  • Timers
  • PWM
  • GPIO events
  • Comparators

This makes the ADC module very flexible and powerful.

Hardware Setup for TM4C123G ADC

Before writing any code, we must prepare the hardware. The TM4C123G LaunchPad does not include a built-in potentiometer, so you must use an external analog sensor or an external potentiometer. This section explains the required components, supported ADC pins, and how to wire them safely.

Using External Potentiometer or Analog Sensors

Since the TM4C123G LaunchPad has no onboard potentiometer, you need an external analog input source. A simple 10k potentiometer is the best option for testing, because it provides a clean and adjustable voltage between 0 and 3.3V.

You can also use other analog sensors, such as:

  • LM35 temperature sensor
  • Light sensor (LDR with a voltage divider)
  • Soil moisture sensor
  • Analog joystick
  • Any sensor with a 0–3.3V output

These devices make it easy to test how the ADC reads varying voltages.


ADC Input Pins and Voltage Limits

The TM4C123G provides 12 analog channels, mapped to specific GPIO pins. Some commonly used pins include:

ADC ChannelGPIO Pin
AIN0PE3
AIN1PE2
AIN2PE1
AIN3PE0
AIN8PE5
AIN9PE4
AIN10PB4
AIN11PB5

The most convenient pins for beginners are PE3 (AIN0) and PE4 (AIN9) because they are easy to access on the LaunchPad header.

Voltage Limits (Important)

The ADC input must stay within:

  • 0V minimum
  • 3.3V maximum

Applying more than 3.3V can permanently damage the pin. If your sensor outputs 5V, use a voltage divider or level shifter.


Wiring the Sensor to the ADC Pin

To test the ADC with a potentiometer, connect it as shown in the image below.

Image showing potentiometer connected to pin PE3 of TM4C123G.

This setup works with any analog sensor that outputs 0–3.3V. The table below explains the pin connections.

Potentiometer PinConnect ToDescription
Left PinGNDSets the lower voltage reference (0V)
Right Pin3.3VSets the upper voltage reference (maximum ADC voltage)
Middle PinADC Input Pin (e.g., PE3 / AIN0)Outputs a variable voltage based on knob position

Setting up a new project in CCS

I have already explained how to create a new project in TM4C123 using CCStudio. You can create a similar project with blinky template and then modify it for this tutorial.

There are only 2 things we need to do to turn the blinky template into a new project:

  1. Delete everything from the blinky.c file, so it should be completely empty. We will write everything ourselves.
  2. Rename the following:
    • blinky.c → main.c, indicating that this is the main file for the project
    • blinky_css.cmd → linker.cmd, indicating that this is the linker file
    • blinky (Folder) → projectName (Folder)

The final modified project is shown in the image below.

Creating an Empty project in TI Launchpad CCStudio

After creating the project, add basic_conf.c and basic_conf.h files in the project directory. These files will configure the following:

Adding library files in CCStudio basic project for TIVA C.
  • Add the #include "basic_conf.h" to include the basic configuration library file and #include "driverlib/interrupt.h" to include interrupt functions in the main file.
  • Inside the main function, call systemClockConfig() to configure the PLL to run the system at 80MHz clock.
  • After everything else is initialized, call IntMasterEnable() to enable the master interrupt. This is needed for the SysTick based Delay to work.

Step-By-Step ADC Initialization in TM4C123G

This section explains every ADC configuration step in simple words.
You will learn how to:

  • Enable ADC and GPIO clocks
  • Configure a GPIO pin for analog input
  • Choose an ADC sequencer
  • Select the trigger source
  • Set the channel and sample control
  • Finalize the ADC setup

All steps use TivaWare functions, making the code cleaner and easier to understand.

Enabling ADC and GPIO Peripheral Clock

Before using ADC, you must enable its clock and the clock for the GPIO port where your analog input pin is located. For example, if you want to read from PE3 (AIN0), both ADC0 and GPIOE must be enabled.

Below is the Code to enable ADC0 and GPIOE Clocks.

// Enable ADC0 and GPIOE clocks
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

// Wait until peripherals are ready
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE));

Configuring the ADC Input Pin as Analog

ADC pins must be configured as analog inputs.
This means:

  • Disable digital function
  • Enable analog mode
  • Keep alternate functions disabled (TivaWare handles this automatically)

Below is the code to configure PE3 as ADC Input

// Configure PE3 as an ADC input (AIN0)
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);

TivaWare internally handles analog mode and disables digital functionality.


Selecting ADC Sequencer and Trigger Source

TM4C123G has four sequencers (SS0–SS3).
For simple single-channel sampling, Sequencer 3 (SS3) is best because:

  • It samples only one channel
  • It is easy to configure
  • Good for beginners

Trigger source:
We use processor trigger, meaning the software will start sampling.

Below is the code to configure the ADC sequencer, trigger source and priority.

// Disable SS3 before configuration
ADCSequenceDisable(ADC0_BASE, 3);

// Configure SS3 for processor trigger, highest priority
ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);

Setting Sample Control and Input Channel

Now we tell the ADC:

  • Which channel to read (AIN0 : PE3)
  • Whether this is the end of the sequence
  • Whether to generate an interrupt (optional)

Below is the code to set the sample control and ADC channel.

// Configure SS3 step 0 to read AIN0
// ADC_CTL_END marks the end of the sequence
ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_END);

If you want interrupts later, you may add ADC_CTL_IE.


Finalizing the ADC Setup

Once the sequencer, steps, and channel are set:

  • Enable the sequencer
  • ADC is ready to sample

Below is the code to enable the sequencer.

ADCSequenceEnable(ADC0_BASE, 3);   // Enable SS3

ADC is now fully configured.


Final Combined ADC Initialization Code

Here is the complete setup code to initialize the ADC0 Channel 0, combining all sections:

void ADC0_Init(void)
{
    //
    // 1. Enable ADC0 and GPIOE clocks
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE));

    //
    // 2. Configure PE3 (AIN0) as analog input
    //
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);

    //
    // 3. Configure ADC0 Sequencer 3 for processor trigger
    //
    ADCSequenceDisable(ADC0_BASE, 3);  // Disable SS3 before config
    ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);

    //
    // 4. Configure step 0 of SS3 to sample AIN0
    //
    ADCSequenceStepConfigure(ADC0_BASE, 3, 0,
                             ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);

    //
    // 5. Enable SS3
    //
    ADCSequenceEnable(ADC0_BASE, 3);
}

Writing the ADC Driver Code

Now that the ADC is initialized, the next step is to write modular code to read values and convert them to real voltages. We will break it into simple functions so beginners can easily understand and reuse them in their projects.

Creating ADC Read Function

The simplest way to read ADC data is using polling. This function will trigger a conversion, wait until it completes, and return the ADC result.

uint32_t ADC0_Read(void)
{
    uint32_t value;

    // Trigger SS3 conversion
    ADCProcessorTrigger(ADC0_BASE, 3);

    // Read the ADC value
    ADCSequenceDataGet(ADC0_BASE, 3, &value);

    return value;
}

Explanation

  1. uint32_t value;
    • Declares a variable to store the ADC result.
    • uint32_t ensures it can hold all 12-bit values (0–4095).
  2. ADCProcessorTrigger(ADC0_BASE, 3);
    • Starts the ADC conversion manually using Processor Trigger.
    • ADC0_BASE tells the function which ADC module to use.
    • 3 is the sequencer number (SS3) we configured earlier.
  3. ADCSequenceDataGet(ADC0_BASE, 3, &value);
    • Reads the ADC result from the sequencer.
    • Stores the raw ADC value (0–4095) in value.
  4. return value;
    • Returns the raw ADC reading to the caller.
    • Can be used for averaging, conversion to voltage, or display.

Handling Multiple Samples

If you want smoother readings or averaging, you can read the ADC multiple times and calculate the average.

Below is the code to read multiple samples and then averaging the result.

uint32_t ADC0_ReadAverage(uint8_t samples)
{
    uint32_t sum = 0;
    for(uint8_t i = 0; i < samples; i++)
    {
        sum += ADC0_Read();  // Use single-read function
    }
    return sum / samples;
}

Explanation:

  1. uint32_t sum = 0;
    • Variable to accumulate the ADC readings.
  2. for(uint8_t i = 0; i < samples; i++)
    • Loop runs samples times. Example: 10 readings.
  3. sum += ADC0_Read();
    • Reads ADC once using the function above and adds it to the sum.
  4. return sum / samples;
    • Calculates the average by dividing total sum by number of samples.
    • Reduces noise and stabilizes the reading.

Why use averaging?

  • ADC readings may have small fluctuations due to electrical noise.
  • Averaging multiple readings gives smoother, more reliable data.

Converting ADC Value to Voltage

The ADC gives a raw number (0–4095 for 12-bit).
We can convert it to a voltage using the formula:

float ADC0_ToVoltage(uint32_t adc_value)
{
    return (adc_value * 3.3f) / 4095.0f;
}

Explanation

  1. adc_value
    • The raw 12-bit ADC reading (0–4095).
  2. 3.3f
    • Reference voltage of the microcontroller.
    • The voltage corresponding to the maximum ADC reading (4095).
  3. 4095.0f
    • Maximum ADC value for 12-bit resolution.
  4. (adc_value * 3.3f) / 4095.0f;
    • Converts the ADC value into a voltage between 0V and 3.3V.

Example:

  • ADC reading = 2048 -> Voltage = 1.65V

Sending ADC Output to Serial Monitor

Once the ADC reads a sensor value, we can send it to a PC using UART. This allows us to monitor raw ADC values and converted voltages in real time. The TM4C123G LaunchPad uses UART0 connected to the on-board debugger (Virtual COM Port), so no extra hardware is needed.

Initializing UART0 Initialization for Debug printf

UART0 is used in this project to print debug messages. This helps us confirm whether the project is working correctly. The UART0 is connected to the Virtual COM Port (VCP) through the on-board debugger, so no external hardware is required to view the output.

You can check more information about UART on the Tiva C LaunchPad by visiting the following link:
Use UART VCP in TiVa C.

The function below sets up UART0 on pins PA0 (RX) and PA1 (TX) at a baud rate of 115200.

#include "driverlib/uart.h"
#include "utils/uartstdio.h"
void UART0_Init(void)
{
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
    UARTStdioConfig(0, 115200, 16000000);
}

Explanation

  • Enable UART0 and GPIOA clocks.
    Required before configuring any pins or UART registers.
  • Configure PA0 and PA1 for UART.
    PA0: U0RX, PA1: U0TX using alternate pin functions.
  • Set pin type to UART.
    Activates UART electrical characteristics on these pins.
  • Select UART clock source.
    Uses the 16 MHz PIOSC for stable baud rate generation.
  • Configure UARTStdio.
    Sets baud rate to 115200 and prepares UARTprintf() for debugging.
Important Note

To use UARTprintf() and UARTStdioConfig(), you must add the file to your project:

TivaWare/utils/uartstdio.c

Create a folder named utils inside your project and copy uartstdio.c into it.

add the uartstudio.c file to the project. This is needed for uartPrintf to work.

Without this file, the linker will give errors for undefined symbols.


Printing ADC Values Continuously

Once UART is ready, we can print ADC values in a loop. We can use either single readings or averaged readings for stability.

Code Example

uint16_t adc_value;
float voltage;
char vol_array[6];

while(1)
{
    // Read ADC with averaging
    adc_value = ADC0_ReadAverage(10);
    voltage = ADC0_ToVoltage(adc_value);

    // Print raw ADC value and voltage to serial monitor
    float_to_string(voltage, vol_array, 2);  // convert float value to string form
    UARTprintf("ADC Value: %u, Voltage: %s V\n", adc_value, vol_array);
    delay_ms(100);
}

Explanation

  • ADC0_ReadAverage(10) reads ADC 10 times and averages to reduce noise.
  • ADC0_ToVoltage(adc_value) converts the raw value to real voltage.
  • float_to_string(voltage, vol_array, 2) converts the float voltage value to string format with a precision of 2 decimal places. The string is stored in the vol_array.
  • UARTprintf() prints both raw ADC value and the string containing the float voltage value.

This loop runs continuously, allowing real-time monitoring of sensor readings on your PC.


Checking Raw vs Converted Voltage Output

By printing both raw ADC numbers and calculated voltages, you can:

  • Verify your ADC is working correctly
  • Check sensor response
  • Compare voltage changes with sensor input (e.g., rotating a potentiometer)

Example Serial Output:

ADC Value: 2048, Voltage: 1.65 V
ADC Value: 3072, Voltage: 2.48 V
ADC Value: 1024, Voltage: 0.82 V
  • 0 -> 0V
  • 4095 -> 3.3V
  • Values in between scale proportionally

This ensures your ADC reading and voltage conversion formulas are correct.

Complete TM4C123G ADC Project with UART Output

This section brings everything together. We will read an analog voltage from a sensor (like a potentiometer), convert it to a voltage, and print it continuously to the serial monitor using UART0. This makes it easy to visualize sensor readings on your PC in real-time.

uint16_t adc_value;
float voltage;
char vol_array[6];

int main(void)
{
    systemClockConfig();
    UART0_Init();
    ADC0_Init();
    IntMasterEnable();
    while(1)
    {
        // Read ADC with averaging
        adc_value = ADC0_ReadAverage(10);
        voltage = ADC0_ToVoltage(adc_value);

        // Print raw ADC value and voltage to serial monitor
        float_to_string(voltage, vol_array, 2);
        UARTprintf("ADC Value: %u, Voltage: %s V\n", adc_value, vol_array);
        delay_ms(100);
    }
}

Explanation of the Code

  1. ADC Initialization (ADC0_Init)
    • Enables ADC0 and GPIOE clocks.
    • Configures PE3 (AIN0) as analog input.
    • Sets up Sequencer 3 for processor-triggered single-sample conversions.
  2. ADC Reading Functions (ADC0_Read and ADC0_ReadAverage)
    • ADC0_Read() triggers a conversion, waits for completion, and returns the raw value.
    • ADC0_ReadAverage() reads multiple samples and averages them for smoother readings.
  3. Voltage Conversion (ADC0_ToVoltage)
    • Converts raw 12-bit ADC values (0–4095) to voltage (0–3.3V).
  4. UART Initialization (UART0_Init)
    • Sets up UART0 on PA0/PA1 at 115200 baud.
    • Uses UARTstdio so you can print formatted strings via UARTprintf().
  5. Main Loop
    • Continuously reads the ADC, converts it to voltage, and prints both raw and converted values.
    • Runs infinitely to provide real-time monitoring.

Output on Serial Console

The video below shows the TM4C123G ADC in action with a potentiometer as input:

As the video shows:

  • When the potentiometer is rotated, the raw ADC value changes smoothly between 0 and 4095.
  • The converted voltage output corresponds to the position of the potentiometer, from 0V to 3.3V.
  • This demonstrates that the ADC is correctly sampling and the UART prints the readings in real-time.

Testing and Verifying ADC Results

Once the ADC is configured, the next step is to validate the readings and ensure the output responds correctly to the input voltage. This involves observing changes from a potentiometer, detecting noise or jitter, and addressing common issues that might affect accuracy.

Varying the Potentiometer and Checking Output

  1. Connect the potentiometer
    • One outer pin → 3.3V
    • Other outer pin → GND
    • Middle pin (wiper) → AINx (e.g., PE3/AIN0)
  2. Run your ADC print loop
    As you rotate the potentiometer:
    • At 0 V, ADC should read ~0
    • At 3.3 V, ADC should read ~4095 (12-bit ADC)
    • In between, the ADC should smoothly transition.
  3. Confirm linear behavior
    Turn the knob slowly and verify that:
    • Values increase and decrease smoothly
    • No sudden jumps or stuck values
    • No saturation unless at 0 or peak voltage

If values appear reversed (high when low, low when high), swap the outer pins of the potentiometer.


Filtering Noise or Jitter

ADC readings may fluctuate due to noise from:

  • Long wires
  • High-impedance sensor sources
  • Switching supply noise
  • Fast ADC sampling

You can smooth readings using:

1. Simple Moving Average

Average the last N samples:

uint32_t smooth_adc(uint32_t new_value) {
    static uint32_t buffer[8];
    static int index = 0;
    static uint32_t sum = 0;

    sum -= buffer[index];
    buffer[index] = new_value;
    sum += new_value;

    index = (index + 1) % 8;

    return sum / 8;
}

2. Oversampling and Averaging (built-in)

Enable multiple steps in a sequencer (SS1 or SS2) and average the results.


3. Hardware Filtering
  • Add a 0.1 µF capacitor from analog input to ground
  • Keep analog wires short
  • Use a stable 3.3 V supply

Common ADC Issues and Fixes

ProblemCauseFix
ADC always reads 0Wrong pin config, channel mismatch, no analog functionEnable GPIO analog mode (GPIOPinTypeADC), correct AIN channel
ADC always reads 4095Input > 3.3V, pin floating, hardware wired wrongCheck wiring, ground, ensure stable voltage
Very noisy readingsNo filtering, long wiresEnable averaging, add capacitor
Results don’t change with potentiometerWiper not connected to AINxRecheck wiring
ADC reads invertedPot’s outer pins swappedSwap the two ends
ADC reads incorrect max/minWrong reference voltage assumptionTM4C uses 3.3 V internal reference

Conclusion

n this post, we walked through the complete process of setting up the ADC on the TM4C123G, from understanding sequencers, configuring the GPIO pins, initializing the ADC module, and finally writing clean driver functions to read and convert analog values. Each step was broken down to ensure even beginners can follow the configuration flow with confidence.

We also tested the ADC output using a potentiometer and explored techniques to ensure readings are accurate and stable. Troubleshooting tips, noise-filtering methods, and common mistakes were covered so you can quickly identify and fix issues during development. These checks are essential for any real-world embedded application where signal reliability matters.

Mastering the ADC is foundational, and this experience sets you up for more advanced topics like DMA-based sampling, multi-channel acquisition, and digital signal processing in future projects

Browse More TM4C123G Tutorials

1 2

TM4C123G ADC Project Download

Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

TM4C123G ADC FAQs

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments