MCP3424 18-bit ADC

Has anyone had experience with this chip? It’s an ADC. You can buy an interface board for it from ereshop.com, which allows 0-10V and 4-20mA input, which is pretty handy for a Particle Electron, as it would let you interface with ‘industry standard’ control voltages.

I’ve tried a few libraries but had no luck:

Both will compile, with the usual modifications from Arduino code to Particle, but produce no output. The first library gives me “Read error” and “Read failed”…

I’m not asking for answers, because that’s lazy :wink: But if anyone has any experience with this device I’d be really interested to know what you did.

Thanks

1 Like

@joearkay, gotta ask… did you add pull-up resistors to both I2C lines? :wink:

Standard question I’m sure :wink: The board has built-in 10k’s for both SDA and SCLK, and this is the only I2C device connected. I’ve literally tried all the addresses also, because there is some discrepancy between the manufacturers information and the datasheet of the module on the ereshop.com website.

Triple checked all connections with my multimeter, everything is connected and no shorts etc. It’s very odd behaviour.

I know that both libraries do conversions and give read outs in mV, despite the ereshop board being 0-10v/4-20mA etc but this just a scaling issue. Something I can solve once I know I’m getting data!

@joearkay, how are you powering the board. I assume you have both pull-up jumpers on the board. Have you run an I2C bus scanner to see if anything comes up?

// --------------------------------------
// i2c_scanner
//
// http://playground.arduino.cc/Main/I2cScanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// 
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <application.h>

//#include <Wire.h>


void setup()
{
  Wire.begin();

  Serial.begin(9600);
  delay(10000);
  Serial.println("\nI2C Scanner");
}


void loop()
{
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknow error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

  delay(5000);           // wait 5 seconds for next scan
}

@peekay123 Oh brilliant, I had no idea you could scan the bus like this! Fantastic, it shows a device at 0x68. Both pull-ups are enabled and it’s powered with the battery and the USB port.

1 Like

Are you using a power shield?

Nope. Just the Particle Electron’s built in 2-pin LiPo connector in conjunction with my USB power. I was originally concerned that the I2C device was drawing more current that can be provided, but the 3.3V pin should be able to deliver ~800mA. Anyway, it shows up on the I2C scanner so must be working properly.

I’m using this library, and it still gives me an error. Like i said before, I’ve made a few modifications to make sure it works for Particle, mainly including ‘application.h’, removing the ‘wire.h’ reference and a few tweaks here and there.

@joearkay, I wouldn’t count on 800ma from the 3V3 pin but it will certainly power your board. What errors are you getting?

Sorry I wasn’t being clear. There are no ‘compilation’ errors, but when I run the sketch, I get “Read Error” and “Read Failed”, which is being called from readData()

Yes, I did think 800mA is a high value, considering a standard USB port pumps out 500mA max, but this is what’s in the documentation!

For reference:

  long MCP3424::readData()
{
  long data;
  // pointer used to form int32 data
  byte *p = (byte *)&data;
  // timeout - not really needed?
  long start = millis();
  do {
    // assume 18-bit mode
    Wire.requestFrom(MCP3424_ADDRESS, 4);
    if (Wire.available() != 4) {
      Serial.println("read failed");
      return false;
    }
    for (int i = 2; i >= 0; i--) {
      p[i] = Wire.read();
    }
    // extend sign bits
    p[3] = p[2] & 0X80 ? 0XFF : 0;
    // read config/status byte
    byte s = Wire.read();
    if ((s & MCP342X_RES_FIELD) != MCP342X_18_BIT) {
      // not 18 bits - shift bytes for 12, 14, or 16 bits
      p[0] = p[1];
      p[1] = p[2];
      p[2] = p[3];
    }
    if ((s & MCP342X_BUSY) == 0) return data;
  }
  while (millis() - start < 500); //allows rollover of millis()
  Serial.println("read timeout"); 
  return false; 
}



