ADC gives noisy readings on VF61

I am running code on the M4 core of the VF61 to read the ADC. I am getting very noisy readings.

First I have a question about the ADC reference voltage. I am setting REFSEL to 00 to use an external reference voltage. This seems to give me a full scale reading of about 3.31V. The reference can also be set to an alternate external reference voltage, or to an internal bandgap reference. Please explain exactly what is being used as the reference voltage with each of these settings, and what the full scale input voltage would be.

I am setting my other parameters as follows:

Clock = IPG, clock divisor = 8, mode = 12 bit, sample time = 8 clocks, no averaging.

When I set my input voltage to 1.65V (using a well regulated low noise DC power supply) I get readings around 2032. However, the readings are not very stable. I have seen readings as high as 2054, and as low as 1990. That amounts to a spread of 64 LSBs, or +22 to -42 LSB from the average reading of 2032. That means I am losing about 5 bits worth of precision, and am only getting about 7 bits worth of accuracy.

When I input 0 volts (by using a jumper between the analog input pin and the adjacent GND pin on the evaluation board) the reading jumps around from 0 to 7, so I am still losing 3 bits of precision.

My code for initializing and reading the ADC is as follows:

uint32_t init_adc(void)
	// set ADC_CFG, ADC_GC, ADC_HC0
	ADC0_CFG = ADC_CFG_ADICLK(0) | ADC_CFG_MODE(2) | ADC_CFG_ADIV(2)	// clock=IPG, 12 bit, clock/4
				| ADC_CFG_ADSTS(3) | ADC_CFG_REFSEL(0) | ADC_CFG_AVGS(0);	// sample 8 clocks, external ref, averaging not used
	ADC0_GC = 0; // no cal, continuous, averaging, dma or adacken
	ADC0_HC0 = 31;	// disable conversion
	ADC0_GC |= ADC_GC_CAL_MASK;	// start the calibration
	while ((ADC0_HS & ADC_HS_COCO0_MASK) == 0);	// wait for calibration done
	return ADC0_R0;	// this forces a read of the result register, but the returned data is meaningless

uint32_t read_adc(int channel)
	ADC0_HC0 = channel;	// start conversion
	while ((ADC0_HS & ADC_HS_COCO0_MASK) == 0);	// wait for conversion done
	return ADC0_R0;	// return result register

Is there anything I am doing wrong? How can I get better accuracy out of the ADC? Averaging multiple reading is not an option because I am sampling an AC signal and doing RMS conversion in software. Should I be using a different ADC clock or a different clock rate? Any suggestions would be appreciated. Right now the readings are so noisy as to be pretty much useless.

Dear @irbsd
As a quick test, I just did some measurements on the Colibri Evaluationboard under Linux:

  • I generated the voltage to measure by connecting a resistor divider

    • 470Ω to X10-1 (3.3V)
    • 270Ω to X14-2 (AGND)
    • common point to X14-1 (ANALOG_IN0)
  • I booted Linux and executed the following command to get 1000 readings of ANALOG_IN0

    seq 1000 | xargs -I -- cat /sys/bus/iio/devices/iio:device0/in_voltage8_raw

I measured variations from 1467 to 1480 (±7LSBs from average) which leaves me with 9 usable bits.

I currently can’t say where the difference in your setup is. Maybe Linux does some averaging, maybe you just have different software running or other peripherals connected, which can introduce noise in the measurement.

About the Reference Voltage

On the module, the circuit is as follows:

  • VREFH is connected to SODIMM pins 10/12 (AVDD_AUDIO)
  • VREFL is connected to SODIMM pins 9/11 (VSS_AUDIO)

both signals have a simple LC lowpass filter between the SODIMM pin and the VF61 CPU.

According to the VFxx reference manual, this is the only reference voltage supported (see ADCx_CFG field descriptions). Even though other options are mentioned, we intentionally did not use them.

Regards, Andy