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.

Tiva TM4C123G LaunchPad: ADC is swapping the channel readings

Hi,


I have built a timer triggered ADC program to read 4 different channel using sequencer 1. I am checking values in watch window in debug mode. While the ADC is measuring values properly, channels are getting swapped. Here is the code:

#include <stdint.h>
#include <stdbool.h>
#include <math.h>

#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/adc.h"
#include "driverlib/fpu.h"
#include "driverlib/pin_map.h"

uint32_t ui32ADC0Value[4];
volatile uint32_t a,b,c,d;

int main(void)
{
	//Set processor @ 80 MHz.
	SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

//===============================================================================================
	//Give clock to the peripherals:

	//GPIO for testing: LED: R B G
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

	//GPIO for capture interrupt
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);

	//ADC pins
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

	//Timer for 40 kHz calc and adc trigger
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

	//ADC 0
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

	SysCtlDelay(10);

//===============================================================================================
	//Peripheral configuration

	//Config GPIO: LED R B G
	GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);

	//Config Timer0: 40 kHz
	TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);	//32 bit mode
	TimerLoadSet(TIMER0_BASE, TIMER_A, 2000-1);			//load period
	IntEnable(INT_TIMER0A);								//interrupt enable
	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);	//interrupt condition: timeout
	TimerControlTrigger(TIMER0_BASE, TIMER_A, true);	//ADC trigger

	//Configure ADC0
	ADCSequenceDisable(ADC0_BASE, 1);

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

	ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_TIMER, 0); //sequencer 1, proccessor trig, priority zero highest

	ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH4);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH3);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_CH2);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);  //final step: give int, stop

	IntEnable(INT_ADC0SS1);
	ADCIntClear(ADC0_BASE, 1);
	ADCIntEnable(ADC0_BASE, 1);

//===============================================================================================
	//Enable the peripherals one by one

	//Int master enable
	IntMasterEnable();

	//Timer0: Calc & ADC trigger
	TimerEnable(TIMER0_BASE, TIMER_A);

	//ADC
	ADCSequenceEnable(ADC0_BASE, 1);

//===============================================================================================
	while(1)
	{

	}
}
//===============================================================================================
void Timer0IntHandler(void)
{
	TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

	if(GPIOPinRead(GPIO_PORTC_BASE, GPIO_PIN_4))
		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
	else
		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 2);
}
//================================================================================================
void ADC0SS1IntHandler(void)
{
	ADCIntClear(ADC0_BASE, 1);
	ADCSequenceDataGet(ADC0_BASE, 1, ui32ADC0Value);

	a = ui32ADC0Value[0];
	b = ui32ADC0Value[1];
	c = ui32ADC0Value[2];
	d = ui32ADC0Value[3];
}
//==================================================================================================

e.g. the graph of first value ui32ADC0Value[0] is shown

There are sudden random spikes, which correspond to readings of other channels.

If all 4 steps in the sequencer are configured to read the same channel then the issue does not arise. So hardware seems fine.

Has anyone encountered similar issue?

Thank you.

