RC Lowpass Filter between Amplifier and ADC input

MrChips

Joined Oct 2, 2009
24,621
sum is just a holding register. Make sure the data type of sum is large enough to be able store N averages.
Begin with

sum = 0

Over time sum will grow and in fact will be equal to N x average.
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
sum is just a holding register. Make sure the data type of sum is large enough to be able store N averages.
Begin with

sum = 0

Over time sum will grow and in fact will be equal to N x average.
I see that there are multiple techniques to calculcate a moving average for an ADC.
Is there a certain one you would recommend or you do have good resources or tutorials for?
 

bogosort

Joined Sep 24, 2011
678
I see that there are multiple techniques to calculcate a moving average for an ADC.
Is there a certain one you would recommend or you do have good resources or tutorials for?
I'd recommend experimenting. Once you have your board setup, feed the INA with a stable dc voltage reference and then test a few different moving-average implementations. Log and graph the resulting values, then choose the one that gives you the best combination of accuracy and repeatability. This has the advantage of taking into account the various errors that your components will introduce.

A while back I did a dc measurement project with the MCP3421 in (one-shot) 18-bit mode. For what it's worth, here's the implementation I settled on:

C:
static double get_sensor_voltage(void)
{
  int32_t sum = 0;
  int32_t result;
  uint16_t count;

  for ( count = 0; count < WINDOW_SIZE; ++count )
  {
    startConversion();
    sum += getData();
  }

#if WINDOW_SIZE > 1
  // round the averaged result
  result = (sum + (WINDOW_SIZE / 2)) / WINDOW_SIZE;
#else
  result = sum;
#endif

  return ((double)result * LSB);
}
Here's a graph comparing the raw signal (gray) to a moving average with WINDOW_SIZE of 16 (blue), and an attempt at coaxing 20-bit resolution out of the ADC by using 16x oversampling (orange):

 

Attachments

Thread Starter

Henry603

Joined Nov 19, 2018
69
I'd recommend experimenting. Once you have your board setup, feed the INA with a stable dc voltage reference and then test a few different moving-average implementations. Log and graph the resulting values, then choose the one that gives you the best combination of accuracy and repeatability. This has the advantage of taking into account the various errors that your components will introduce.

A while back I did a dc measurement project with the MCP3421 in (one-shot) 18-bit mode. For what it's worth, here's the implementation I settled on:

C:
static double get_sensor_voltage(void)
{
  int32_t sum = 0;
  int32_t result;
  uint16_t count;

  for ( count = 0; count < WINDOW_SIZE; ++count )
  {
    startConversion();
    sum += getData();
  }

#if WINDOW_SIZE > 1
  // round the averaged result
  result = (sum + (WINDOW_SIZE / 2)) / WINDOW_SIZE;
#else
  result = sum;
#endif

  return ((double)result * LSB);
}
Here's a graph comparing the raw signal (gray) to a moving average with WINDOW_SIZE of 16 (blue), and an attempt at coaxing 20-bit resolution out of the ADC by using 16x oversampling (orange):

Very interesting, thanks for sharing!
So what do you think about the MCP3421 after the testing?
Did you get your 20Bits?
How did you feed your instrumentation amp or ADC (what did you hook up and how exactly).
Did you pay special attention to decoupling and layout (you used this on a breakout-board or in a carefully done layout)?

Thank you very much.
 

bogosort

Joined Sep 24, 2011
678
Very interesting, thanks for sharing!
So what do you think about the MCP3421 after the testing?
Did you get your 20Bits?
How did you feed your instrumentation amp or ADC (what did you hook up and how exactly).
Did you pay special attention to decoupling and layout (you used this on a breakout-board or in a carefully done layout)?
The MCP3421 performed well in my application; I'd have no problem using it again for dc measurements. I didn't get a chance to test its ac performance.

As you can see in the graph above, 20-bit resolution is possible. Each vertical tick represents 10 μV. In 18-bit mode, the ADC's quantization step size is 15.625 μV; at 20-bits, it's just under 4 μV. But to get n extra bits of resolution, you have to oversample by a factor of 4^n. So, in 18-bit mode, that means 16x oversampling to get to 20 bits. Since the (already oversampled) ADC rate is essentially 4 samples per second, 16x oversampling reduces the effective rate to 0.25 samples per second. This was too slow for my application, and in any case I was more concerned with repeatability than sub-10 μV resolution, so I settled on a moving-average filter at 18-bit resolution with a window size of 4 samples. This hit the sweet spot between measurement repeatability and speed (1 sps) for the application.

