Noisy ADCs

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

I have about a dozen MCU development boards from various manufacturers sitting around my lab. With one exception, the ADCs built into the MCUs on these boards generate lots of noise.

To attempt to characterize this problem, I wrote a small bit of test code that allocates an array of longs either 1024 (for a 10-bit ADC) or 4096 (for a 12-bit ADC) elements in size. I then set up a resistor voltage divider to place the input voltage to the ADC right in the middle of the ADCs input range. The code then continuously reads the ADC and uses the value read as an index into the counter array and increments that element.

After letting this code run for a few days, I can then download the contents of the array and use it to plot a histogram of the results.

I would expect to see a fairly sharp, well-defined spike right at the midpoint (512 for a 10-bit ADC or 2048 for a 12-bit), with some outliers at maybe +/- 20 counts on either side.

What I'm actually seeing are very wide Gaussian curves that reach all the way down to 0 and all the way up to the maximum limit of the ADC. The counts at the extreme ends are not trivial, either.

I'm at the point in a project where I have to design my own PCB. I have two questions:

1. Can anyone point me to an article or book describing how to properly design the analog section of an otherwise digital PCB to avoid noise issues on the analog side?

2. Is what I'm trying to do even realistic? Can a 10 or 12 bit internal ADC in a 32-bit MCU running at 100 MHz with UARTs, SPI buses, I2C, etc, running simultaneously, even hope to generate good results under these circumstances?

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

Quote:

the ADCs built into the MCUs on these boards generate lots of noise.

You surely mean they DETECT noise? I doubt the ADC's themselves are the source of it. (usually unstable power rails in fact).

If you are talking AVR then I assume you have read AVR040 and AVR042? Also each AVr datasheet has some guidance on best practice to reduce noise. Oh and most AVR ADCs can be coerced to make a reading with the CPU in SLEEP which may help too.

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

A/D checklist: run AVR from a battery. This eliminates power supply ripple. A/D runs from AVCC. Data sheet shows an LC filter. An RC filter might filter any ps ripple some. A cap on AREF will eat any noise on the vref. Don't run the time constant too fast. 0x87 with 16MHz xtal is 9600 samp per sec.

Imagecraft compiler user

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

There are special resistance (impedance) requirements for feeding voltage to AVR input. I bet your experiment did not fullfill these requirements. For best results using AVR ADC, the impedance should be below 10k ohms. So if you put 1 megaohm divider, of course it gives you bad results.

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

Quote:
2. Is what I'm trying to do even realistic?

If manufacturer states it is a X-bit ADC with Y bits of effective resolution at some Z conditions then yes, that is realistic.
ADCs are qualified in their datasheets.
mxslj

No RSTDISBL, no fun!

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

You should certainly NOT see the deviation you are seeing - all the way from 0 to full scale. I would check the following:

1) Is the reference stable? That includes a good cap on the Vref pin if you use the internal reference.

2) Is the source voltage for the divider stable? You don't say what the source is, but it should NOT be from Vref. The reference output is not cable of delivering much current, at all.

3) Source impedance - the resistance seen by the the ADC input should be no larger than 10K. For a resistive divider, that is the parallel combination of the two resistors in the divider.

4) Histogram access - is the code REALLY doing it correctly?

5) Are the divider connections reliable? For example, are you using a spring-contact prototyping board?

Readings of zero have a special implication. There is no value of Vref that will result in a zero reading except, possibly, that the reference is physically zero. This implies either that the input is zero, or an improper access puts data into the zero-indexed bin. Supply ripple will never give you a zero input because that probably implies that the MCU power supply voltage is zero. That leaves array access or divider connections.

Further, since you are seeing this on multiple processors (apparently including others besides AVRs), I would suggest that this also points toward a flaw in your testing, again, either software or hardware.

I'd say that you have some hardware or code problem.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

In addition to all of the above, make sure you are not ding any digital I/O on the PortA during an ADC reading. Cliff touched on this when he mentioned putting the uC in sleep mode. Avoiding PortA digital I/O is a half way step to the sleep state.

Most real world apps have a Shannon-Nyquist filter on the input to the ADC. That will remove some of the HF noise, but it won't account for your readings.

As mentioned above, the input signal to the ADC and Aref both significantly impact the reading. Feeding the divider from Vcc means you are starting with a pretty noisy input signal. A small cap across the lower resistor helps in this regard, but doesn't fix the problem.

What did you use as the ADC reference?
What value resistors did you use for the divider?
What was your power supply?

Getting a lot of values from 0 to ADCmax isn't at all what is expected, or what I've ever seen in my projects. You should get about what you stated above was your expected output.

I would also suggest you put a 10K pot from Vcc to Ground, and a 0.1 uF on the wiper to ground, and feed it into the ADC input.

