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.

TM4C123GH6PM: TM4C123 ADC IN DIFFERENTIAL MODE

Part Number: TM4C123GH6PM

Hello All,

Iam using a TM4C123GH6PM custom board. Iam trying to use the internal ADC in differential mode but somehow my ADC output is not coming correctly when I'am using multiple ADC pins.

I followed the ADC differential example and it works for single channel using sample sequencer 0.

However , When I use multiple pins with sample sequencer 2, The ADC FIFO result is incorrect as Iam getting signal only on  

ui32ADC0Value[0] 
ui32ADC0Value[1] is showing incorrect result. 

Ia'm just testing with two input channels first.



  Below is my code. Iam using timer to trigger the ADC with sampling rate as 256Hz

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"
#include "driverlib/adc.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "utils/uartstdio.h"

uint32_t ui32ADC0Value[4];

uint32_t outputsamples1;
uint32_t outputsamples2;
uint32_t outputsamples3;
uint32_t outputsamples4;

void Timer0IntHandler(void)
{
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    //GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0xFF);
    ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_TIMER, 0);

    //m=m+1;
}


int main(void)
{



   // uint32_t ui32ADC0Value[8];
    uint32_t ui32Period;




    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // clock at 80 Mhz

    ui32SysClkFreq = 80000000;

    /*********************PIN INTILIAZATION********************************/

        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

                while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)));

                GPIOPinTypeADC(GPIO_PORTE_BASE,GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0);

                SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

                            while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD)));

                            HWREG(GPIO_PORTD_BASE+GPIO_O_LOCK) = GPIO_LOCK_KEY;

                            HWREG(GPIO_PORTD_BASE+GPIO_O_CR) |= GPIO_PIN_7;

                            GPIOPinTypeADC(GPIO_PORTD_BASE,GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0);

    /*********************ADC INTILIAZATION********************************/

                SysCtlPeripheralDisable(SYSCTL_PERIPH_ADC0);

                  SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);

                SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

                while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)));

                

                 ADCSequenceDisable(ADC0_BASE, 2);
                            
                            ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH0);
                            ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH1);
                            ADCSequenceStepConfigure(ADC0_BASE,2,2,ADC_CTL_D|ADC_CTL_CH2);
                            ADCSequenceStepConfigure(ADC0_BASE,2,3,ADC_CTL_D|ADC_CTL_CH3|ADC_CTL_IE|ADC_CTL_END);
                           
                            ADCSequenceEnable(ADC0_BASE, 2);

     /*********************TIMER0 INTILIAZATION********************************/
        SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

                    TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);

                    ui32Period = 80000000/256;

                    TimerLoadSet(TIMER0_BASE, TIMER_A, ui32Period-1);

                    TimerControlTrigger(TIMER0_BASE, TIMER_A, true);

IntEnable(INT_TIMER0A);

                                TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

                                TimerEnable(TIMER0_BASE, TIMER_A);
                    IntMasterEnable();

        while(1)
        {
            ADCIntClear(ADC0_BASE, 2);


                    while(!ADCIntStatus(ADC0_BASE, 2, false))
                    {
                    }
                    ADCSequenceDataGet(ADC0_BASE, 2, ui32ADC0Value);
outputsamples1 =  ui32ADC0Value[0];
                    outputsamples2 =  ui32ADC0Value[1];
                    outputsamples3 =  ui32ADC0Value[2];
                    outputsamples4 =  ui32ADC0Value[3];

}
}