The sensor output of my application needed to be scaled down to the ADC voltage range. I used precision resistors with tiny temperature coefficients as a voltage divider, buffered by a zero-drift op-amp to feed the ADC. Here's a simplified schematic of the analog frontend:

Since I needed the resistor in the signal path anyway, including the filtering capacitor was an easy choice (especially since the RC filter was isolated from the ADC). Note that I used a polyester film cap and not a cheap electrolytic.

As for layout, in any precision measurement application, decoupling, layout, and routing are critically important. Initially, I used a demo board to learn how to use the ADC and write a driver. But for the actual app, I designed a 4-layer PCB and took special care to keep the 96 MHz digital signals from influencing the analog measurement. The data in the graph above were taken from the PCB.
 

Attachments

Thread Starter

Henry603

Joined Nov 19, 2018
69
Great insight, thank you!

As this ADC gives 3.75 SPS (lets say 4SPS) in 18Bit mode configuration you chose the window size to be 4 samples.
That means the sample you actually get out of it is always averaged over the last 4 samples.
That will output effectively one sample per second (as the 4SPS the ADC is originally delivering are used to create one averaged sample with "reduced noise").
Did I get your approach right?

May I ask why you used a 8.2k Ohm resistor as I²C pullup resistor (I assume you have used one on the data line as well?).
Most of the time one uses 5k - 10k so I wonder If you had a special intention to go for that very specific value?

In my layout do I need special care regarding the I²C bus routing? Can I just route the SCL and Data line next to each other or should I keep some distance as the clock line might interfer with the data line?
I guess I should keep this noisy bus lines as far away from my power supplies and other analog signal traces as possible?

Thank you so much for all your input.
 

bogosort

Joined Sep 24, 2011
678
As this ADC gives 3.75 SPS (lets say 4SPS) in 18Bit mode configuration you chose the window size to be 4 samples.
That means the sample you actually get out of it is always averaged over the last 4 samples.
That will output effectively one sample per second (as the 4SPS the ADC is originally delivering are used to create one averaged sample with "reduced noise").
Did I get your approach right?
Exactly. With a window of 4, we're using 4 samples to make 1 sample, so the effective sampling rate is 1 sps.

May I ask why you used a 8.2k Ohm resistor as I²C pullup resistor (I assume you have used one on the data line as well?).
Most of the time one uses 5k - 10k so I wonder If you had a special intention to go for that very specific value?
I don't remember the exact reason, but I do remember trying a couple of different values and settling on 8.2k for both SCLK and SDATA.

In my layout do I need special care regarding the I²C bus routing? Can I just route the SCL and Data line next to each other or should I keep some distance as the clock line might interfer with the data line?
I guess I should keep this noisy bus lines as far away from my power supplies and other analog signal traces as possible?
They're both digital lines, so you don't need to worry about them interfering with each other -- but do keep them (and all digital switching) as far away as possible from the analog side.
 

MrChips

Joined Oct 2, 2009
24,621
As @bogosort says, you do not have to be concerned with digital SCLK and SDATA lines, power and ground.
Follow basic PCB guidelines, keep all your traces as short as possible.

On a mixed signal PCB, i.e. analog and digital on the same board, there are some basic rules:

1) Keep digital signals away from analog.
2) Put power supply decoupling capacitors on all ICs.
3) Keep traces short, especially on decoupling capacitors.
4) Provide separate ground planes, one for analog and one for digital.
5) Watch for ground loops.
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
@MrChips : Any sites you would recommend especially regarding ground loops (in PCB design)? I find it hard to find good tutorials/explanations on this topic.
Most of what I have found is dealing with ground loops in sound systems or general customer electronics (and not really with PCB layout and circuit design).
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
@bogosort :
Regarding your mentioned project with the MCP3421, another 2 questions came to my mind:

1.
I also want to power the ADC with 3.3V but now I have the problem that I want to interface with it over I2C using my Arduino Mega (5V).
I now I plan to use the I2C Bus Voltage-Level Translator like e.g. the PCA9306: Datasheet.
Maybe you can tell me some advise if you think this is a good idea or how you did interface with the ADC back in time.
Also regarding the pullups, is the value really that critical or can one just estimate it, maybe you could give me a hint on that as well as im a bit confused regarding the calculations of the pullups (im not concerned about speed, but the less current it draws the better, so I chose 5.1 kOhm for both sides now)...

2.
I plan to use the 4-channel version of the MCP3421. It has a multiplexer and the saving the values of the channels into seperate registers.
Do you know if the 3.75SPS are per channel or can I expect 3.75 samples per second from every of the four channels?

Thank you very much in advance! :)
 

bogosort