Display your readings on an LCD, or upload to a PC for a real time plot and readout of the data. As you vary the pot from end-to-end the ADC readings should correlate nicely. (Note that some pots are noisy / erratic at the very end points).

Also, on the AVR, do you have AVcc timed to Vcc, preferably through an LC filter?

Also does EVERY Vcc/Ground pin pair, and AVcc/Ground, have a by-pass cap across the pins, very close to the micro's pins? Does ARef have a cap to ground?

JC

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

All that is true, though somewhat AVR-specific. The OP says that this behavior occurs with MCUs from several manufacturers.

The mere fact that the histogram includes one or more readings of zero makes it very, very suspect. There is no reference drift, Vcc noise, or anything in internal hardware or power supply that can produce a reading of zero. And, very little in external hardware. I listed one possible external implementation problem that might do it. And, a potential software problem (conceptually).

One additional possibility: Is the ADC input even connected to the divider? That is one of the few things that might cause a distribution like that, though I would somewhat expect uniform rather than Gaussian distribution.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

"I'm at the point in a project where I have to design my own PCB."

So I take it you are running it off a breadboard? If so then get rid of the breadboard, and redo your histogram using protoboard/wirewrap or better yet with a PCB you designed based on your best efforts. Breadboard is horrible when it comes to adding noise to ADC samples.

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

I like to test the a/d stuff with a 10K pot. Should see a smooth increase from 0000 to 1023 on the serial or lcd or whatever as you turn the pot up and down. The last digit might dither a little.

Imagecraft compiler user

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

Here is some additional information and my responses to the questions asked in the thread:

The development boards are powered by one of my HP bench power supplies (one at a time). It's a linear supply which can supply up to 7A at up to 15V, which is more than any of these boards require. I've also powered the boards from a 7.2v battery and get the same anomalous results.

Voltage for the divider is coming from either the power supply directly, or from the output of the 5v regulator on the boards. It looks stable on my scope. I've also created it using a battery. I'm following the MCU datasheet recommendations regarding impedance of the input to the ADC for each of the MCUs on these development boards. I initially used a solderless breadboard for the divider resistors, but later soldered the resistors directly to the ADC inputs dead-bug style where possible.

The test code is written in C and is common to all of the development boards I've tried it on. The code does the allocation of memory for the histogram array and sits in a loop reading the ADC. The code that reads the ADC itself is custom for each MCU and is in a separate function. I'm sampling the ADCs at rates far slower than they actually support, and I make sure the hold time requirements (if any) are met. I'm confident that this code is correct because on at least one of the development boards (AVR32 EVK1100) I get the histogram results I expect.

I'm not using any other ports on the same port used by the ADC. I am, however, doing SPI, UART, and I2C transfers simultaneously on other ports. The application can't completely stop all of this other activity while reading the ADC.

I did a quick and nasty test over the weekend with an external ADC (MCP 3208) that I found in my parts box. I plunked it down on a solderless breadboard, along with the resisters for the voltage divider, and ran the SPI lines over to the development board. I ran this setup all weekend on both ARM Cortex-M3 and Pic32 boards that exhibit the issue with their internal ADCs. In both cases, the results were good -- almost all of the data points were at 2048 with a few outliers, but nothing like what I've seen with the internal ADCs on some of these development boards. This shows that my basic code is correct.

As for the fact that I'm getting numerous counts down at zero, I can't explain this either. If I modify the code to have it write the ADC count to an LCD, it looks correct 99% of the time, but I can see it jump around to seemingly random values occasionally. In fact, this is what led me to investigate the ADC behavior. I had a thermistor circuit connected to the ADC input of one of my dev boards and had it displaying the temperature on an LCD. I noticed that occasionally the temperature took on completely impossible values.

At this point I'm pretty confident that my test code and my ADC driver code works correctly. I may have a hardware issue with how I'm connecting the voltage divider or how I'm supplying power to the driver, however, but I'm also not confident that this is not an issue with how some of these development boards are designed.

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

It is worth a shot as it happened to me a few times, maybe the uC is resetting itself every so often, it could be due to the stack overflowing or the watchdog timer.

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

toalan wrote:
It is worth a shot as it happened to me a few times, maybe the uC is resetting itself every so often, it could be due to the stack overflowing or the watchdog timer.

I'm pretty sure that's not happening, but I'll check.

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

Something sounds terribly suspect. I'd be digging a bit deeper as your evidence suggests there are other issues at play that you may not have considered. If you show your code for the AVR then others can repeat your tests and report.

The onboard adcs are usually a compromise regarding analog performance as they need to be fabricated on a process that supports flash and coexist with high speed digital. Nevertheless, you still need to filter the adc data regardless of whether it is internal or external - the real world is full of transients and other sources of noise.

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

Have any way to read one channel and print it out in a loop?

Imagecraft compiler user

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