Kindly suggest a solution.

  • Hi Sumit,

     How much is the value off from expected?

     I'm not sure if you are hitting the below errata. For experiment, can you try to not use PE3 and see if it makes a difference?

  • Hi Charles,

    I tried not using The PE3 pin, however it does not make any difference.

    The output of First FIFO i.e. ui32ADC0Value[0] is coming in the other FIFO as well.

    My setup is that Iam giving two sinewave of different frequencies (10HZ & 60HZ) on different Pins PE1,PE0(CH1) & PD3,PD2(CH2) . However in both the channels the output is same. The sinewave which is connected to PE1,PE0(CH1) is also getting shown in the second channel(CH2) because when I change the frequency of PE1,PE0 signal ,the signal of PD3,PD2 also changes accordingly. However there is no change in the PD3,PD2 signal if I change its own frequency or amplitude.

    Iam really lost here!! Is There any example of using multiple ADC pins in differential mode?
  • Sumit Mourya said:
    ADCSequenceDataGet(ADC0_BASE, 2, ui32ADC0Value);

    Should not that 'final' parameter (properly) be,  '&ui32ADC0Value' ?

    In addition - are you (really) providing 'properly balanced' differential signals - to the MCU?    Might you describe (how) you've 'created and/or sourced' such signals?    These signals must remain w/in the MCU's common mode voltage range - but really should be 'differentially referenced' - not 'ground referenced.'

    You must note that, 'while in differential mode - the 'difference between each of those 2 channels' must remain as, 'equal to or less than VDD or VDDA'  as appropriate.   (i.e. neither of those analog channels can exceed one-half of VDD or VDDA.)

  • Hello,

    It gives me a warning during compliation.

    Description Resource Path Location Type
    #169-D argument of type "uint32_t (*)[4]" is incompatible with parameter of type "uint32_t *" main.c /PC COMM line 204 C/C++ Problem
  • Yes, IAm giving correct input because when I test different inputs using single channel FIFO(sample sequencer 0), Iam getting correct result for different types of inputs.
  • So - how did you determine that,
    uint32_t ui32ADC0Value[4]; was proper?
  • The issue is that ADC is giving output of only One FIFO ui32ADC0Value[0]. ui32ADC0Value[1] Is acting as if its an open signal. It is not reading the input given on second channel.
  • Sumit Mourya said:
    Iam getting correct result for different types of inputs.

    You are 'offering up' your 'belief' (correct result) in place of REAL PROOF!    That is not a valid argument.

    It is suspected that your 'sine-wave' is Single-Ended - not differential.   (the differential sine-wave would appear 180° out of phase - and requires (proper) analog processing - to create such a signal.

    Nevertheless - your code (as identified) is incorrect.   (final parameter - as earlier noted)

  • When I connect A sine wave of suppose 20 hz on CH0(PE3 & PE2) , I get the actual signal of 20Hz In output. Now when I connect a sine wave of 100 Hz on CH0(PE3 & PE2) , again I'am geting correct signal of 100Hz as output.

    Now when I connect 20 Hz on CH0(PE3,PE2) and 100Hz on CH1(PE1,PE0) then I'am only getting output signal of 20 Hz on both channels!! If I make it vice versa conencting 100HZ on CH0 & 20 Hz on CH1, then I'am getting 100Hz only as output.
  • Hello,

    I've even tried recording My ECG ,PULSE,RESPIRATION type biopotential signals using multiple ADC Pins in differential mode. In single channel I'am getting My ECG,PULSE,RESPIRATION all the signals correctly.
  • Differential signals are 'extremely NOISE RESISTANT' - NEVER/EVER would a single-ended measurement be employed in a medical application!

    The ADC is intended to measure voltage levels - yet you report FREQUENCY MEASURE - ONLY! Minus your detailing - such indicates a 'lack of understanding' of (any) ADC's opeation.
  • Hello,

    I'am not here to discuss your's or mine level of understanding of ADC's or Differential Signals. Your suggestion in my code is giving me warning error which is not correct.

    I'am just asking for an example of differential ADC Mode using multiple channels.
  • Thank you for (now) listing the 'limited conditions' - under which you REQUEST HELP!     (Diplomacy - NOT so much - in evidence.)
    Nevertheless:

    Do try replacing (your) uint32_t ui32ADC0Value[4]; with uint32_t ui32ADC0Value and report...     The importance of [4] is acknowledged - yet further insight is being sought.

  • If I replace uint32_t ui32ADC0Value[4] with uint32_t ui32ADC0Value , How will I differentiate or store different FIFO values in variables?

    Iam storing FIFO values in four different variables.
  • I have resorted to digging out K&R - and 're-reading chapter/section 5.3', "Pointers & Arrays."

    The earlier direction - to employ  &ui32ADC0Value  flowed (directly)  from this vendor's  'Peripheral Driver Library - User Guide!'

    I (now) believe that the above should CHANGE to *ui32ADC0Value.    

    K&R notes that the subsequent assignment:   (assume)   x = *ui32ADC0Value   will copy the contents of  ui32ADC0Value[0]  into  x.

    Do give this a try - and report.    If this succeeds - the Example furnished w/in vendor's 'PDL User Guide'  is in error - and that has been revealed by (our) joint effort - here...

  • Hi,

    Hi,
    The ui32ADC0Value is an array of 4 elements so it is a pointer. Therefore, the line ADCSequenceDataGet(ADC0_BASE, 2, ui32ADC0Value) is fine.

    I'm reading into your code again. Will get back.

  • Hi again,

    Can you change

    from:

                               ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH0);

                               ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH1);

                               ADCSequenceStepConfigure(ADC0_BASE,2,2,ADC_CTL_D|ADC_CTL_CH2);

                               ADCSequenceStepConfigure(ADC0_BASE,2,3,ADC_CTL_D|ADC_CTL_CH3|ADC_CTL_IE|ADC_CTL_END);

    to:

                               ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH0);

                               ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);

    Please read the description from the driverlib user's guide for differential mode. ADC_CTL_CHO is for AIN0/AIN1 pair while ADC_CTL_CH1 is for AIN2/AIN3 pair.

    The ui32Step parameter determines the order in which the samples are captured by the ADC when
    the trigger occurs. It can range from zero to seven for the first sample sequencer, from zero to three
    for the second and third sample sequencer, and can only be zero for the fourth sample sequencer.

    Differential mode only works with adjacent channel pairs (for example, 0 and 1). The channel select
    must be the number of the channel pair to sample (for example, ADC_CTL_CH0 for 0 and 1, or
    ADC_CTL_CH1 for 2 and 3) or undefined results are returned by the ADC. Additionally, if differential
    mode is selected when the temperature sensor is being sampled, undefined results are returned
    by the ADC.

  • Aha - good to have 'Vendor Presence'  (half of my lawn is now (almost) mowed!)

    But Charles - you MUST note - that  ONLY  your 'Great Sense of Detail'  (after being alerted to such issue) revealed such 'unexpected combination!'      In defense of this originating poster - your advice 'Makes Sense NOW' - yet (almost) everyone is likely to stumble w/such (HIGH) Detail.     (Would not a differential ADC example - trump the (needless) 'Murder of 'LIKE?')     (NO your job - we know - yet still...)

    But do note the   &variable   INDEED APPEARS w/in the PDL User Guide - and also w/in the earlier Stellaris User Guide - and (does) appear in conflict w/'my read' of K&R.

    Attached now - a 'true copy' - of the PDL User's Guide, ADC Example:    (the 'conflict' appears w/in the final line of  this (pasted) PDL code!)

    ***************************************************
    uint32_t ui32Value;
    //
    // Enable the ADC0 module.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    //
    // Wait for the ADC0 module to be ready.
    //
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0))
    {
    }
    //
    // Enable the first sample sequencer to capture the value of channel 0 when the processor trigger occurs.
    //
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0,ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH0);
    ADCSequenceEnable(ADC0_BASE, 0);
    //
    // Trigger the sample sequence.
    //
    ADCProcessorTrigger(ADC0_BASE, 0);
    //
    // Wait until the sample sequence has completed.
    //
    while(!ADCIntStatus(ADC0_BASE, 0, false))
    {
    }
    //
    // Read the value from the ADC.
    //
    ADCSequenceDataGet(ADC0_BASE, 0, &ui32Value);

  • Hi cb1,

    In the code you showed below the ui32Value is a unsigned integer variable and therefore the &ui32Value is the pointer to the variable.
    uint32_t ui32Value;

    In the OP's code he has:
    uint32_t ui32ADC0Value[4];

    The ui32ADC0Value is declared as an array. In K&R, page 99, it says the name of an array is a synonym for the location (a pointer) of the initial element. So &ui32ADC0Value[0] is equivalent to ui32ADC0Value.
  • Sorry - I can't fully understand 'if or where' the PDL example is correct or wrong. And - I had the poster remove the [4] from ui32ADC0Value - yet still - he reported 'code squawking.'

    Note too - poster initially employed (just) ui32ADC0Value - (minus the PDL's &) - to compiler's squawk.
  • Hi cb1,
    Both are correct. The example shown in the PDL declare a unsigned int variable while the Poster's original code declares a 4-element array. What I'm saying is that the array by itself is equivalent to a pointer. If he were to declare an int variable as shown in the PDL example then yes, he needs to typecast with the & to make it a pointer.
  • Hello Charles,

    I've already tried doing this. I have tried using differential pair 0 & 1(PE3,PE2- PE1,PE0) and also differential pair 1 & 2(PE1,PE0-PD3,PD2).

    ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH0);

    ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);

    OR

    ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH1);

    ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH2|ADC_CTL_IE|ADC_CTL_END);


    But still the same issue. Can you try using the multiple Pins of ADC in differential mode and see what results you get?

    Have you ever heard this issue before or tried using it?
  • Hi Charles,

    NOW OK - and thank you. There were 'many' back n forths here - and it WAS (pretty) early.

    Poster's original code contained:

    uint32_t ui32ADC0Value[4];
    // and followed that with
    ADCSequenceDataGet(ADC0_BASE, 2, ui32ADC0Value);

    And - that code properly managed the pointer ... yet the poster reported that it had FAILED to yield the correct ADC Value! And then somewhere - I 'lost' the fact of the code's correctness - never/ever sensing that the Differential Mode would be 'compressed' - as you so well discovered & presented.

    Again - in aid to ALL future 'others' - who may wage such differential battle - your 'example' code (here) should be EASILY FOUND/PROVIDED -
    lest (other) hapless client-users - march to the Differential ADC Mode's (beckoning) Cliff-Side!     (It IS - a (very) long way - down!)

  • Hi Sumit,
    I have some questions.

    1. With the below code, what did you read from ui32ADC0Value[0] and ui32ADC0Value[1]? Did you earlier say that ui32ADC0Value[0] will return correct value corresponding to pair AIN0/AIN1 but ui32ADC0Value[1] will show like the ADC input is open.

    ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH0);

    ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);

    2. If you were to reverse the order like below, what will you read from ui32ADC0Value[0] and ui32ADC0Value[1]? Will ui32ADC0Value[0] still show correct value corresponding to pair AIN2/AIN3?

    ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_D|ADC_CTL_CH1);

    ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_D|ADC_CTL_CH0|ADC_CTL_IE|ADC_CTL_END);

    3. When you call ADCSequenceDataGet, what did it return? The API will return the number of samples copied to the buffer.

    4. I'm not sure what you are trying to do in your below timer interrupt ISR. Why are you changing the ADC trigger type here? The line ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_TIMER, 0) should be part of your ADC initialization. Why wait until the Timer interrupt ISR to configure it. Even though this may not be the cause of your problem, please move this line to where you do the ADC initialization.

    void Timer0IntHandler(void)
    {
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    //GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0xFF);
    ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_TIMER, 0);

    //m=m+1;
    }
  • Hi Charles,

    Just wanted to update that my issue is resolved. The code which I had written Initially was correct. There was issue in Analysing the output of the ADC in my .Net code.

    Just wanted to answer your 4th question that I'am triggering the ADC through TIMER because I have adjusted my sampling rate of the ADC to 256Hz. If I do PROCESSOR TRIGGER then the sampling rate will be default 1MSPS I guess in TM4C123.

    So, I keep my TIMER COUNT as MCU CLOCK/256( Clock/desired sampling rate). So everytime my TIMER interrupt triggers, it further Triggers the ADC to do conversion.

    Thanks for your suggestion!

  • Hi Sumit,
    Glad that your problem is resolved.