MCP 3424 - maybe bug in lib ?

Hi All

I have connected an MCP 3424 I2C module to an UNO. I can read all 4 channels well enough, so the i2c and power connections are good.

The issue I am facing is that when I reverse the polarity of my test-source, I get really strange results. I am using the device in Differential mode, and my test source for measurements is an AA battery, nominally 1.5V

The output, shown below, is of channel 2 at different resolutions. The first block is with the AA battery connected +-to-+ and neg-to-neg.

The second block shows the result with the battery connected +-to-neg and neg-to-+ .... I expected the output to show the correct voltage, but with sign negative.

As can be seen, the numbers are meaningless (to me, at least) ... another oddity is the result of the conversion at 14 bits resolution is 'out-of-whack" with the others.

I would greatly appreciate any and all assistance

#include <MCP3424.h>
#include <Wire.h>

MCP3424 MCP(0x6e); // MCP3424 Adress

long startmillis, endmillis = 0;
long Voltage[4]; // Array used to store results
int NumBits = 12;

void setup() {
  Serial.begin(115200);
  MCP.begin(0);
  Serial.println("");
}

void loop() {
  Serial.print(NumBits);
  Serial.print(" bits ");
  for (int i = 2; i <= 2; i++) { // looks odd, but just to test 1 channel
    startmillis = millis();
    MCP.configuration(i, NumBits, 1, 1);
    //channel i with Numbits resolution, continous mode and gain 1
    Voltage[i - 1] = MCP.measure();
    endmillis = millis();
    Serial.print("Channel ");
    Serial.print(i);
    Serial.print(" : ");
    float result = Voltage[i - 1];
    result /= 1000.0;
    result /= 1000.0;
    Serial.print(result, 6);        // comment out this or the next
    //Serial.print(Voltage[i - 1]); //  line for read-ability
    Serial.print(" V");
    Serial.print(", Time= ");
    Serial.println(endmillis - startmillis);
  }
  NumBits += 2;
  if (NumBits >= 19) {
    while (1);  // just loop once for now
    NumBits = 12;
  }
}
12 bits Channel 2 : 1.342000 V, Time= 5
14 bits Channel 2 : 1.341750 V, Time= 16
16 bits Channel 2 : 1.341750 V, Time= 64
18 bits Channel 2 : 1.341750 V, Time= 253

12 bits Channel 2 : -2147.483642 V, Time= 5
14 bits Channel 2 : 10.946250 V, Time= 16
16 bits Channel 2 : -2147.483642 V, Time= 65
18 bits Channel 2 : -2147.483642 V, Time= 253

Options:
a) lib. doesn't support dif. mode
b) lib. treated negative number not as compliment 2

Post a link to library.

Not sure how to post a link, but here is the .h file