I'd be suspicious of conflict with the other processes. Can you kill those and try the bare ADC? It is possible that you are not managing the coexistence of those in a way that would avoid errors.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Big data buffer gets written over by the stack. The compiler I use everyday (Imagecraft) has a _Stackcheck that returns a code when the stack walks into the bss. GCC got something like that, or do you need to roll your own?

Imagecraft compiler user

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

Bob's hypothesis is as good as any, at this point.

However, since SodaAnt has tested with a variety of MCUs, presumably with a variety of RAM sizes, it seems a bit of a stretch. It would be really interesting to know which MCU did NOT exhibit this, and which ones did.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

ka7ehk wrote:
It would be really interesting to know which MCU did NOT exhibit this, and which ones did.

Here's the list:

Ones that exhibit this problem:

ARM Cortex-M3 & M4
PIC32

One that didn't:

AVR32UC3

I have multiple PIC32 and Cortex M3 and M4 dev boards from various manufactuers, and while they all exhibit this to one degree or another, some are better than others.

The AVR32 is on an Atmel EVK1100.

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

yikes

Imagecraft compiler user

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

This is way over the heads of most of us, here. I was expecting an AVR, a PIC, and an MSP430! Or, something along those lines.

How much RAM, data section, and stack?

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Cortex M3,M4 - that narrows down the field a little.....NOT!
I have an extensive range of dev boards from various manufacturers that I can test with. Let's see some test code and the test process - if the adcs are really as bad as you say, one would've expected significant negative feedback by now.

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

I have tested internal 12-bit ADC running at max speed (1Msps), core running at max speed (32MHz), all power saving modes disabled.

Internal temperature sensor:

Max: 665 Min: 659
Max: 666 Min: 659
Max: 665 Min: 659
Max: 665 Min: 659
Max: 665 Min: 659

Internal reference:

Max: 1686 Min: 1679
Max: 1687 Min: 1679
Max: 1687 Min: 1680
Max: 1688 Min: 1679
Max: 1687 Min: 1679
Max: 1688 Min: 1679

External ADC1_Ch5 pin, no input filtering, just 4k pot attached:

Max: 2656 Min: 2647
Max: 2656 Min: 2647
Max: 2657 Min: 2648
Max: 2657 Min: 2648
Max: 2658 Min: 2647
Max: 2657 Min: 2648
Max: 2658 Min: 2649
Max: 2657 Min: 2649

The printfing code:

while(1){
uint32_t sample_counter = 100000UL;
uint16_t ADC_min = 0xFFF; //for 12-bit ADC 0xFFF is max
uint16_t ADC_max = 0x000;

do{
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //wait for some data
ADC_data = ADC_GetConversionValue(ADC1);
if(ADC_data > ADC_max){
ADC_max = ADC_data;
}else if(ADC_data < ADC_min){
ADC_min = ADC_data;
}
}while(--sample_counter);
printf("Max: %u Min: %u\n",ADC_max,ADC_min);
}

EDIT: Forgot to add: STM32L-Discovery

No RSTDISBL, no fun!

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

I'll do some experimenting tonight. I'll cut the code down to the bare minimum necessary to read the ADC and to update the histogram data. I'll do it all with polling and leave interrupts disabled.

If the above works, then that rules out the MCU (but maybe not the PCB layout) and I'll start to add more functions (UARTs, SPI, I2C, etc.) back into the mix until it starts failing again.

I'm hoping that I'm doing something fishy and it's not that these MCUs are just not able to read internal ADCs when lots of other stuff is going on without generating garbage results.

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

Quote:

If the above works, then that rules out the MCU (but maybe not the PCB layout)...

I can only speak in depth about AVR8. Many discussions on "crappy results" over the years.

But certainly not "rail-to-rail" anomaly results as you first reported. In the end I think you might find a coding situation with e.g. re-used temp variable or the like.

Anyway, on an AVR8, the 10-bit ADC is much maligned IME. Sure, it is only 10 bits. Indeed, the conversion rate is modest at best. And there are situations requiring multiple conversions on internal channels, as well as signal "bleed through" from channel to channel when using the mux.

However, as far as the actual conversion goes, with a squeaky-clean analog subsystem, near-10-bit results can be obtained (for unipolar work).

A vociferous bashing of the AVR8 ADC generally leads to the finding that the analog subsystem is >>not<< squeaky clean. E.g., ripple on reference or ripple on signal.

Real world signals have ripple, especially when they come in on wires. Simple averaging of a few readings on a slow-moving signal typically ends up with a nice stable average result.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

OP wrote:
To attempt to characterize this problem, I wrote a small bit of test code that allocates an array of longs either 1024 (for a 10-bit ADC) or 4096 (for a 12-bit ADC) elements in size.

You can use statistical properties of the process: standard deviation or variance, noise density, bandwidth etc to compare ADCs in uCs (ignoring other properties such as non-linearity, offset, current consumption, package, channel count etc).

