Converting I2C binary read to number

I have a LTC2487 ADC wired up using I2C.
Extract from the data sheet below
Have successfully used the wire library to convert a 24bit reading to the voltage.
Code is below
I feel like there should have been an easier way to convert the output without if statements etc

The chip programmers thought a 24 bit message with one status bit, 17 bits two's complement binary number and then 6 zeroes would be an easy convert?

I think the binary would convert to integer fine if it was a 16 bit two's complement number.

Just occurred to me - maybe I should just lose the least significant bit.

Any thoughts - please school me! I have long but very sporadic experience in various programming languages.

Thanks in advance!

// Wire Master Reader
// by devyte
// based on the example of the same name by Nicholas Zambetti <http://www.zambetti.com>


// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this


// This example code is in the public domain.




#include <Wire.h>
#include <PolledTimeout.h>


#define SDA_PIN 4
#define SCL_PIN 5
const int16_t I2C_MASTER = 0x42;
const int16_t I2C_SLAVE = 0x24; //ltc2487


void setup() {
  Serial.begin(9600);  // start serial for output
  Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER);        // join i2c bus (address optional for master)
}


void loop() {
  using periodic = esp8266::polledTimeout::periodicMs;
  static periodic nextPing(1000);


  if (nextPing) {
    Wire.requestFrom(I2C_SLAVE, 3);    // request 6 bytes from slave device #8
    int result = 0;
    while (Wire.available()) { // slave may send less than requested
      byte  c = Wire.read(); // receive a byte
      result = (result << 8) + c;
      //Serial.print (c);
      prntBits(c, 8);        // print byte
      Serial.print(" ");
    }
    Serial.println();
    prntBits(result, 24);
    Serial.print("  ");




    bool neg =      (result & 0b010000000000000000000000) >> 22 ; // bit 22
    int binvalue =  (result & 0b001111111111111111000000) >> 6; // bits 21-6


    long attempt =  (result & 0b011111111111111111000000) >> 6; // bits 22-6 - try to get signed int from 17 bit twos complement


    Serial.print (attempt); //doesn't work as presumes it's 32 bit binary
    Serial.print ("  ");


    float value;


    Serial.print (neg);
    Serial.print("  ");
    prntBits(binvalue, 16);
    Serial.print("  ");
    Serial.print(binvalue);
    Serial.print("  ");


    if (neg == 1) {
      binvalue = binvalue ^ 0xffff; //invert bits
      value = binvalue / (float)0xffff  * -1.0; // number between 0 and -1
    }
    else {
      value = (binvalue / (float)0xffff); //number between 0 and 1
    }


    float voltage = (3.26 / 2) * value ; // ref = 3.26v






    Serial.print(value, 5);
    Serial.print("  ");
    Serial.print(voltage, 5);


    Serial.println();


    Serial.println();
  }
}


void prntBits(long b, byte length)
{
  for (int i = length; i >= 1; i--)
    Serial.print(bitRead(b, i - 1));
}

TYPICAL OUTPUT -

01010101 10100001 00000000 
010101011010000100000000  87684  1  0101011010000100  22148  -0.66204  -1.07913


10101010 01011110 00000000 
101010100101111000000000  43384  0  1010100101111000  43384  0.66200  1.07906

ltc2487 p16 (2).pdf (55.7 KB)

Have you tried this Arduino library for that device family?

https://github.com/kasskas/libraries/blob/master/LTC24XX_general

not sure your results are correct

assuming your interested in figuring out how to do this yourself,

the code below generates following from values in pdf and your test values

FS 0x020000   131072

  0x00c00000  0xfffe0000  overflow overflow
  0x00bfffe0  0x0001ffff    1.0000 FS   -1
  0x00a00000  0x00010000    0.5000 FS/2
  0x009fffe0  0x0000ffff    0.5000 FS/2 -1
  0x00800000  0x00000000    0.0000 zero
  0x007fffe0  0xffffffff   -0.0000 -1
  0x00600000  0xffff0000   -0.5000 -FS/2
  0x00400000  0xfffe0000   -1.0000 -FS
  0x003fffe0  0x0001ffff underflow underflow
  0x0055a100  0xfffead08   -0.6620 test val 1
  0x00a55e00  0x00012af0    0.5839 test val 2

