Xmega ADC cross talk on inputs?

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

Sorry, this is going to be a bit long winded, but you have to understand where I am comming from.

I am experiencing a stange issue on the xmega ADC. The chip I am using is the xmega32A4U. I am using the latest Atmel studio 6.1 with the latest ASF. I am not using ASF however.

The hardware: The xmega is running from internal 32MHz RC oscillator, divided down to 8MHz for cpu clock. I have good de-coupling on all power pins - 100nF. I am feeding 3V3 via an inductor to AVCC and there is a 100nF cap on the AVCC side. My layout has a full ground plane. I have a MCP1525 2V5 voltage reference on AREF. Please see attached schematic.

The schematic and board layout had 2 problems that I have fixed. A cap (100nF) is needed on the VREF input. A ground connection is needed between the I-GND signal and GND signal.

I am using a timer event to trigger a sweep conversion on 4 ADC inputs every 1ms. The sweep runs on ADC1 to ADC4. The signals on ADC1, ADC3 and ADC4 are small - of the order of 5 to 100 mV at the moment. These are current measurements. The signal on ADC2 is a much larger signal - measuring voltage through a resistive divider. After a sweep conversion an interrupt service routine is reading the values from all 4 channels into arrays 16 entries deep (one for each channel) for calculating a moving average.

What I am seeing is that the adc values of channel 2 are appearing in channel 1 - every fourth conversion. They are always in the same position in the values array of channel 1. Please see attached screen capture of the debugger showing the values of the conversion arrays.

I have measured the inputs of ADC channel 1 with a sampling scope - I do not see a glitch on the input signal of ADC1 at the time of conversion (or at all). If there was a glitch - I would expect a more random value - not something that correlates this well with the values of ADC2.

The setup code of the ADC looks like this


//unsigned char aveidx=0;						// count values in average array
T_A2D  a2d_val[A2D_IDX_LAST];					// Storage space for processed adc values
int16u a2d_samples[A2D_IDX_LAST][A2D_MAXSAMP];	// sample storage space.
//static unsigned char a2d_chan = 0;				// The current active adc channel
static unsigned char a2d_idx = 0;				// The current in-use sample index (the same for all channels
//Preset ADMUX mask: Set reference to AVCC and clear Left Adjust, set channel 0
//static unsigned char a2c_stopflag = 0;					// When set to 1 temporary stop the conversions

// Default full-scale values for each channel
int16u a2d_fullscale[A2D_MAXSAMP] = 
{
	12000,	//fullscale amps in milliamps
	40000,	// fullscale volts in 10ths of millivolts
	1000,	// low side volts for amps differential
	1000,	// low side volts for GND amps differential
};