Thinking in terms of:

"resolution(ADC_A) > resolution(ADC_B) ==> ADC_A is better than ADC_B"

is naive as you can easily trade bandwidth for resolution.
The problem is (at least) two-dimensional and observing one of its dimensions is not really objective.

For example you can have an 8-bit ADC that would outperform a 12-bit one if the 8-bitter is fast enough (just average returned samples).

A more adequate way to qualify ADCs is to calculate how much information those return.

"The more information ==> the better ADC is"

In case of ADCs we can calculate standard deviation (which is sometimes called "sigma", that is the one from "six sigma" rule).
From that (and the bandwidth) you can calculate the information that ADC provides.
If you want to know how many bits a fixed ADC value may return then 6*sigma is where ~99.8% of samples fall into (assuming a Gaussian probability distribution).

Actually standard deviation is not frequently used. It is its square, called variance, that is more common.

I have written a simple code to run-time calculate the variance of ADC on any fixed-point tiny. RAM requirement is really small (32 bytes or so). Should fit into ATTiny13 (the last division can be made by shifting), the code was tested on mentioned STM32L152.

//Statistical properties of ADC conversion.

	/**
	Calculating E(X) a.k.a. Expected Value of a process X does not require
	significant amounts of memory or CPU as this is only an arithmetical average of samples.
	It is enough to sum all samples and then divide by sample count at the end.

	Calculating V(X) = E((X-E(X))^2) a.k.a. Variance of X from V(X) definition is heavy
	as we have to store samples, calculate float E(X) first, then from that calculate X-E(X),
	square, floats and errors.

	Fortunately after some algebraic transformations of the above V(X) equation we get:
	V(X) = E(X^2) - (E(X))^2	( lets name it: "Variance = EX2 - E2X" )
	That one is easy and fast to calculate even when using only fixed point uC.
	Additionally there are no numerical errors involved and only a single division by constant
	(a shift) is needed at the end.
	*/

	//Number of samples to take.
	#define ADC_SAMPLES 0x100000ULL			//2^N possibly
		assert(ADC_SAMPLES <= 0x100000ULL);	//That is for 12-bit ADC samples to fit into uint32_t

	while(1){
		uint32_t Sample_Downcounter = ADC_SAMPLES;
		uint32_t Summed_Samples = 0;
		uint64_t Summed_SqSamples = 0;

		do{
			while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//wait for some ADC data

			uint16_t Sample;
			Sample = ADC_GetConversionValue(ADC1);
			Summed_Samples += Sample;

			uint32_t SqSample;
			SqSample = (uint32_t)Sample * Sample;			//uint32_t > uint16_t * uint16_t
			Summed_SqSamples += SqSample;

		}while(--Sample_Downcounter);

//		//Expected Value (if ever needed):
//		float Expected_Value;
//		Expected_Value = (float)Summed_Samples/ADC_SAMPLES;
//		printf("%6.4f	",Expected_Value);

		//EX2 and E2X multiplied by constant (ADC_SAMPLES*ADC_SAMPLES)
		uint64_t EX2_times_SqADC_SAMPLES, E2X_times_SqADC_SAMPLES;
		EX2_times_SqADC_SAMPLES = Summed_SqSamples * ADC_SAMPLES;				//A shift really
		E2X_times_SqADC_SAMPLES = (uint64_t)Summed_Samples * Summed_Samples;
			assert(EX2_times_SqADC_SAMPLES >= E2X_times_SqADC_SAMPLES);			//Always holds

		//Variance multiplied by constant (ADC_SAMPLES*ADC_SAMPLES)
		uint64_t Variance_times_SqADC_SAMPLES;
		Variance_times_SqADC_SAMPLES = EX2_times_SqADC_SAMPLES - E2X_times_SqADC_SAMPLES;

		//Variance:
		float Variance;
		Variance = (float)Variance_times_SqADC_SAMPLES / (ADC_SAMPLES * ADC_SAMPLES);	//Another shift.

		printf("%6.4f\n",Variance);



	}

Bug reports always welcome.

I will report some output data later.

No RSTDISBL, no fun!

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

I ran a test last night on an LPC-1768 dev board with the code cut down to the bare minimum to set up the processor clocks, initialize the ADC, and sit in a loop reading the ADC and tracking each reading by incrementing a bucket for each of the 4096 possible values returned by the ADC. All of this was done with interrupts disabled and no other I/O running on the MCU.

I let it run overnight and it accumulated 2.25 billion ADC reads. This was at a rate of around 73K ADC readings a second. The ADC on this MCU is rated at 200K per second. The ADC was polled and the next conversion not started until the previous one completed.

The results are interesting. Almost every one of the 4096 counter buckets had at least one hit, some many thousands, even ones far removed from the expected value of 2048.

