ADC autorange

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

Dear friends,
I am on a project, that uses an atmega16 @ 16MHz
and a 16-bit bipolar ADC. Communication is made with SPI
@ 4 MHz.
The ADC is sampling a signal from an amplifier and the uC is able to change the gain of the amplifier.
In order to take advantage of the dynamic range I decided on using an autorange algorithm.
If the ADC value is lower than 500 or greater than 65000
the uC must change the gain to a lower value in order to avoid saturation. Now the tricky part is when to decide to increase the gain in order to "zoom" into the signal.
So after a lot of thought, mathematics and tables, I decided on using three arrays.
For simplicity let's consider we have 5 values of gain.

One contains the values where if the ADC value becomes lower than, the gain must be increased
The second contains the values where if the ADC value becomes greater than, the gain must be increased

That is because of the bipolar nature of signal and ADC, 0x0000 = -32767 and 0xFFFF = 32768

There is also a third array that is used as index for the checking routine

 //This is pseudo-code
unsigned char UpperG[] = {61277,57337,59895,53756,56442};
unsigned char LowerG[] = {4227,8170,5610,11756,9067};
unsigned char indexG[] = {3,2,2,1,1};
if ((ADC > 65000) || (ADC < 500))
{
  index++;
  //Now output new gain
  //based on indexG
; } if ((ADC > LowerG[indexG
]) && (ADC < UpperG[indexG
])) { index--; //Now output new gain //based on indexG
; }

Now the questions
a) Is there a more efficient way of doing something like autorange?
b) I know from my small experience in C that the indexing can be more easily done with pointers. Could you please point me (:lol:) to the right direction?

Thank you a priori.

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

Another question is if I could use the analog comparator of the atmega16 in order to make the decision to change or not the gain?

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

Using such an array an index is already quite efficient and flexible. The Values in the table don't look useful, as the lowest range should avoid going even lower. So the first entry should have limits impossible to meet, like 0 and 0. Also the other values don't look plausible: the lower and upper limit should be much closer together, more like 25000 and 38000. In addition there should be an upper limit.
Depending on the signal it may not be wise to switch to a higher amplification just because a single value would a allow this. Increasing gain may require something like 1000 values inside the range before increasing the gain.

As 16 Bit ADCs usually are not that fast, efficiency should not be that important.

Using the analog comparator should be possible for 1 of the 4 comparisons, but there are still 3 remaining, so it does not help much.

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

Thank you for your answer.
My real application uses 49 different settings of gain,
so I just entered the 5 of them.

Quote:
So the first entry should have limits impossible to meet, like 0 and 0.
This is quite correct.

Considering this

Quote:
the lower and upper limit should be much closer together, more like 25000 and 38000

the ADC MAX1135 input range is bipolar +/-2048 mV
so I have calculated some ADC values that would allow
to go to a lower gain or higher without compromising resolution.
I will try to implement something on
Quote:

Increasing gain may require something like 1000 values inside the range before increasing the gain

As to the efficiency I was referring to the .c program and the space in uC's flash more than the speed.

Thank you. I will try to do something simpler for start, like only 3 ranges - gains, and I will tell you.

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

I think the gain change algo is as simple as "if any sample outside of a +-31000 window, reduce gain. If any sample is within a +-100 window increase gain." volts=ad*gain, display volts.

Imagecraft compiler user

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

You haven't provided any info about how you adjust gain and what gains are available, but...

The ideal is to obtain a result with 15 significant bits, all else being equal.

The first conversion, at minimum gain, will often provide all the information you need to determine the gain required to obtain 15 significant digits.

Given a function which performs a conversion using a certain gain...

int adc_read(int gain);

The ideal gain (in this case) is very near...

gain = 32768 / abs( adc_read(1) );

Starting at that ideal gain, choose the closest available gain less than that.

Take a series of samples, increasing the gain to the next available, until you overflow, or reach maximum gain. The last non-overflowed sample is the best you can get.

This is a 'trick' often used with multimeters, which usually feature '1/2/5' gain steps.

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

Quote:
My real application uses 49 different settings of gain
:shock:

I would think you would have to have a rather unique project to benefit from so many gain stages.

You did not mention how fast the signal changes in amplitude, how fast you need to supply the reading to the User or other device, or how many samples you need per reading; all of which would be helpful to know.

I would look at the difference in reading for several "sample" signals of different voltages with your original plan of 49 gain stages, and with just 5 stages.

Then look at the real accuracy of your 16 bit ADC, and the accuracy on the internal amplifier, etc., and see if the difference in gain really impacts the accuracy of the measurement, all other sources of error being considered.

If you already have your ADC and micro hardware you should spend some time testing the accuracy of the system with different gains, and with different ambient temperatures, battery voltages, etc.

I suspect getting reproducible 16-Bit readings, regardless of the gain setting, will be more challenging than first expected.

JC

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

Quote:

You haven't provided any info about how you adjust gain and what gains are available, but...

ok here it is.
I have a transimpedance amplifier IVC102, meaning current to voltage amplifier, but instead of Vout = -i x Rf, with this amplifier Vout = -i x Tint / Cint,
where Tint is the integration time and Cint is the integration capacitor.
So with 3 capacitors available on chip, the available combinations with analog switches can make 7 different capacitors.
Now if I choose different integration times (us) like
1
10
100
1000
10000
20000
100000
1000000
I have 49 different gains (a few of them overlap).
Quote:

I would think you would have to have a rather unique project to benefit from so many gain stages.

Not quite unique, I just want to test if having 49 or just 5 gains
is really different. So I start with many in order not to miss something from the hardware implementation (overkill though) and to see as DocJC said
Quote:

getting reproducible 16-Bit readings
how challenging would it be.
What gives more push to thinking is
Quote:

The ideal is to obtain a result with 15 significant bits, all else being equal.

Thanks for your suggestions, I will try some of them the weekend and I will give you feedback. I really appreciate your help.

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

@ bobgardner
+/-100 means that in +/-200 values of ADC
I won't be taking advantage of the 16-bit ADC
Am I correct?

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

Just make it work just like an autoranging multimeter. 16 bits give about 4 decimal digits. So you can display from 9999, to 999.9 to 99.99 to 9.999 to .9999 to .0999 to .0099 to .0009 Seven decades. Increase or decrease the gain to keep the display filled.

Imagecraft compiler user

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

I send the data over usart to PC software
but I now see what you mean.
Thanks.

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

Usually switching gain only makes sense if the steps are relatively large, like a factor of 10 or at least 2. A 16 Bit AD works well with a range of 1:10 or at least 1:4. So there is no real need to switch the capacitors. Changing the capacitors only gives a modest change in amplification, but will likely introduces leakage currents, that cause more trouble than it helps.

As the time is used to set amplification, it should be possible to use a fixed factor (e.g. 2) between ranges. So the limits to compare with can be constant. Going up to a higher amplification can also use one value to go up by more than one step, e.g. increase the amplification by 2 or even more steps at once - this would do a speedup, though with more code.

At very low currents, it may help to combine the readings at 2 or 3 different timings to compensate for some errors.

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

Using the power of 2 gives chances for easy code optimization... thanks.

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

Auto gain algo for audio: fill buffer with samples. If 10% of samples are in the red (within 3dB of max) reduce gain 6dB. If 90% of samples are more than 12dB below max, increase gain 6dB.

Imagecraft compiler user

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

How big should this buffer be?
If it can hold 10 samples, only 1 sample can trigger the gain variation...
3dB of 32767 is about half (16422)...correct?
db = 10*log(P1/P0), where log is with base 10
so

Quote:

reduce gain 6dB

means divide by 2 the incoming signal or half the gain?

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

Yes, 6db is half the voltage. I guess the algo would work using the avg of N samples instead of a buffer. The idea is to make the display have more digits. If you have a 5 digit display, no sense in using a range that only shows one or two digits. But you dont want it banging back and forth all the time, so some averaging is needed.

Imagecraft compiler user

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

Quote:

But you dont want it banging back and forth all the time, so some averaging is needed.

This is something I was worrying about, but you just confirmed my worries...