I see that there are multiple techniques to calculcate a moving average for an ADC.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'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.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?
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);
}
Very interesting, thanks for sharing!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:
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):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); }
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.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)?
Exactly. With a window of 4, we're using 4 samples to make 1 sample, so the effective sampling rate is 1 sps.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?
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.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?
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.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?
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.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)...
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.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 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.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.
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.@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?
#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;
}
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.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.
by Jake Hertz
by Robert Keim
by Aaron Carman