The average of all 2.25 billion readings was 2049.12, which is very close to the expected value, and plotting all of the results shows a sharp spike centered on 2048, but with millions of outliers. For example, the reading 4087 had over 100,000 hits. The bucket at 0 had 106 hits.

Since the average was exactly what I expected, I can average over a few hundred readings at run time to get this, but I'd still like to understand what's going on here, especially since the AVR32 on my EVK1100 doesn't show this, nor does using an external ADC ship (MCP3208).

The possibilities I can think of at this point are:

* I'm not setting up the processor clocks correctly and am therefore violating the ADC clock max of 13 MHz.

* I'm not initializing the ADC correctly and this is leading to the erroneous readings.

* The board is introducing noise into the ADC input.

* (Least likely) the ADC peripheral is poorly implemented.

Here's the code (all concatenated together):


volatile unsigned long guard1[32];
volatile unsigned long buckets[4096];
volatile unsigned long guard2[32];

int main(void)
{

unsigned long reset_cause; // reset cause bits
volatile unsigned long j;

reset_cause = 0;

GlobalIntDisable(); // disable interrupts

j = 500000UL;

while(j--)
; // delay to allow JTAG to get control before messing with processor clocks

guard1[0]++; // write to guard arrays to force linker to include them in output file
guard2[0]++;

reset_cause = HardwareInit(); // Initialize hardware
ADCtest(); // start ADC test

return 0 ;
}

void
ADCtest(void)

{

unsigned long sample;
unsigned long totalSamples = 0;

ADCInit();
fillmemw(&buckets[0], 4096, 0);

while(1) {
sample = ADCReadPoll(0);
if(sample > 4095)
continue; // ignore out of range ADC sample

buckets[sample]++; // increment histogram bucket count
totalSamples++;
}

}

#include "include/LPC1768.h"

void
fillmemw(unsigned long *p, unsigned long cnt, unsigned long pattern)

{

while(cnt--)
*p++ = pattern;

}

void
Busfault_ISR(void)

{

while(1)
;

}

void
Usagefault_ISR(void)

{

while(1)
;

}

unsigned long
HardwareInit(void)

{

unsigned long reset_cause;

reset_cause = RSID; // read reset source ID register
RSID = 0x0f; // clear reset source ID register

// Initialize the CPU clocks and PLL

PLL0CON &= ~PLL0CON_PLLC0; // turn off PLL0 connect bit in the PLL0 Control register
PLL0FEED = 0xaa;
PLL0FEED = 0x55; // send PLL0 feed sequence
PLL0CON &= ~PLL0CON_PLLE0; // turn off PLL0 enable bit in the PLL0 Control register
PLL0FEED = 0xaa;
PLL0FEED = 0x55; // send PLL0 feed sequence

SCS = 0; // clear Systems Controls and Status register (sets main oscillator range to 1-10 MHz)
SCS = SCS_OSCEN; // enable main oscillator

while(!(SCS & SCS_OSCSTAT))
; // wait for main oscillator to be ready

CLKSRCSEL = 1; // set main oscillator as clock source

PLL0CFG = (25 - 1) | ((2 - 1) << PLL0CFG_NSEL0_BIT); // set M and N (as M-1 and N-1) in PLL0 Config register
PLL0FEED = 0xaa;
PLL0FEED = 0x55; // send PLL0 feed sequence

PLL0CON |= PLL0CON_PLLE0; // turn on PLL0 enable bit in the PLL0 Control register
PLL0FEED = 0xaa;
PLL0FEED = 0x55; // send PLL0 feed sequence

CCLKCFG = 3 - 1; // set CPU clock to divide by 3 (CPU clock is now 100 MHz)

while(!(PLL0STAT & PLL0STAT_PLOCK0))
; // wait for PLL0 lock

PLL0CON |= PLL0CON_PLLC0; // turn on PLL0 connect bit in the PLL0 Control register
PLL0FEED = 0xaa;
PLL0FEED = 0x55; // send PLL0 feed sequence

return(reset_cause);

}

#include "include/LPC1768.h"

void
ADCInit(void)

{

PCONP |= PCONP_PCAD; // Enable ADC in the Power Control for Peripherals register
AD0CR |= AD0CR_PDN; // Enable the ADC in the ADC control register
AD0CR = (AD0CR & 0xffff00ff) | (3 << AD0CR_CLKDIV_BIT); // Set ADC clock to PCLK_ADC0 / 4 = 25 MHz/4 = 6.25 MHz
PINSEL1 = (PINSEL1 & ~PINSEL1_P0_23_MASK) | (0x01 << PINSEL1_P0_23_BIT); // Select the ADC function on P0.23 (ADC0)
PINMODE1 = (PINMODE1 & 0xffff3fff) | (0x02 << PINMODE_OD1_P1_14OD_BIT); // Set mode to neither pull-up or pull-down

AD0INTEN = 0; // All ADC interrupts disabled

}

