How do I convert a bipolar differential ADC value?

  adc, bit-shift, c++

I am trying to convert an ADC bipolar differential signal in C++. The device I am using is a 12 bit MAX11613 (datasheet) and the microcontroller is a Raspberry PI 3B+. So far I am able to capture values, however the results are not the differential result of AIN0 and AIN1 I would expect.

The device is a 3.3V supply and the input on AIN0 is 0-3.3V.
The input on AIN1 is the virtual ground of the incoming signal at 1.65V.

The bipolar transfer function of the device shown below may be my source of confusion. Why is it that a value just under the midrange of 1.65V would produce a value of 0b111111111111 and not 0b100000000001? And if that is actually correct, how does one adjust for that to reflect an actual negative voltage compared to the virtual ground input (AIN1)?

Bipolar Transfer Function

For what it’s worth, here’s my code:

max11613.h

#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
extern "C" {
   #include <linux/i2c.h>
   #include <linux/i2c-dev.h>
   #include <i2c/smbus.h>
}
#define MAX_ADDRESS     (0x34)
#define MAX_READ        (0x01)
#define MAX_WRITE       (0x00)

#define MAX_SETUP       (0x8E)
//REG SEL2 SEL1 SEL0 CLK BIP/UNI RST X
//1000 1100=0x8E SEL2=0, SEL1=0, SEL0=0, VDD REF, EXTERNAL CLOCK, BIPOLAR, NO CONFIG RESET
#define MAX_CONFIG      (0x00)
//REG SCAN1 SCAN0 CS3 CS2 CS1 CS0 SGL/DIFF
//0000 0000=0x00 NO SCAN, CHANNELS AIN0 AIN1, DIFFERENTIAL   <<THIS ONE WORKS
class MAX11613 {
    protected:
        uint8_t   m_i2cAddress;
        uint8_t   m_bitShift;
        uint32_t  m_conversionDelay;
    public:
        MAX11613(uint8_t i2cAddress = MAX_ADDRESS);
        int testMAXAvailable(void);
        void setup(void);
        int16_t readMAXADC_Differential_0_1(void);
    private:
};

max11613.cpp

#include "max11613.h"
#include <iostream>
#include <bitset>
int i2cMAXHandle;
static void beginMAXTransmission(uint8_t i2cAddress) {
  i2cMAXHandle = open("/dev/i2c-1", O_RDWR);
  if(i2cMAXHandle < 0)
  {
    fprintf(stderr, "Error while opening the i2c-2 device! Error: %sn", strerror(errno));
    exit(1);
  }
  // Set the slave address
  if(ioctl(i2cMAXHandle, I2C_SLAVE, i2cAddress) < 0)
  {
    fprintf(stderr, "Error while configuring the slave address %d. Error: %sn", i2cAddress, strerror(errno));
    exit(1);
  }
}
static void endMAXTransmission(void) {
    close(i2cMAXHandle);
}
static void writeMAXRegister(uint8_t i2cAddress, uint8_t reg, uint8_t value) {
    uint8_t payload = value;
    beginMAXTransmission(i2cAddress);
    i2c_smbus_write_word_data(i2cMAXHandle, reg, payload);
    endMAXTransmission();
}
static uint16_t readMAXRegister(uint8_t i2cAddress, uint8_t reg) {
    const uint8_t datalength = 2;
    unsigned char data[datalength];
    beginMAXTransmission(i2cAddress);
    i2c_smbus_read_i2c_block_data(i2cMAXHandle, reg, datalength, data););
    endMAXTransmission();
    std::cout << std::bitset<8>((uint8_t)data[0]) << std::bitset<8>((uint8_t)data[1]) << std::endl;
    uint16_t res =((data[0]&0xF)<<8)+data[1];//<---THIS ONE  READS 16 BITS AND REMOVES PRECEDING 4 1111 BITS OF DATA
    std::cout << std::bitset<16>(res) << std::endl;
    return res;
}
MAX11613::MAX11613(uint8_t i2cAddress) {
    m_i2cAddress = i2cAddress;
    m_bitShift = 4;
    m_conversionDelay = int(50);
}
void MAX11613::setup() {
    writeMAXRegister(m_i2cAddress, MAX_WRITE, MAX_SETUP);
}
int16_t MAX11613::readMAXADC_Differential_0_1() {
    writeMAXRegister(m_i2cAddress, MAX_WRITE, MAX_CONFIG);// Write config register to the ADC
    usleep(m_conversionDelay);// Wait for the conversion to complete
    uint16_t res = readMAXRegister(m_i2cAddress, MAX_READ);//>>m_bitShift;// Read the conversion results AND SHIFT RIGHT TO GET 12 BIT RESULT
    res = (res >> 11) == 0 ? res : -1 ^ 0xFFF | res;
    std::cout << std::bitset<16>(res) << " " << (int16_t)res << std::endl;
    return (int16_t)res;
}

Example 1 Output data:
When oscilloscope reading is -1.16V

FIRST OUTPUT FROM readMAXRegister(): 1111001010110010 << FIRST FOUR BITS ARE ALWAYS WRITTEN BY THE CHIP AS 1111

SECOND OUTPUT FROM readMAXRegister(): 0000001010110010 << ADJUSTED TO REMOVE FIRST 4 bits (1111)

LAST OUTPUT FROM readMAXADC_Differential_0_1(): 0000001010110010 690 << NOTE THIS IS A POSITIVE VALUE AND NOT THE EXPECTED NEGATIVE DIFFERENTIAL VALUE AND SEEMS TO CORRESPOND TO THE ACTUAL GND TO VDD VOLTAGE.

Example 2 Output Data:

When oscilloscope reading is +1.28V

FIRST OUTPUT FROM readMAXRegister(): 1111110011111110 << FIRST FOUR BITS ARE ALWAYS WRITTEN BY THE CHIP AS 1111

SECOND OUTPUT FROM readMAXRegister(): 0000110011111110 << ADJUSTED TO REMOVE FIRST 4 bits (1111)

LAST OUTPUT FROM readMAXADC_Differential_0_1(): 1111110011111011 -773 << NOTE THIS NEGATIVE VALUE SHOULD BE POSITIVE

Source: Windows Questions C++

LEAVE A COMMENT