Joined Sep 24, 2011
678
I also want to power the ADC with 3.3V but now I have the problem that I want to interface with it over I2C using my Arduino Mega (5V).
I now I plan to use the I2C Bus Voltage-Level Translator like e.g. the PCA9306: Datasheet.
Maybe you can tell me some advise if you think this is a good idea or how you did interface with the ADC back in time.
Also regarding the pullups, is the value really that critical or can one just estimate it, maybe you could give me a hint on that as well as im a bit confused regarding the calculations of the pullups (im not concerned about speed, but the less current it draws the better, so I chose 5.1 kOhm for both sides now)...
For that project I used a 3.3V μC (Teensy 3.2), so I2C voltage level wasn't an issue. It's possible that the Mega will be fine with the I2C bus pulled to 3.3V; you can test this before ordering the level shifter. But now that you mention it, I'm curious why you're using an 8-bit μC to handle 18-bit data.

As for the pull-up resistors, 5k should be fine. My priority was minimizing power usage (battery application), so I chose the largest value the bus could reliably handle.

I plan to use the 4-channel version of the MCP3421. It has a multiplexer and the saving the values of the channels into seperate registers.
Do you know if the 3.75SPS are per channel or can I expect 3.75 samples per second from every of the four channels?
The datasheet should have the definitive answer, but given that the ADC is multiplexed, it only makes sense that the channels share the total sampling rate. In other words, there's only one ADC (running at 3.75 sps) and the multiplexer determines which sensor output gets connected to the single ADC input. So, to get 4 samples from each of the sensors will require roughly 4 seconds.
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
For that project I used a 3.3V μC (Teensy 3.2), so I2C voltage level wasn't an issue. It's possible that the Mega will be fine with the I2C bus pulled to 3.3V; you can test this before ordering the level shifter. But now that you mention it, I'm curious why you're using an 8-bit μC to handle 18-bit data.

As for the pull-up resistors, 5k should be fine. My priority was minimizing power usage (battery application), so I chose the largest value the bus could reliably handle.


The datasheet should have the definitive answer, but given that the ADC is multiplexed, it only makes sense that the channels share the total sampling rate. In other words, there's only one ADC (running at 3.75 sps) and the multiplexer determines which sensor output gets connected to the single ADC input. So, to get 4 samples from each of the sensors will require roughly 4 seconds.
The Arduino Mega is a data acquisition unit in my system that is collecting data of multiple sensors and forwarding it to a data processing unit.
(so its only purpose is not to interface only with this module and as this µC has only a 10 bit ADC and I need to be way more accurate than that I use the external ADC)

Ok, great to hear. I was a bit confused as in the datasheet of the I2C Bus Voltage-Level Translator (PCA9306: Datasheet), to choose the right pullup sounds quite complicated. But as far as I have read ~5k should be a good trade-off between current draw and speed.
Speed is not a concern for me, also the module is the only slave on the bus and the bus lines are not going to be too long either.
So hopefully I will not run into problems here.

Also I think I will use a level-translator here to be on the safe side (as I read many issues regarding sporadic misbehaviour connecting 3.3 to 5V i²C without a level-translator under varying environmental conditions etc.). The use seems pretty straightforward so I hopefully will not run into any issues here.

Thank you very much for your input! :)
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
@bogosort : One more question regarding the MCP342x ADC:

What library did you use in order to communicate with the ADC over I2C?
I saw there are multiple libraries available, can you recommend one or did you write code yourself?

Thank you.
 

bogosort

Joined Sep 24, 2011
678
@bogosort : One more question regarding the MCP342x ADC:

What library did you use in order to communicate with the ADC over I2C?
I saw there are multiple libraries available, can you recommend one or did you write code yourself?
I didn't find anything I liked, so I wrote my own driver. I remember it being easy to do; the datasheet has everything you need to know.

The only gotcha I recall was that 18-bit mode requires special parsing compared to the other modes. I wanted the driver to work in any resolution, so I wrapped the data parsing into its own routine that returned the ADC data in a signed 32-bit integer container, regardless of resolution.

This snippet shows the basic idea:

C:
#define RESOLUTION 18
static uint8_t buff[4];  // buffer to hold bytes returned from the ADC

/* ... */