unsigned long
ADCReadPoll(unsigned char channel)

{

unsigned long sample;

if(channel > 7)
return(-1); // Channel must be in range 0-7

AD0CR &= AD0CR_CLKDIV_MASK | AD0CR_PDN_MASK; // Clear all but clkdiv and PDN bits in control register
AD0CR = (AD0CR & 0xf8ffff00) | (1 << channel); // Set channel number to sample
AD0CR = (AD0CR & 0xf8ffffff) | (1 << AD0CR_START_BIT); // Start conversion on selected channel

while(!((sample = AD0DR0) & AD0GDR_DONE))
; // Wait for conversion complete

AD0CR &= ~(0b111 << 24); // clear start bits in control register
sample = (sample >> 4) & 0xfff; // Isolate result bits (12)
return(sample);
}

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

Quote:
but with millions of outliers.

Interesting results. IMHO there must be something wrong with your settings.
Did you read errata of that uC? Perhaps it has some Si bugs?

One of the problems is that I do not have LPC1768/9 any more. I won't be able to test our code.

I can run min/max/E(X)/V(X) code on STM32L152 and log some values.

Knowing that the variance at that speed is ~0.6 of a bit then six sigma (99.8%) should have ~4.6 bits of spread (that is +-2.3 bits).

Each line is calculated with 0x100000 ADC samples taken, columns are: min max E(X) V(X)

2054	2067	2059.1917	0.5703
2054	2065	2059.2737	0.5815
2054	2067	2059.2595	0.5704
2054	2066	2059.2551	0.5683
2052	2069	2059.2351	0.5637
2052	2066	2059.1938	0.5645
2053	2069	2059.1206	0.5491
2052	2066	2059.1211	0.5453
2053	2067	2059.1431	0.5424
2053	2067	2059.1978	0.5549
2053	2066	2059.2261	0.5611
2053	2065	2059.2339	0.5694
2054	2065	2059.2556	0.5790
2053	2064	2059.2659	0.5778
2054	2066	2059.2690	0.5740
2053	2065	2059.2776	0.5786
2053	2067	2059.2827	0.5828
2053	2067	2059.3018	0.5897
2054	2066	2059.3105	0.5849
2054	2066	2059.3235	0.5885
2055	2065	2059.3313	0.5916
2055	2065	2059.3325	0.5908
2055	2065	2059.3423	0.5892
2053	2067	2059.3560	0.5982
2054	2065	2059.3645	0.6000
2054	2065	2059.3623	0.5981
2053	2066	2059.3564	0.5974
2053	2066	2059.3704	0.5967
2052	2066	2059.3867	0.6008
2053	2066	2059.3804	0.6047
2054	2065	2059.3713	0.6061
2055	2065	2059.3906	0.5973
2054	2065	2059.4041	0.5933
2053	2065	2059.4099	0.6057
2053	2065	2059.4094	0.6073
2053	2067	2059.4023	0.6064
2054	2067	2059.4153	0.6055
2054	2067	2059.4292	0.6103
2054	2065	2059.4241	0.6089
2054	2066	2059.4263	0.6082
2054	2065	2059.4111	0.6139
2054	2066	2059.4199	0.6084
2055	2064	2059.4268	0.6074
2053	2065	2059.4211	0.6017
2054	2065	2059.4128	0.6051
2053	2066	2059.4224	0.5992
2054	2065	2059.4260	0.6033
to be continued

Looks like ADC is heating up slowly..

No RSTDISBL, no fun!

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

I'll check the chip errata tonight.

Statistics on the run I did last night:

Variance: 11356
Standard Deviation: 106.6

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

Quote:
Variance: 11356

Misery.

That is more like a decent noise generator.

No RSTDISBL, no fun!

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

More data:

I dug another LPC-1768 development board out of the closet that I forgot I had. It has the same MCU as the previous board, so I downloaded my earlier code to it without change, switched the power supply over to it, and ran the ADC test routine.

I was only able to collect around 135 million samples so far, but wow! what a difference. I'm seeing exactly what I expected to see: a narrow peak right around 2048 with a few outliers very close to 2048.

The stats for these 135 million samples are:

Variance = 1.088
Standard Deviation = 1.04

Looks like my other LPC-1768 board has a noisy analog section and acts more like a random number generator.

The board that works is from Futurelec, and I stuffed it in the closet because of a number of quirks. I really haven't found any development boards yet that I like 100%. This Futurelec board has two main issues. Firstly, the designer didn't break out all of the MCU pins to accessible test points, so it's hard to debug things like the SD card interface because I can't easily get at the pins with my logic analyzer probes. The other issue this board has is the placement of the SD card connector. It's the type of SD connection that swings open to allow insertion of the card. The grounded metal cover swings open towards the middle of the board and guess what it hits when fully open? Yep, a 3.3v 0.1" male pin. Creates nice sparks when the cover hits that pin.