//============================================================================
// ------------------------------- IMPLEMENTATION ----------------------------
void A2D_Init(void)
{	// init A2D by setting clock preset, interrupts etc.
	// remember Aref and AVcc may tied together,  don't use internal references!!
	// In this application we are only using ADCA

	//vERR_Sprint_P(ERR_LVL_DEBUG, PSTR("A2D Init."));
		
	//----- Make sure the ADC is not in power reduction mode
	// default boot state is not disabled.
	//PR.PRPA &= 0x00;	// enable DAC, ADC, AC for port A, default startup state is enabled
	//PR.PRPB &= 0x00;	// enable DAC, ADC, AC for port B, default startup state is enabled

	//----- Setup ADC
	// Clear all DMA settings - will set bit 0 - enable as last step
	ADCA.CTRLA = 0x00;
	// High impedance, no current limit, single ended, not free running, 12bit right adjusted.
	ADCA.CTRLB = 0x00;
	// Set voltage reference, no bandgap, no tempref
	ADCA.REFCTRL = ADC_REFSEL_AREFA_gc;
	// Set event control
//	ADCA.EVCTRL = (ADC_SWEEP_0123_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SWEEP_gc);
	ADCA.EVCTRL = (ADC_SWEEP_0123_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SYNCHSWEEP_gc);
	// Set ADC clock to 500ksps
	ADCA.PRESCALER = ADC_PRESCALER_DIV16_gc;
	// Clear all interrupt flags
	ADCA.INTFLAGS = 0x00;
	// Load Callibration values
	ADCA.CALL = A2D_readNVM(PROD_SIGNATURES_START + PRODSIGNATURES_ADCACAL0);
	ADCA.CALH = A2D_readNVM(PROD_SIGNATURES_START + PRODSIGNATURES_ADCACAL1);
	// Set up the conversion channels
	// Setup channel 0 = I-SHUNT
	ADCA.CH0.CTRL = (ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc);
	ADCA.CH0.MUXCTRL = (ADC_CH_MUXPOS_PIN1_gc);
	ADCA.CH0.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc);
	//ADCA.CH0.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc);
	//Scan only available for channel 0
	//ADCA.CH0.SCAN = (ADC_CH_OFFSET_gp<<val | ADC_CH_C)
	// Convert count plus1 channels in a scan.
	ADCA.CH0.SCAN = 0x03;
	// Setup channel 1 = V-OUT
	ADCA.CH1.CTRL = (ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc);
	ADCA.CH1.MUXCTRL = (ADC_CH_MUXPOS_PIN2_gc);
	ADCA.CH1.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc);
	//ADCA.CH1.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc);
	// Setup channel 1 = I-GND1
	ADCA.CH2.CTRL = (ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc);
	ADCA.CH2.MUXCTRL = (ADC_CH_MUXPOS_PIN3_gc);
	ADCA.CH2.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc);
	//ADCA.CH2.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc);
	// Setup channel 1 = I-GND2
	ADCA.CH3.CTRL = (ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc);
	ADCA.CH3.MUXCTRL = (ADC_CH_MUXPOS_PIN4_gc);
	//ADCA.CH3.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc);
	ADCA.CH3.INTCTRL = (ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc);

	// Enable the ADC
	ADCA.CTRLA |= 0x01;
	
	
	// Preset ADMUX register: Set reference to AVCC and clear Left Adjust, set channel 0
	// refer to ADC_MUXMASK definition above
	//ADMUX = A2D_MUXMASK;

	// preset channel indexing
//	a2d_chan = A2D_IDX_FWRDP;	// start with channel 0 = PA0_ADC_FWRDP
	a2d_idx = 0;	// start with sample index 0

	// now start first conversion : interrupts will handle further conversions from here
	// Conversions are triggered by an event from the timer
}

The interrupt service looks like this

ISR (ADCA_CH3_vect) 
{	// ADC conversion complete interrupt for channel3
	// This should be the last one in a 4 channel sweep.
	// so only respond to this one and handle all 4
	
	//!! Only used while debugging
	PORTA.OUTSET = (1<<PA5_ADC5);	// debug

	// Read each conversion channel and store its value in current index
	a2d_samples[0][a2d_idx] = ADCA.CH0RES;
	a2d_samples[1][a2d_idx] = ADCA.CH1RES;
	a2d_samples[2][a2d_idx] = ADCA.CH2RES;
	a2d_samples[3][a2d_idx] = ADCA.CH3RES;
	// Increment channel index and handle overflow
	//   --> not needed as we are handling all 4 in one go
	
	// increment sample index and handle overflow.
	a2d_idx++;
	// Handle sample index overflow
	if (a2d_idx >= A2D_MAXSAMP)
	{	// sample index overflow - reset
		a2d_idx = 0;
	}
	// No need to Set new conversion channel - this is triggered by the timer event.

	PORTA.OUTCLR = (1<<PA5_ADC5);	// debug

}

Adding the code for defines.


// ------ module controls ------------------------------------------------------
// The below is used in calculating calibrated values - change if ADC settings changed.
#define A2D_NBITS			12			// number of bits in ADC conversion
#define A2D_MAXVAL			4095		// maximum adc increments for number of bits
#define A2D_IDX_AMP_HIGH	0			// index to read amps from high side sensor
#define A2D_IDX_VOLTS		1			// index to read voltage for display
#define A2D_IDX_AMP_GND		2			// index for ground reference
#define A2D_IDX_AMP_LOW		3			// index to read amps from low side sensor
#define A2D_IDX_LAST		4			// used to generate the data arrays.

// ----------- Module scope defines and definitions -----------------------------
#define A2D_MAXSAMP		16			// storage space for multiple conversions for averaging, set modulo 2 value
#define A2D_DIV			4			// Nr of bits to right shift for devision to above nr of samples
#define A2D_MUXMASK     (1<<REFS0)	// a global channel select mask



The attached image shows the debugger view on the ADC sample arrays. For channel 0 array index entries 1,5,9,13 shows values that correllate very well with the values in channel 1 at the same time.