code tested on laptop using gcc

// test LTC2487 conversion

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <limits.h>

char        *progname;
unsigned int debug = 0;
unsigned int flag  = 0;

// --------------------------------------------------------------------
// test values specified for LTC2487
struct Val_s {
    uint8_t     b [3];      // 3 bytes of data
    const char *desc;       // description of value
};

Val_s vals [] = {
    { 0xc0, 0x00, 0x00, "overflow" },
    { 0xbf, 0xff, 0xe0, "FS   -1" },
    { 0xa0, 0x00, 0x00, "FS/2" },
    { 0x9f, 0xff, 0xe0, "FS/2 -1" },

    { 0x80, 0x00, 0x00, "zero" },
    { 0x7f, 0xff, 0xe0, "-1" },
    { 0x60, 0x00, 0x00, "-FS/2" },
    { 0x40, 0x00, 0x00, "-FS" },
    { 0x3f, 0xff, 0xe0, "underflow" },

    { 0x55, 0xa1, 0x00, "test val 1" },
    { 0xa5, 0x5e, 0x00, "test val 2" },
};

#define N_VALS (sizeof(vals)/sizeof(Val_s))

// --------------------------------------------------------------------
// convert 3 bytes of LTC2487 values into flow
// return int indicating overflow (1), underflow (-1) or ok (0)
#define NBITS       24
#define SIG         (1<<(NBITS-1))
#define MSB         (1<<(NBITS-2))
#define FS          (1<<(NBITS-7))

int
convert (
    uint8_t   *bytes,
    float     *f )
{
    int32_t   val = 0;

    for (int i = 0; i < 3;  i++)    // pack bytes in to 32 bits
        val = val << 8 | bytes [i];

    printf ("  0x%08x",  val);

    bool sig = val & SIG ? 1 : 0;   // capture sig and msb bits
    bool msb = val & MSB ? 1 : 0;

    val  =  (val << 9) >> 14;       // sign-extend and shift out 5 bits
    *f   =  1.0 * val / FS;         // scale to FS
             
    printf ("  0x%08x",  val);

    return sig ^ msb ? 0 : (sig ? 1 : -1);
}

// ------------------------------------------------
void
application (char *filename)
{
    printf (" FS 0x%06x %8d\n", FS, FS);
    printf ("\n");

    // convert each value in table
    for (int i = 0; i < N_VALS; i++)  {
        float f;
        int   result = convert (& vals [i].b[0], &f);

        if (result)
            printf (" %9s", result > 0 ? "overflow" : "underflow");
        else
            printf (" %9.4f", f);

        printf (" %s\n", vals [i].desc);
    }
}

// --------------------------------------------------------------------
void help (void)  {
    printf (" Usage: %s \n", progname);
}

// --------------------------------------------------------------------
int main (int argc, char **argv)  {
    int   c;

    progname = *argv;

    while ((c = getopt(argc, argv, "D:o")) != -1)  {
        switch (c)  {
        case 'D':
            debug = atoi (optarg);
            break;

        case 'o':
            break;

        default:
            printf ("Error: unknown option '%c'\n", optopt);
                        help();
            break;
        };

    }

    application (NULL);

    return 0;
}

jremington:
Have you tried this Arduino library for that device family?

https://github.com/kasskas/libraries/blob/master/LTC24XX_general

Yes I have it but it doesn;t mention my device specifically. Also the other devices seem to be 24 bit whereas mine is 16 bit, not sure of other differences. I started using the wire library and it went fairly well as far as I've got anyway.

gcjr:
not sure your results are correct

assuming your interested in figuring out how to do this yourself,

the code below generates following from values in pdf and your test values

FS 0x020000   131072