One of my favorite dev boards is the Atmel EVK1100, but that board breaks the MCU pins out on idiotic 0.05" headers.

While scrounging for the Futurelec board, I also found a PIC32 dev board made by Digilent. I ported the ADC test code over to it and I let it run overnight and see how it does.

That gets me back to one of my original questions: anyone know of a good article or book on how to lay out an analog section on an otherwise digital PCB to avoid problems like I've been seeing with noise on the ADC lines?

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

Can't help you with the article - but I did some investigation last year with small P*C16 ADCs and found them usable but not wonderful.

In particular, they're not consistent between inputs, they're temperature sensitive, and they don't go to either rail by a few bits, as they are advertised to do. However, by picking the right input and using AC signal inputs they are usable, and best at the recommended sample rates.

The original use I had required precision DC measurement and they're not useful for that in my high temperature (well out of chip spec) environment.

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

Quote:
but wow! what a difference.

What about min/max then? Was it within several ( 6 - 8 ) sigma range or were there outliers as before?

Quote:
Variance = 1.088

Now another important "simplification" we made so far in our comparisons is that we didn't include the quantity of the samples provided, only the quality was characterized. One might think that:

Variance_of(ADC_A) > Variance_of(ADC_B) ==> ADC_B is better than ADC_A

That is exactly the same naive inference as in the previous statement but made along the second perpendicular axis of two-dimensional problem.

In the above 12-bit examples, if ADC_A has variance of 1.1 and ADC_B has variance of 0.6 then it is not that bad, both have +- about same comparable performance, right?

Wrong.

The variance can be changed by filtering (traded for bandwidth, both up and down).
The simplest (and optimal) way to cut variance down is to average samples. With every N samples averaged you get the 1/sqrt(N) reduction in variance. So averaging 2 samples cuts the variance in 0.707, with 3 samples cuts it in 0.577 and with 4 cuts in 0.5 etc.

Back to comparing: both ADCs are 12-bit.

    ADC_A from LPC provided 70ksps with variance 1.1 bit ADC_B from STM provided 1Msps with variance 0.6 bit

If the ADC_B were to provide 70ksps of data, then with averaging it would have the resulting variance of 0.6*1/sqrt(1Msps/70ksps) = 0.16

Now you see that there is still something wrong with your ADC from LPC.

Variance is a measure of the energy a signal (here noise) carries. It is additive and thus very intuitive. So if instead of hardwiring your ADC to some *fixed voltage now you attach ADC to something that has variance of noise = 1.5 then the output is going to have variance of 1.1+1.5=2.6

*Do not you worry, even plain resistors generate noise so from the above variance of 1.1 some of it comes from the potentiometer, some from noisy ADC reference and only some from the ADC itself.

No RSTDISBL, no fun!

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

Quote:

Now you see that there is still something wrong with your ADC from LPC.


While I indeed average some samples in my AVR8 applications, Indicated counts (or the resulting control variable in user units) will stay rock-steady or maybe bobble LSB with a steady input.

If my AVR8 apps had outliers such as SodaAnt is seeing, then it would move the average and the displayed value wouldn't be steady.

Something else is going on, IMO/IME. Glancing at Brutte's tests it looks like +/- a few LSB and no outliers.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Brutte wrote:

What about min/max then? Was it within several ( 6 - 8 ) sigma range or were there outliers as before?

The full range of buckets around 2030 (which is where the peak was) is as follows. There were no non-zero counts anywhere else.

2015,0,
2016,0,
2017,0,
2018,0,
2019,0,
2020,0,
2021,0,
2022,0,
2023,0,
2024,0,
2025,118,
2026,11912,
2027,81210,
2028,54940991,
2029,25385021,
2030,44726084,
2031,8653107,
2032,1090924,
2033,201083,
2034,4181,
2035,0,
2036,0,
2037,0,
2038,0,
2039,1,
2040,0,
2041,0,
2042,0,
2043,0,
2044,0,
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
2039,1,

Oops.

Quote:
2028,54940991,
2029,25385021,
2030,44726084,

That looks suspicious.
Nonlinearity of some kind.

theusch wrote:
Indicated counts (..) will stay rock-steady.

Both ADCs may provide same information (plus some noise) but the LPCish one injects *almost 7 times as much noise as the one from STM. That does not look right to me.

As for "rock-steady" what do you mean by that?
That the variance of noise is below 0.027 of a bit?
Then standard deviation (so called sigma) is sqrt(0.027)= 0.16 and six times sigma is below 1 bit. In such case, depending on E(X), you would observe those 99.8% of samples return only one integer value.

Measuring noise in such V(x)< 0.027 case is tough since the quantization errors of ADC dominate. If ADC returns mainly one count then that does not mean there is no ADC noise - it is just that the bastard is "hiding" behind one ADC bit.