// getData() blocks while waiting for a conversion to finish, parses
// the data bytes (see datasheet for the gory details), and returns the
// resulting code
static int32_t getData(void)
{
  uint8_t  sign1; // holds 8 sign-extended bits
  uint16_t sign2; // holds 16 sign-extended bits
  int32_t  data;  // stores the conversion code

  // wait for conversion to be finished
  while ( conversionBusy() == true );

  switch (RESOLUTION)
  {
    case 18:   // 18-bit data needs special handling
      sign1 = buff[0] & 0x80 ? 0xFF : 0;
      data = (sign1 << 24) + (buff[0] << 16) + (buff[1] << 8) + buff[2];
      break;
    default:
      sign2 = buff[0] & 0x80 ? 0xFFFF : 0;
      data = (sign2 << 16) + (buff[0] << 8) + buff[1];
  }

  return data;
}
The buff array gets written to in conversionBusy(), which is just a blocking i2c read from the ADC.
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
@bogosort :

Thank you very much for the insight!
In case this ADC gets a supply voltage of VDD=3.3V and GND=0V (and it has an internal reference voltage of 2.048V) and I'm doing a single ended measurement (the CH+ input pin will get the signal applied and the CH- pin will get tied to GND),
I guess that in case the ADC will get a negative voltage on the CH+ input pin, it will be converted to '0' (lowest digital value the ADC can provide)?

Did I get it right? (of course I will keep the minimum voltage specification of V_ss-0.3V in mind that the analog input pins can handle).

Thank you.
 

bogosort

Joined Sep 24, 2011
678
In case this ADC gets a supply voltage of VDD=3.3V and GND=0V (and it has an internal reference voltage of 2.048V) and I'm doing a single ended measurement (the CH+ input pin will get the signal applied and the CH- pin will get tied to GND),
I guess that in case the ADC will get a negative voltage on the CH+ input pin, it will be converted to '0' (lowest digital value the ADC can provide)?

Did I get it right? (of course I will keep the minimum voltage specification of V_ss-0.3V in mind that the analog input pins can handle).
With Vss and CH- tied to ground, the ADC input cannot go negative with respect to ground (you'll break it if you try!). If your sensor/INA output can swing positive and negative with respect to the ADC's Vss, you'll need to add a dc offset and scaling to keep the signal between 0 and 2.048 V. The ADC will only output positive codes.

Alternatively, if your application requires negative codes for some reason, you can use "pseudo-differential" mode and tie 1.024 Vdc to CH-, letting CH+ range between 0 and 2.048 V. Since the ADC takes the input voltage as the difference between CH+ and CH-, it will produce positive codes for CH+ in the range (1.024, 2.048] V, and negative codes for the range [0, 1.024) V. If you go this route, ensure the 1.024 Vdc reference is ultra stable.

Note that, in either case, you automatically lose one bit (6 dB) of dynamic range / SNR. Since the MSB is always a sign bit, half of the available codes (all those that start with 1) can never be reached in single-ended mode. Likewise, in pseudo-differential mode you lose half of the codes (because you're using half the voltage range). The good news is that the resolution doesn't change, as the LSB still represents 15.625 μV in 18-bit mode, regardless of input topology. But if SNR is important to your application, make sure you've scaled the sensor output (or used the ADC's internal PGA) to fill as much of the 0 to 2.048V range as possible.

If you have the time and budget, you should purchase the MCP3421 evaluation board and perform some experiments. It's incredibly helpful to be able to quickly test various approaches, and a lot of fun, too. :)
 

Thread Starter

Henry603

Joined Nov 19, 2018
69
With Vss and CH- tied to ground, the ADC input cannot go negative with respect to ground (you'll break it if you try!). If your sensor/INA output can swing positive and negative with respect to the ADC's Vss, you'll need to add a dc offset and scaling to keep the signal between 0 and 2.048 V. The ADC will only output positive codes.

Alternatively, if your application requires negative codes for some reason, you can use "pseudo-differential" mode and tie 1.024 Vdc to CH-, letting CH+ range between 0 and 2.048 V. Since the ADC takes the input voltage as the difference between CH+ and CH-, it will produce positive codes for CH+ in the range (1.024, 2.048] V, and negative codes for the range [0, 1.024) V. If you go this route, ensure the 1.024 Vdc reference is ultra stable.

Note that, in either case, you automatically lose one bit (6 dB) of dynamic range / SNR. Since the MSB is always a sign bit, half of the available codes (all those that start with 1) can never be reached in single-ended mode. Likewise, in pseudo-differential mode you lose half of the codes (because you're using half the voltage range). The good news is that the resolution doesn't change, as the LSB still represents 15.625 μV in 18-bit mode, regardless of input topology. But if SNR is important to your application, make sure you've scaled the sensor output (or used the ADC's internal PGA) to fill as much of the 0 to 2.048V range as possible.

If you have the time and budget, you should purchase the MCP3421 evaluation board and perform some experiments. It's incredibly helpful to be able to quickly test various approaches, and a lot of fun, too. :)
 
Top