0x00c00000  0xfffe0000  overflow overflow
 0x00bfffe0  0x0001ffff    1.0000 FS   -1
 0x00a00000  0x00010000    0.5000 FS/2
 0x009fffe0  0x0000ffff    0.5000 FS/2 -1
 0x00800000  0x00000000    0.0000 zero
 0x007fffe0  0xffffffff   -0.0000 -1
 0x00600000  0xffff0000   -0.5000 -FS/2
 0x00400000  0xfffe0000   -1.0000 -FS
 0x003fffe0  0x0001ffff underflow underflow
 0x0055a100  0xfffead08   -0.6620 test val 1
 0x00a55e00  0x00012af0    0.5839 test val 2




code tested on laptop using gcc


// test LTC2487 conversion

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <limits.h>

char        *progname;
unsigned int debug = 0;
unsigned int flag  = 0;

// --------------------------------------------------------------------
// test values specified for LTC2487
struct Val_s {
   uint8_t     b [3];      // 3 bytes of data
   const char *desc;       // description of value
};

Val_s vals [] = {
   { 0xc0, 0x00, 0x00, "overflow" },
   { 0xbf, 0xff, 0xe0, "FS   -1" },
   { 0xa0, 0x00, 0x00, "FS/2" },
   { 0x9f, 0xff, 0xe0, "FS/2 -1" },

{ 0x80, 0x00, 0x00, "zero" },
   { 0x7f, 0xff, 0xe0, "-1" },
   { 0x60, 0x00, 0x00, "-FS/2" },
   { 0x40, 0x00, 0x00, "-FS" },
   { 0x3f, 0xff, 0xe0, "underflow" },

{ 0x55, 0xa1, 0x00, "test val 1" },
   { 0xa5, 0x5e, 0x00, "test val 2" },
};

#define N_VALS (sizeof(vals)/sizeof(Val_s))

// --------------------------------------------------------------------
// convert 3 bytes of LTC2487 values into flow
// return int indicating overflow (1), underflow (-1) or ok (0)
#define NBITS       24
#define SIG         (1<<(NBITS-1))
#define MSB         (1<<(NBITS-2))
#define FS          (1<<(NBITS-7))

int
convert (
   uint8_t   *bytes,
   float     *f )
{
   int32_t   val = 0;

for (int i = 0; i < 3;  i++)    // pack bytes in to 32 bits
       val = val << 8 | bytes [i];

printf ("  0x%08x",  val);

bool sig = val & SIG ? 1 : 0;   // capture sig and msb bits
   bool msb = val & MSB ? 1 : 0;

val  =  (val << 9) >> 14;       // sign-extend and shift out 5 bits
   *f   =  1.0 * val / FS;         // scale to FS
           
   printf ("  0x%08x",  val);

return sig ^ msb ? 0 : (sig ? 1 : -1);
}

// ------------------------------------------------
void
application (char *filename)
{
   printf (" FS 0x%06x %8d\n", FS, FS);
   printf ("\n");

// convert each value in table
   for (int i = 0; i < N_VALS; i++)  {
       float f;
       int   result = convert (& vals [i].b[0], &f);

if (result)
           printf (" %9s", result > 0 ? "overflow" : "underflow");
       else
           printf (" %9.4f", f);

printf (" %s\n", vals [i].desc);
   }
}

// --------------------------------------------------------------------
void help (void)  {
   printf (" Usage: %s \n", progname);
}

// --------------------------------------------------------------------
int main (int argc, char **argv)  {
   int   c;

progname = *argv;

while ((c = getopt(argc, argv, "D:o")) != -1)  {
       switch (c)  {
       case 'D':
           debug = atoi (optarg);
           break;

case 'o':
           break;

default:
           printf ("Error: unknown option '%c'\n", optopt);
                       help();
           break;
       };

}

application (NULL);

return 0;
}

I seemed to be getting the right voltages, ref voltage is 3.3v so FS is 1.65V. I haven't programmed the "out of range" values yet. I'll study your code and see if I can learn something

minor difference: 0.66200 vs 0.5839

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.