Here, just for you, results of a 10-bit conversion (1.066Msps) of the mentioned "hidden" case, columns are: min max E(X) V(X)

544	546	 544.9871	0.0263
544	546	 544.9868	0.0265
544	546	 544.9871	0.0259
544	546	 544.9869	0.0263
544	546	 544.9866	0.0261
544	547	 544.9862	0.0264
544	546	 544.9861	0.0261
544	546	 544.9854	0.0267
544	546	 544.9861	0.0263
544	546	 544.9860	0.0259
544	546	 544.9859	0.0262
544	546	 544.9858	0.0265
544	547	 544.9855	0.0263
544	546	 544.9850	0.0264
543	547	 544.9882	0.0258
542	547	 544.9880	0.0257
544	546	 544.9877	0.0257
544	547	 544.9871	0.0260
544	546	 544.9875	0.0256
543	546	 544.9869	0.0261

*I also do not know the details of OP's PCB but STM32L are forged with separate:

    VCC/VSS for powering digital domain (core, memory etc) AVCC/AVSS for powering analog domain (opamps, comparators, ADC etc)
    AREF+/AREF- for voltage reference for ADC and comparators.
The problem is that the one I have is an economy LQFP64 version which has AREF+/AREF- pins tied to AVCC/AVSS inside of an epoxy. With 15ksps that is ok but when ADC is running at 1Msps, it shakes AREF lines..
Well, that is $15 STM32L-Discovery. All beefier STM32L packages have those pins separate.

No RSTDISBL, no fun!

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

The full run overnight on the Futurelec board wasn't as clean as I would have liked. There were 2.75 billion samples recorded, centered at 2030. This board has a pot connected to the ADC input, and 2030 was a close as I could get it to 2048 tweaking it by hand.

Mean = 2029.07
Variance = 1.081
Standard Deviation = 1.040

There were two hits at 1023, one each at 1165, 1407, 1451, 1527, 1535, 1777, 1789, 1978, 1983, 2112, and three at 1919.

Disappointing, but nowhere near as bad as on the other board.

As of this morning, the the Digilent PIC32 board was running cleanly with no outliers beyond +/- about 10 counts from the center. Since the ADC on the PIC32 is slower, I decided to let it run longer before dumping and analyzing the data, but so far it looks good.

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

Does microchip sell any arms, or just the mips?

So AtmelFreaks will be about AVRs and ARMs, but not HC11s, HC512s, MSP430s, PICS, or MIPS?

Imagecraft compiler user

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

Quote:

Does microchip sell any arms

Only to dissident rebels.

What does it say at arm.com, Bob?

Quote:
For a list and description of each Partners standard parts, and information on how to buy, click on the company logo.

Do you see the Microchip logo on the page?
http://www.arm.com/products/buyi...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

Disappointing, but nowhere near as bad as on the other board.


The previous posted results had only one modest outlier.

I've got a suggestion, just to see: Modify the procedure to do say 100 conversions first, and then jump into the long test. "first conversion" can often be an outlier, and even n/a. For example on AVR8, with a good-sized cap on AREF and switch to internal reference, it may take some time and/or conversions to get the reference settled down.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:
Mean = 2029.07
Variance = 1.081

As long as V(X) is big enough (bigger than ~0.25) there is no necessity with picking specific E(X), any one will do. There won't be a big difference in V(X) if you pick E(X)=2029.0001 or E(X)=2029.4999 then.

Quote:
There were two hits (..) Disappointing, but nowhere near as bad as on the other board.

The outliers do not come from ADC noise. More like some intermittent short on a PCB. Or Si flaw. Or Gremlins.

Quote:
reset_cause = HardwareInit();

I assume you run the test under a control of a debugger and you have a breakpoint set at this line, haven't you?

EDIT: It occurred that significant part of the output noise and V(X) trends come from the carbon pot. When briefly squeezed the V(X) drops.

Attachment(s): 

No RSTDISBL, no fun!

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

Do you have a digital scope or Logic analyzer, I would hook the scope/LA to the test voltage going to the ADC, and set the trigger so that it starts to acquire if the voltages hit some threshold. Run it over night, in the morning if the see that the scope/LA has acquired data, then you know there is some issue causing real voltage spikes on the ADC input.

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

I can observe the V(X) on the output and I have noticed that it is very easy to ruin ADC measurements. Not only by tying AVCC to AREF+. The analog voltage signal is another factor but also all the wiring that hangs nearby influences that. I have also made some experiments with driving MCO (2MHz 50% PWM in my case) through some unused IO and that increases the V(X) of measured pot by some 30%! OTOH nothing jams ADC better than my soldering gun.

So my suggestion would be to start from disabling any IOs and measuring some internal signal (bandgap reference, temperature etc) just to eliminate other sources of the problem.

No RSTDISBL, no fun!

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

No RSTDISBL, no fun!