/* MCP3424 library version 1.3

Writed by B@tto 
Contact : batto@hotmail.fr


  MCP3424.h - ADC 18 bits i2c library for Wiring & Arduino
  Copyright (c) 2012 Yann LEFEBVRE.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifndef MCP3424_H
#define MCP3424_H

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include <Wire.h>
#include <math.h>

class MCP3424 {

public:

MCP3424(uint8_t adresse);
~MCP3424();
void begin(byte setMod = 1);
void configuration(uint8_t channel,uint8_t resolution,bool mode,uint8_t PGA);
void newConversion();
bool isConversionFinished();
long measure();

private:

uint8_t _adresse;
uint8_t _resolution;
bool _mode;
uint8_t _cfgbyte;
uint8_t _PGA;
uint8_t _buffer[4];

};

#endif

and this be the .cpp

/* MCP3424 library version 1.3

Writed by B@tto
Contact : batto@hotmail.fr


  MCP3424.cpp - ADC 18 bits i2c library for Wiring & Arduino
  Copyright (c) 2012 Yann LEFEBVRE.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "MCP3424.h"

MCP3424::MCP3424(uint8_t adresse){

_adresse=0b1101<<3;
_adresse|=adresse;



}

MCP3424::~MCP3424(){

}

void MCP3424::begin(byte setMod){

#ifdef ENERGIA
Wire.setModule(setMod);
#endif

Wire.begin();

}

void MCP3424::configuration(uint8_t channel,uint8_t resolution,bool mode,uint8_t PGA){

if(resolution!=12 && resolution!=14 && resolution!=16 && resolution!=18) _resolution=12;
 else _resolution=resolution;

_PGA=PGA;
_mode=mode;

_cfgbyte=0;
_cfgbyte |= ((channel-1) & 0x3) << 5;
_cfgbyte |= (mode & 0x1) << 4;
_cfgbyte |= (int((_resolution-12)/2) & 0x3) << 2;
_cfgbyte |= (int(logf(PGA)/logf(2))) & 0x3;

Wire.beginTransmission(_adresse);
Wire.write(_cfgbyte);
Wire.endTransmission();

}

void MCP3424::newConversion(){

Wire.beginTransmission(_adresse);
Wire.write((_cfgbyte|=128));
Wire.endTransmission();

}

bool MCP3424::isConversionFinished(){

uint8_t _requestedByte;

if(_resolution!=18){
_requestedByte = 3;
} else _requestedByte = 4;

Wire.requestFrom(_adresse, _requestedByte);

uint8_t _i=0;

while(Wire.available()) _buffer[_i++]=Wire.read();

return (_buffer[_requestedByte-1] & 0b10000000);

}


long MCP3424::measure(){

long _resultat=0;

while(isConversionFinished()==1);

switch (_resolution){

case 12:

 _resultat = (((long)_buffer[0] & 0x0F) << 8) | ((long)_buffer[1] & 0xFF); 
 
 _resultat |= long(_buffer[0] & 0x80) << 24;

 _resultat = _resultat*1000.0/_PGA;

 break;

 case 14:
 
_resultat = (((long)_buffer[0] & 0xBF) << 8) | ((long)_buffer[1] & 0xFF); 

_resultat |= long(_buffer[0] & 0x80) << 24;

_resultat = _resultat*250/_PGA;

 break;

case 16:

_resultat = (((long)_buffer[0] & 0x7F) << 8) | ((long)_buffer[1] & 0xFF); 

 _resultat |= long(_buffer[0] & 0x80) << 24;

_resultat = _resultat*62.5/_PGA;

 break;

case 18:

_resultat = (((long)_buffer[0] & 0x01) << 16) | (((long)_buffer[1] & 0xFF) <<8) | ((long)_buffer[2] & 0xFF); 

_resultat |=((long)_buffer[0] & 0x80) << 24;

_resultat = _resultat*15.625/_PGA;

  break;
}

return _resultat;

}

The following looks like utter nonsense to me. The sign bit is not treated even remotely correctly.

case 16:

_resultat = (((long)_buffer[0] & 0x7F) << 8) | ((long)_buffer[1] & 0xFF);

 _resultat |= long(_buffer[0] & 0x80) << 24;

_resultat = _resultat*62.5/_PGA;

case 18:

_resultat = (((long)_buffer[0] & 0x01) << 16) | (((long)_buffer[1] & 0xFF) <<8) | ((long)_buffer[2] & 0xFF);


_resultat |=((long)_buffer[0] & 0x80) << 24;

_resultat = _resultat*15.625/_PGA;

Try this library instead. It appears to handle three byte reads correctly: GitHub - uChip/MCP342X: Arduino Library for MCP3421, MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427, MCP3428 ADCs. But again, a three byte read of a negative 18 or 24 bit quantity will not handle the sign of a 32 bit result correctly. The proper approach for variable bit lengths like this is to test the sign bit and if set, take the twos complement to form a positive number and then negate the final result.

Thanks for that. It took a while to figure out how to pass the device adress in the ReadLoop example, but I now get beleiveable results - for 12-bit resolution only, both +ve and -ve.

The 14, 16 and 18 - bit results are not what one would expect, though.

14 -> 5369
16 -> 21477
18 -> 85908

all with the same source connected, and their negatives have the same magnitudes.

Looks like an error of *4, but I haven't yet found where...

Cheers, Charles

Assuming you are using the library linked in reply #3, note that it does not scale the (integer) result. You have to do that, after converting to float.

There is a table of scale factors that is currently commented out:

/* static float stepSizeTbl[] = {
 0.001, // 12-bit, 1X Gain
 0.0005, // 12-bit, 2X Gain
 0.00025, // 12-bit, 4X Gain
 0.000125, // 12-bit, 8X Gain
 0.00025, // 14-bit, 1X Gain
 0.000125, // 14-bit, 2X Gain
 0.0000625, // 14-bit, 4X Gain
 0.00003125, // 14-bit, 8X Gain
 0.0000625, // 16-bit, 1X Gain
 0.00003125, // 16-bit, 2X Gain
 0.000015625, // 16-bit, 4X Gain
 0.0000078125, // 16-bit, 8X Gain
 0.000015625, // 18-bit, 1X Gain
 0.0000078125, // 18-bit, 2X Gain
 0.00000390625, // 18-bit, 4X Gain
 0.000001953125 // 18-bit, 8X Gain
 };
*/

I'm not convinced that the negative sign is handled correctly, but don't have a chip to test. You can do that after reading it out of the chip, by testing the sign bit, if set take the twos complement, then scaling and negating the result.

Look around on the web. Other people have struggled with this. This thread has some useful information: MCP3424 18-bit ADC - #21 by joearkay - Libraries - Particle

Yup, I am using that lib you refrenced. The commented-out part does explain the scaling i am seeing. I am not sure how to correct that in the lib, so will implement what I need in my main code ... I am planning to only use 14- or 12- bit resolution with the PGA at 1.

It seems there are several Libs out there with the same name !

CharlesDavis:
I am using the device in Differential mode, and my test source for measurements is an AA battery, nominally 1.5V

I doubt that you can connect a floating voltage to balanced inputs without additional circuitry to keep both inputs inside the common mode range of the chip.
Leo..