This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EK-TM4C1294XL: ADC measurement offset on first step of sequence

Part Number: EK-TM4C1294XL

I am using the EK-TM4C1294XL to measure 4 analog channels (CH11, CH10, CH8, CH9). I am using sequence 0 to collect the data using 4 steps. CH11 is collected on step 0, CH10 on step 1, CH8 on step 2, CH9 on step 3. Each ADC pin is connected to 1.65V though a voltage divider of two 10K resistors connected between the launch pad's 3.3V and GND. 

Bellow is a plot of the data that is collected. I am expecting each channel to read ~1.65V but there is a large ~0.2V offset on CH11 collected on step 0.

If I switch which channels are collected on the steps , ex collect CH10 on step 0 and CH11 on step 1, the offset follows the sequence step and not the analog channel. I have not been able to figure out why this offset is occurring. Have I missed some setup or configuration?

Here is my code.

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_adc.h"
#include "inc/hw_types.h"
#include "inc/hw_udma.h"
#include "driverlib/adc.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "driverlib/udma.h"
#include "utils/uartstdio.h"
#include "driverlib/flash.h"

uint32_t g_ui32SysClock;

//
// Configure the UART and its pins.  This must be called before UARTprintf().
//
void ConfigureUART(void)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable UART0.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure GPIO Pins for UART mode.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, g_ui32SysClock);
}

//
// This function will initialize ADC0 sequence 0 for 4 steps
//
void Init_ADC0_debug()
{
    //
    // Disable the ADC0 sequence 0 interrupt on the processor (NVIC).
    //
    IntDisable(INT_ADC0SS0);

    //
    // Disable interrupts for ADC0 sample sequence 0 to configure it.
    //
    ADCIntDisable(ADC0_BASE, 0);

    //
    // Set the ADC reference
    //
    ADCReferenceSet(ADC0_BASE, ADC_REF_INT);

    //
    // Set the ADC clock speed
    //
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_RATE_FULL | ADC_CLOCK_SRC_PIOSC , 1);

    //
    // Disable ADC0 sample sequence 0.  With the sequence disabled, it is now
    // safe to load the new configuration parameters.
    //
    ADCSequenceDisable(ADC0_BASE, 0);

    //
    // Enable sample sequence 0 with a processor signal trigger
    //
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0); //ADC_TRIGGER_ALWAYS

    //
    // Configure step 0,1,2,3 on sequence 0, Sample all channels in single-ended mode
    // Set end of sequence and interrupt on the last step
    //
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH11 );
    ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH10 );
    ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH8 );
    ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH9 | ADC_CTL_END | ADC_CTL_IE);

    //
    // Since sample sequence 0 is now configured, it must be enabled.
    //
    ADCSequenceEnable(ADC0_BASE, 0);

    //
    // Clear the interrupt status flag.  This is done to make sure the
    // interrupt flag is cleared before we sample.
    //
    ADCIntClear(ADC0_BASE, 0);
}

int main(void)
{

    uint32_t ADC0SS0_FIF0[4];

    //
    // Setup system clock to run from the PLL at 120 MHz.
    //
    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

    //
    // Enable the peripherals used by this application.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Enable I/O
    //
    GPIOPinTypeGPIOInput(GPIO_PORTJ_BASE,GPIO_PIN_0 | GPIO_PIN_1);                                              //Buttons
    GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU);       //Pull-ups for buttons

    //
    // Enable ADC pins
    //
    GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_5 );                                                  // analog ch10,11
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5 );                                                  //Analog ch9,8

    //
    // Initialize UART0 and write initial status.
    //
    ConfigureUART();

    //
    // Initialize the ADC
    //
    Init_ADC0_debug();

    //
    // Enable processor interrupts.
    //
    IntMasterEnable();

    while(1)
    {

        //
        // Start the data collection when button 1 is pressed
        //
        if( GPIOPinRead(GPIO_PORTJ_BASE,GPIO_PIN_0) == 0){


            // Capture a single data point from the ADC
            ADCProcessorTrigger(ADC0_BASE, 0);
            while(!ADCIntStatus(ADC0_BASE, 0, false)) {}                    // Wait for pending ADC conversion to finish
            ADCIntClear(ADC0_BASE, 0);                                      // Stores ADC value automatically to FIFO array
            ADCSequenceDataGet(ADC0_BASE, 0, ADC0SS0_FIF0);

            //
            // Send ADC values over terminal
            //
            uint32_t count;
            for(count = 0; count < 4; count++){

                UARTprintf("%i\n",ADC0SS0_FIF0[count]);
            }


        }


    }

}


  • Hello Jake,

    That is a pretty large input impendence to the ADC. The ADC source resistance is just 250 ohm. I think with the amount of input impendence, what's happening is that with the first sample the input has not fully settled on the internal capacitors when the first measurement is being taken while the latter samples have added time passing that lets the signal settle and therefore get an accurate reading - this lines up with how if you change which channel is first, the offset is just following that first reading.

    Can you repeat the experiment but with lower resistors like 3.3kOhm or even down to 1kOhm resistors for the divider instead?

    I really don't think this is a configuration issue as much as needed to let the signal settle in time for the ADC so let me know if that resolves the issue or not.

    Best Regards,

    Ralph Jacobi

  • Hi Ralph,

    That has seemed to fix the issue. The reading are closer to what I would expect.

    Is there a recommended range for the input impedance?

  • Hi Jake,

    As small as possible is the recommendation but realistically to keep currents in check you won't always be able to minimize the input impendence fully. There are ways to help with that - the whole issue is basically the external resistance coupled with the internal capacitor creates an RC time constant and if that is too large then you may sample before the capacitor is fully charged - resulting in the offset you were seeing.

    To resolve this, you could add external capacitance to the ADC pins to reduce the time constant OR you could increase the sample time to allow more time for the signal to charge and settle before sampling. These both would allow for higher input impedance when it is unavoidable.

    This app note is for a different MCU family but it's also an Arm so the ADC is rather similar and all the concepts apply pretty broadly to other MCUs. It provides a lot more background on the topic. I find the table near the end on Page 11 to be pretty helpful to understand the impact of resistance as well as external capacitance to improve ADC performance: https://www.ti.com/lit/pdf/spna118

    Best Regards,

    Ralph Jacobi