I am a bit stumped with this one.
Do I have a programming bug?
Is there a glitch in the hardware that bleeds through the sample cap values here? What is weerd is that channel 1 is reflecting the value of channel 2 - which is supposed to be sampled after it in time?

Any suggestions for investigation welcome.

Regards
Johan

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why is the 2nd array group all ~same value, unless ALL the channels are having crosstalk, not just ch. 1 & ch. 2 . What frequencies are these 4 signals, and what are the other 3 adc pins connected to ? Maybe you can delay the big signal conversion compared to the others. Also, try moving the biggie to another pin further away.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the questions indiana.

To clarify what you are seeing in the debug view - arrays. The array view is of a 2 dimensional array. a2d_sample[4][16]. So there are 4 sensors and 16 samples for each sensor. You are therefore viewing the 16 samples for sensor 0 and sensor 1. The values being measured are steady state signals = constant current and constant voltage. Therefore, I am expecting all the values in the sample array for a sensor / input to be virtually the same. So, in fact I am expecting the values in array [0][1..16) to be all around 203 in value, and this is exactly what is wrong with it.

I have worked through the following further things.

Played around with slower sample rates.

Someone in a thread somewhere stated that you should delay around 270 "nops" before reading the adc value registered. Trying various delays up to 300 nops between interrupt and reading made absolutely no difference - and is what I would expect in the conversion complete interrupt.

Used different settings for adc current consuption allowed.

Used a setup with no gain setting because this is supposed to discharge the sample cap between samples.

Used plain sweep and sweep with reset.

Moved the input signals around. Used a hard ground connected to signal 0, a small sigal connected to sensor 1, large signal on sensor 2, small signal on sensor 3. This made no change to the reading of sensor 0. It always have the spurious large reading. So, at the moment I have the first and the last signals = 0 and 3 connected directly to gnd, and still the same problem on sensor 0.

My conclusion is that there is something wrong with the sampling in the adc on the first signal in a sweep conversion. In fact, broken sampling or residual charge on the sample cap does not make sense with ground connected to the first and the last signals?? I would love for sombody to prove me wrong.

I am changing my code to how I have always been doing it on the AVR. Doing one conversion at a time and setting up the next signal to be converted in the interrupt responce of the current one. I have sunk a week into this. That's development hey, and what makes it interresting in the end.

Will keep updating this thread if I find out anything more of value.

Regards
Johan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just as a further note. I have implemented conversion using single channel conversion triggered by an event. In this, I read the value of channel 0 in an interrupt and then setup channel zero for the next input to convert. Using this approach, still storing 16 conversion values for a moving average, I have clean results on all my input channels.

From this I observe the following:

I do not get the spurious strange values in the 16 value oversample array as previously. Therefore, I belief my hardware driving the signals to the ADC input pins are without problems.

Also, with the sweep conversion, I was seeing fairly constant, but elevated value for input channel 1 (directly after channel 0). I was seeing an average value of 206 with sweep conversion, and I am seeing an average value of 195 with single conversion. (These all with zero input value).

This further confirms my thinking that there is a problem with the first channel on sweep conversion.

Regards
Johan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This further confirms my thinking that there is a problem with the first channel on sweep conversion.
If that's true then it should get wrong results even if one used a small signal instead of a large one. Did you try that just to confirm it ? I wonder if it gives wrong results for just adc_ch0, or if you used adc_chX, X != 0, as lowest channel.

I wonder what Atmel would say if you told them about this behavior...

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my last test setup decribed above, I had the following sequence of connected signals for both the sweep trigger and the single conversion tests:
PA0 - Aref voltage = 2.5V
PA1 - Chan0 for sweep - GND
PA2 - Chan1 for sweep - small signal = current sense (with zero current)
PA3 - Chan2 for sweep - Large, voltage signal
PA4 - Chan3 for sweep - GND

I had very large - of the order of 1500 to 2000 conversion results on chan 0 in this configuration.

I did not move the sweep to start at a higher channel.

Come to think of it - the voltage of channel PA0 is the reference voltage - 2V5, so there is a large "signal" on the input just before the start of the sweep.

I have moved on to another project at the moment, but will be able to test moving the the sweep to start on say PA2 in a day or two.

So, from the lack of responce to this thread I, am I to assume that nobody else is using channel sweep conversion setup and can share their experience?

Regards
Johan