double MCP3424::getChannelmV(byte chan)
{
  long data;
  setChannel(chan);
  int mvDivisor = getMvDivisor();
    if (readData()!=0){
    data = readData();
    }
    else Serial.println("Read error");
    
  double mv = (double)data/mvDivisor;
  return mv;
}

@joearkay, try adding a delay between the Wire.requestFrom() and the next line in readData. The device may need a bit of setup time and the STM32 is fast.

    Wire.requestFrom(MCP3424_ADDRESS, 4);
    delay(1);
    if (Wire.available() != 4) {

@peekay123 Good point, I never really considered the clock speed to be an issue. I guess you would probably be able to get away with more using an Arduino, compared to something snappy like a Particle Electron.

I tried 1ms, 10ms and 100ms delays with no luck. I’ve thrown half second delays to each call of getChannelmV() as well. Even tried a half second delay where you suggested. Still no luck :confused:

Ok I’ve tried the other library with the correct address, hey presto! It must just be a timing issue somewhere in the old library! The results I’m getting are sporadic, but that’s probably because the scaling is different on this interface board compared to what it is natively (without the external op-amp etc).

Still doesn’t explain the behaviour of the library that doesn’t work, but hey ho!

@joearkay, there seems to be a minor error in the library. The contructor takes an address argument and correctly sets it private variable _address. However, it then ignores that value and uses its own #define value in:

Wire.requestFrom(MCP3424_ADDRESS, 4);

That line should be:
Wire.requestFrom(_address, 4);

I assume you set the correct address in the .ino file’s #define :wink:

1 Like

Damn, you’re right! I hadn’t even looked at the .h file!

Still, changing to #define MCP3424_ADDRESS 0X68 still doesn’t fix the problem :smiley: Likewise with making the change you suggested.

I don’t think it’s a stable library, I’ll stick with the other one I think!

@joearkay, let me know how it goes :wink:

1 Like

@joearkay, did you get it to work?

@peekay123 Just trying to adjust the conversion now, so that it yields a correct reading. In 12-bit mode, in the response, byte 0 is (MSB)XXXX SDDD(LSB) and byte 1 is DDDD DDDD, where ‘X’ bits are ignored, ‘S’ is the sign bit and ‘D’ bits are the data.

The interface board I’m using has an Op-Amp which means it can accept a larger voltage range. On the datasheet, they give the equation to convert the binary ‘code’ received into a voltage, as follows:

V = (code/2047)(2.048/PGA)(180/33).

I’ve modified the ‘switch’ case in the library to realise this equation, but it’s just returning zeros. I’d originally placed the values straight in-line, but decided to define them ‘long’ so that all arithmetic operations used the same type. I’ll comment each line so you can see my understanding:

switch (_resolution){
    

case 12:
 _resultat = _Buffer[0]; //originally assigning _resultat to the first byte
 _resultat&=0b00001111;   //ANDING this to 'mask' the unwanted bits
 _resultat = _resultat << 8;    //Shifting 8 bits in the MSB direction
_resultat |= _Buffer[1]; //OR to add byte 1 to the end of the first byte

 if(_resultat>2048-1) {
 _resultat=_resultat-4096-1;  //not sure what this bit is
 }

long maxCode = 2047;
long vRef = 2.048;
long invGain = (180/33); //all from datasheet


_resultat = (_resultat/maxCode)*(vRef/_PGA)*invGain; //equation from datasheet

If I just return ‘_resultat’ before doing this last arithmetic calculation, it yields the pure binary code. If I work out the equation on pen and paper, it works out! FYI _resultat is of ‘long’ type and _Buffer and _PGA are ‘uint_8’

Hi @joearkay

Type long is an integer type so 2.048 becomes 2 and 180/33 becomes 5. I think you want float or double types here.

Changing them to float seems to semi-fix it, but now lose the precision of the decimal points. All values seem to return as single integers