-Saurabh

  • Saurabh said:
    While the ADC is measuring values properly, channels are getting swapped.

    Quote from your opening paragraph - yet full read of your post never identifies any such (swap) - only "random spikes!"

    As you report, "both swap & spike-free" ADC operation when you read the same channel - might that suggest a, "mismatch" between your ADC's input and the signals being introduced to the ADC?   No description of those signals - especially their output impedance - appears.   The MCU's ADC employs an input capacitor which must be properly, "charged/discharged" for proper ADC operation.  Spikes often - but not always - indicate that the input capacitor has improperly/incompletely -charged/discharged. 

    ADC chapter w/in MCU manual and ADC specifications (very end of the manual) provide key specs for the ADC's input circuitry.   Review, re-read may provide some insight.

  • Hi,

    Thank you for quick reply.

    What I meant was location of the spike is random, however, magnitude is known. Magnitude is always same as that of one of the other channels. e.g. if I select only 2 channels:

    ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH3);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH3);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_CH2|ADC_CTL_IE|ADC_CTL_END);

    then the graph of one channel contains spikes with magnitude same as measurement of the other channel shown in following figure. That is why I am doubting that noise is the issue here.

    I will look into the input impedance though. For testing purpose right now I have connected simple 10 kohm pot as input after which a 10 Hz low pass RC filter is connected. The magnitude of the spike follows the pot setting.

    Thanks.

  • Thank you. For giggles - you might benefit from employing a classic op-amp to implement that 10Hz low pass. Lowered output impedance (via the op amp) may improve your results.
  • Hello Saurabh,

    The ADC channel have a 500Ohms Input Impedance requirement (Electrical Section for ADC in the data sheet). You would need to have a buffer in the path

    Regards
    Amit
  • cb1- said:
    Lowered output impedance (via the op amp) may improve your results.

    Use of an op amp as "active, low-pass filter" likely achieves "twin goals" of lowered output impedance AND higher "Q" -  low-pass function...

  • I attached opamp voltage follower circuit just before adc pin. The issue still persists.
  • Hello Saurabh,

    Is the Memory Browser in the IDE open or ADC registers visible in the debugger which may cause the samples to shift. Also is the print of the converted value happening in sufficient time to prevent buffer overrun?

    Regards
    Amit
  • Amit Ashara said:
    is the print of the converted value happening in sufficient time to prevent buffer overrun?   

    Along these lines - KISS specifies performing "greatly" slowed conversions (or even just one) to prevent such overruns...

    Once you determine the source & triggering method of such overrun - your repair becomes far faster/simpler...

  • Hi Amit,

    I am also suspecting the debugger though I am not reading the adc FIFO registers in the watch window. I am reading the memory array into which the FIFO is read.
    I have tried low conversion frequency (8 kHz sampling rate). in this case frequency of spikes is reducing.

    Instead of using the debugger to check adc measurement, I gave ADC output to PWM pulse width. Now without the breakpoint in the debug mode there are no glitches in the PWM (duty is constant), opamp buffer was also removed. It seems debug breakpoint is causing the problem as you suggested.

    Thanks
    -Saurabh
  • Saurabh said:
    It seems debug breakpoint is causing the problem as you suggested.

    We've not noted that w/in (paid) IAR. 

    While feeding the ADC output to a PWM generator is one means (albeit an indirect one) would not the, "writing of ADC data to MCU's SRAM" prove faster, easier and (surely) more direct?   Such can happen in real-time - the memory capture may then be quickly/easily imported into Excel - and a far more viewable (and meaningful) ADC data record is achieved.

    You must write to a "safe region" w/in SRAM - and not exceed it's bounds.

  • Hello Saurabh

    In continuous mode of conversion, watch, memory browser and break points can cause issue.

    Regards
    Amit
  • Hi cb1-,

    I am using ccs 5 shipped with the launchpad.
    With the 32 kB internal SRAM, I will be able to monitor only .1 second (40 kHz sampling, 4 channels) . With PWM I am able to monitor much longer using a CRO. Also i am able to check variation in the input signal properly.

    Thanks,
    -Saurabh
  • Saurabh said:
    With PWM I am able to monitor much longer using a CRO. Also i am able to check variation in the input signal properly. 

    Are you (really) certain?  

    As you acquire the 4 channel ADC data - is there not some delay before you can present each channel, successively, to the PWM Generator? Or - do you employ all 4 of the MCU's PWM Generators - loading each one after each/every conversion.

    You don't describe how/when you "load" the PWM Generators.   Often - but not always - the new PWM load data must "wait" until the PWM Conversion completes some "max" or "0" PWM count.  Don't these "particulars" intrude into your "confidence" that you're, "Able to check variation in input signal, properly?"

    I continue in the belief that some "25K or so" successive writes to SRAM (one channel) would far better document & reveal any signal "glitches" which may prove hard to predict (and capture) with (even) a CRO.  (how do you know exactly "how wide" to set your "runt (or wide) pulse" detector?)

    Your method is original - but its indirectness and resulting time-delay and load change - along with the CRO's (quite possible) blindness to a "runt pulse" throw some question upon your stated, "able to check variation in the input signal properly."   "Wishful thinking" does not make a "proven/robust" test/verify...  Your method - while original - may suffer from that very "originality."  (few "pro" firms/products measure input signals precisely via direct conversion to PWM...)

  • Dawns that you may write the first (8 or so) stable ADC values to memory - and then alter "the SRAM write scheme" to detect (and to write) only "deviant" readings!

    Quite easy to "count" each/every arriving ADC measure - (not writing that count to memory) and then to write only the deviant value, chan number, and ADC count number to SRAM.  Such (far more conventional, data-logging) method may harvest beyond 15K "deviant" occurrences - and the linkage to adc channel and adc count number provide great diagnostic insights. 

    In (rather) stark contrast - your PWM-scope method "gives up the ghost" at the first "deviant detection" does it not?  And reveals nothing as to number of "good conversions!"  (prior to your singular (one & done!) scope capture)   (and I remain unconvinced that you can even capture a "single deviant" event!)

    The indirectness of your, "ADC -> PWM" method - and its associated time delays - and the necessity of a "finely triggered" scope (all to "capture" (one hopes) a SINGLE deviant event) - characterize it as a "far less effective, diagnostic effort!"   And one which provides, "No written trail" of multiple, valuable, linked test data results...