BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

24-bit 16Channel ADC with 16 digital I/O expansion PCB

Fri Dec 27, 2013 9:15 pm

I just got this up and running and so far it appears to be working well. It is a single PCB containing an 8/16 channel 24-bit ADC (LTC2499) coupled with a 16 channel digital I/O (MCP23017). It replaces 4 prototype boards that I had stacked on top of each other to get the same functionality. It is a 2-layer PCB, measuring 1.82x2.96 inches (46.20x75.26 mm), so it's a bit smaller than the Pi. Here's a photo:
P1000024-s.jpg
P1000024-s.jpg (47.72 KiB) Viewed 15677 times
I am using it as part of a data acquisition and control system that replaces my home heating controls for my in-floor heating system.
Functionality:
  • 12 single-ended thermistor ADC inputs
    1 differential channel CDS (light) sensor (can be configured as 2 more thermistors)
    2 differential ADC inputs with no input conditioning (14-15)
    2 8-bit banks of digital I/O via MCP23017
    MCP23017 interrupt pins wired to Pi header
    ADC & MCP23017 I2C addresses set by resistors
    3 I2C expansion terminal blocks
    GPIOs 23-25 brought to terminal blocks
    2 3V3 outputs of internal regulator
    2 5V input/outputs
    stackable header
Here's a bottom view of the PCB showing I/O layout.
PCB_Bottom.jpg
PCB bottom view
PCB_Bottom.jpg (55.66 KiB) Viewed 15677 times
And here's the schematic:
https://dl.dropboxusercontent.com/u/265 ... roller.jpg

I placed op-amp buffers between the mux and input of the LTC2499 to allow the use of higher resistance sensors. I'm currently using 100k NTC thermistors in my system.
I have tested most of the functionality and am trying to establish ADC performance data. I've measured basic noise performance of shorted differential inputs with a common mode range of 0.5V to 3.0V - 600nV rms (which is the limiting specification of the LTC2499). Input offset voltage is running about 0.5uV. I'm getting higher noise from the thermistor interfaces, right now around 4uV, but am not sure where it is coming from yet so I'll post an update when I nail it down.
I can make the PCBs available for purchase from the OSH Park website. They cost about $27 for 3 boards. If there is any interest I'll make them a shared project and post the link here. I can also put a BOM together for the adventurous few. Just be aware that the LTC2499 is a 38 lead QFN package with an exposed pad - not easy to attach without special tools.

This circuit is at the absolute limit of my free schematic capture and PCB layout software package, so if you're going to suggest an "improvement" then it better be a good one.

Bud.

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Mon Dec 30, 2013 5:06 pm

I spent a few hours trying to measure the noise floor of the ADC. It’s difficult, with my setup, to get an accurate number when dealing with microvolts. Some results:

1. 500-600nV rms noise for shorted differential inputs with no signal conditioning. Input offset voltage ~ 0.5-1 uV. This is equivalent to that reported in the data sheet. This probably means that the supply noise is acceptable.

2. 1uV rms noise for shorted differential inputs with the thermistor input circuits. (Inputs shorted together with 2 100k resistors from the inputs to ground as bias.) Input offset voltage ~ 1uV. This is probably due to the additive thermal noise from the 10k input resistors, as expected.

3. 1.5-2.5 uV rms noise for thermistor input using the COM pin as the other input. Input offset voltage 2-5 mV (error produced by 1% tolerance resistors). Why is this so much worse? I believe there are two reasons:1) there is added kTC noise from the input RC circuit that is not cancelled when the inputs are shorted, and 2) the external bias resistor is not the same tempco as the on-board resistors and also not the same temperature. The external resistor is a through-hole metal film resistor, so it can have a 10-100ppm difference from the on-board SMD resistors. Also the external resistor is not thermally connected to the board so its temperature may fluctuate more. Consider that the input divider network tempco differs by 10ppm and that there is a 0.05C temperature variation during the measurement: the change in voltage is VDD/1.75*10ppm*0.05C ~ 1uV.

So what do these numbers mean for a 24-bit ADC?

The supply voltage (and reference) for the ADC is 3.3V. A true 24-bit ADC should have an rms noise floor about 1/6 to 1/8 of the 1/2LSB voltage. This is because the rms noise is essentially the standard deviation of the input voltage. To get peak-to-peak voltage you must multiply the rms value by 6 or 8, depending upon how conservative you are. Three SD contains 99.73% of the population, four SD contains 99.99%. All of this assumes that the noise distribution is normal.

So the 24-bit rms noise floor should be VDD/(2^25*6) = 16.4nV rms! Seems hopeless doesn’t it?…the inherent noise floor of the LTC2499 ADC input is 600nV rms.

The effective number of bits (ENOB) for a 2.5uV rms input noise is approximately 18 bits.

All is not lost.

Increasing the ADC reference voltage to 5V yields a fraction of a bit. But you risk giving up more than that if the 5V supply is noisy (which it is).

You can improve the ENOB if you have time. If the noise has a normal distribution, and is statistically independent, you can take multiple samples and average them. The effective rms noise decreases by the square-root of the number of samples. To increase the ENOB by 4 bits you must average 2^(4*2)=256 samples. I’m taking 10 successive samples and then put those into a rolling average of 5, so I believe that I should expect an ENOB of about 21 bits, or a 1/2LSB of about 0.8uV. Thank goodness temperature is slow moving.

Translation to temperature

The 100k NTC thermistors that I’m using have a beta of 4190 ohms/C +/- 1.5%. This translates to 3.4mV/C, so the ADC can resolve temperature to about 0.00025 degrees C (or 0.00042 degrees F if you are a Yank like me).

lacoursj
Posts: 2
Joined: Fri Oct 19, 2012 7:39 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Mon Feb 24, 2014 9:42 pm

Howdy,

My project will need 96 ADC inputs, though I don't need nearly the resolution you are achieving. I will have a matrix of hall effect sensors that I want to be able to poll for the existence of a magnetic field of varying intensity. I'll need to be able to distinguish between 32 different fixed field strengths, so anything more than 5 bits should suffice I would think. Any advice?

Cheers!

j

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Feb 25, 2014 4:59 am

I don't know...

If your 32 levels span the full range of the ADC, then you're right. If they only occupy a fraction of the range then you will need either more bits or an amplifier. In any case you should be able to get what you want from six 16channel analog multiplexers, an amplifier, and a cheap 8-bit ADC. If your signals are differential then double the multiplexers. You will probably need some digital I/O interface expansion to control the muxes, if you don't want to consume all of the GPIO.

Bud.

danjperron
Posts: 3402
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Fri Feb 28, 2014 1:54 am

Another approach which could make more sense is to use a small cpu like the pic18F26K80 with 8 channels at 12 bits resolution. So you could make a module of 8 sensors. After that you link them using i2c directly to the Raspberry Pi. They have their own clock so the number of external parts will be minimal. Programming a small cpu like this is quite easy with MPLAB from microchip and it is possible to program the cpu with the raspberry pi.

http://www.raspberrypi.org/forum/viewto ... 69#p429669

Daniel

danjperron
Posts: 3402
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Fri Feb 28, 2014 12:29 pm

Hi lacoursj,

I forget to mention.

This cpu has CAN BUS. Then it is very easy to put all the handling on the can bus itself.

1 - The Rpi send a CAN packet to start conversion. Or you could use a heart beat from one of the cpu to start the cycle.
2 - On reception of the CAN packet, each cpu will start to grab the voltage on each analog input.(Analog 0 to Analog 7) one at a time.
3 - Once done, each cpu will send 2 CAN packets . Each of them will hold 4 channels.


The beauty of the CAN BUS is the packet handling. You have nothing to do. It will be process by the hardware with priorities.

On The Raspberry Pi side. You will need to send a packet to start conversion and read all packets in returns.

You could run up to 1mbs on can bus but The Raspberry Pi will have problem to follow. With a 250Khz clock you should be able to sample all the 96 transistors at a rate of 50 / sec. This is 4800 data points / sec .


Also since everything is modular. You could just try with one cpu and when it is working add another module. You need to install 128 sensors, just plug 4 modules more. And debugging will be easyi since 8 sensors per modules. And I don't talk about the wiring. a simple cat 3 cable could link all modules together.

This is examples on how to use the cpu in can bus

http://www.raspberrypi.org/phpBB3/viewt ... 38#p444938
http://www.raspberrypi.org/phpBB3/viewt ... 60#p440760

Daniel

etrash2000
Posts: 3
Joined: Sun Jul 20, 2014 6:19 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Sun Jul 20, 2014 6:42 pm

Hi Bud,

I know it's a bit old post I hope that you still get this.
I have a small project which I hope to be able to use the LTC2499 combined with the Pi.

This project consist of monitoring 120 single-ended channels. For this project, I intend to design a board with 8 LTC2499.
The LTC2499 will monitor voltage over a resistor. The expected voltage drop over the resistor is about 100 to 300 mV and I will need a 1mV resolution.

Now my questions.
* What is your experience with using the LTC2499 in combination with the Raspberry Pi. Is it stable, the system needs to run for years without any interruption.
* What resolution do you get?

As a first prototype, if possible, I would like to use your setup. Would it be possibleto share the BOM and the needed files for PCB production and/or if you can make it available at OSH Park website.

Thanks a lot,
etrash2000

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Mon Jul 21, 2014 11:49 pm

Hi etrash,

My system has been running, without any detectable errors, since November of last year. I consider it pretty stable since it controls the heating system for my home.

My resolution is about 18 bits, or 2.5uVrms on a 3.3V reference (which is the supply).

I will check to see if my PCB software will print out a BOM listing and I can make the OSH Park project public. I will post again after checking out those items.

Bud.

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Jul 22, 2014 1:09 am

This is the link to a dropbox directory containing the gerber files that were used to create the PCB with OSH Park: https://www.dropbox.com/sh/3xz7ovh7jcfg ... Controller

You will find a BOM in that directory as well. Let me know if the BOM has sufficient info.

This is the link to the OSH Park Project: https://oshpark.com/shared_projects/zHmwP8eC

Bud.

etrash2000
Posts: 3
Joined: Sun Jul 20, 2014 6:19 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Wed Jul 23, 2014 12:06 am

Thanks Bud,

I have downloaded the files from Dropbox, it has been a great help.

I'll be on vacation for the next 3 weeks, so it will probably take some 4-6 weeks before I can order the parts.
If your interested, then I can keep you updated.

Looking at your design, would it be possible to stack 8 of these cards on top of the Pi and still be able to communicate will all boards? By the looks of, it seems possible, then I probably wont need to redesign your board.

Also, how did you obtain the 18bit resolution? Does the averaging improve the resolution that much?
I will most like do something similar since I only need to log data every hour or so.

How did you do with the communication with the ADC and how do you present or log it?
I just made a very crude prototype with a LTC2451 and the communication turned out to be a bit problematic. None of the standard libraries such as SMBus and wiringPi worked due to the 16-bit length. I had to do use ioclt and the read function for it it work correctly.

For the logging, I intend to run a webserver on the RPi with the ADC, where a master RPi could easily get the data from and log/present it in a nice format.

Regards,
etrash

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Wed Jul 23, 2014 5:03 am

etrash,

My board only has 14 single-ended channels, so you will need more than 8 boards for 120 system channels. Also, each board will have it's own reference so you will have to manage that unless you depopulate the 3.3V regulator on all but one board and feed the 3.3V supply to all of the other boards. I'll leave all of that up to you. It easy to add additional pull-up resistors for channels 14-15 to the existing layout but my free design software hit its limit at 300pins so I can't extend the design.

In order to communicate with 8 different boards you will have to program unique I2C addresses for each LTC2499 - there are six shorted resistors to do that on the board. If you aren't using the MCP23017 then just depopulate it.

As far as getting 18 bits of resolution my explanation above says that the ADC is averaging 50 samples, which works out to a factor of 7 improvement, or nearly 3 bits. You can improve on that, but the signal must be steady-state during the measurement process...and the noise must be independent and uncorrelated.

I'm using the Quick2Wire libraries to communicate with the LTC2499. The standard python smbus routines don't seem to work very well for block reads (as far as I can tell). Q2W requires python3, which causes it's own set of headaches. Here's my code:

Code: Select all

#!/usr/bin/env python3

# must use python 3 because of quick2wire interface

# LTC2499 I2C address is 7'b1110101 = 0x76 when all address pins tied high
import sys
import time
from Q2W_i2c import *
import math

class LTC2499:

    # define variables
    __LTC2499_config = 0b10100000 # set ADC for external input, 60Hz reject
    __LTC2499_temperature = 0b11100000 # performs an internal temp reading, 60Hz reject
    # define channel list as all zeros then populate with numbers
    __LTC2499_channel = [0,0,0,0,0,0,0,0,0,0,0,0]
    __LTC2499_channel[0] = 0b10110000 # thermistor: single-ended, normal polarity
    __LTC2499_channel[1] = 0b10111000 # thermistor: single-ended, normal polarity
    __LTC2499_channel[2] = 0b10110001 # thermistor: single-ended, normal polarity
    __LTC2499_channel[3] = 0b10111001 # thermistor: single-ended, normal polarity
    __LTC2499_channel[4] = 0b10110010 # thermistor: single-ended, normal polarity
    __LTC2499_channel[5] = 0b10111010 # thermistor: single-ended, normal polarity
    __LTC2499_channel[6] = 0b10110011 # thermistor: single-ended, normal polarity
    __LTC2499_channel[7] = 0b10111011 # thermistor: single-ended, normal polarity
    __LTC2499_LightSensor = 0b10100100 # light sensor:differential, normal polarity
    __LTC2499_channel[10] = 0b10110101 # thermistor: single-ended, normal polarity
    __LTC2499_channel[11] = 0b10111101 # thermistor: single-ended, normal polarity
    # channels 12-15 not assigned yet

    #define offset_temp list with zeros then populate with numbers
    __LTC2499_offset_temp = [0,0,0,0,0,0,0,0,0,0,0,0]
    __LTC2499_offset_temp[0] = -0.4  # adjust thermistor offset error
    __LTC2499_offset_temp[1] = -0.346
    __LTC2499_offset_temp[2] = 0.1112-0.7
    __LTC2499_offset_temp[3] = -0.2
    __LTC2499_offset_temp[4] = 0.1754
    __LTC2499_offset_temp[5] = -0.217
    __LTC2499_offset_temp[6] = -0.37
    __LTC2499_offset_temp[7] = 0.02
    __LTC2499_offset_temp[10] = -0.36
    __LTC2499_offset_temp[11] = -0.083


  # Constructor
    def __init__(self, address=0x76,sample_count = 10, debug=False):
        self.address = address
        self.sample_count = sample_count
        self.debug = debug


    def get_adc_conversion(self, channel, config):
        """ 
        This configures the LTC2499 for a conversion type on a channel. Then it will perform n
        conversions on that same channel. This is more efficient than performing a config and
        read for every conversion.
        """
        result_array = []
        time.sleep(0.15) # wait for previous conversion to end
        # set adc channel to convert
        Q2Wwrite8(self.address,channel,self.__LTC2499_config)

        # convert n times on the channel
        for i in range(self.sample_count):
            time.sleep(0.15)    # allow time for the conversion (Fconv ~ 7.5Hz)
            # read result into most significant Byte ... least significant Byte
            msB2, msB1, msB0, lsB = Q2WreadListNoReg(self.address,4)
            # the result is in two's complement, strip off sign bit and ms_bit for conversion to integer
            # the sign_bit is used to check for ADC overrange - implement this later       
            sign_bit = msB2
            sign_bit = sign_bit >>7 # extract the sign bit 
            ms_bit = msB2
            ms_bit = (ms_bit >> 6) & 0x01 #mask off the ms bit
            msB2 =   0x3F & msB2 # remove sign bit and ms_bit from msB2

            # shift the bytes by appropriate power and add together to get result
            ms_bit = ms_bit << 24
            result = (msB2 << 24) + (msB1 << 16)+ (msB0 << 8) + lsB
            result = result >> 7 # Shift the noise bits out of the result
            # convert to integer from two's complement and check for adc overflow
            if (ms_bit > 0 and sign_bit > 0):
                # this is an ADC overflow condition
                result_array.append(16777216/2 +1)
            elif(ms_bit > 0 and sign_bit == 0):
                result_array.append(result - 16777216/2)
            elif(ms_bit == 0 and sign_bit == 0):
                # this is an ADC overflow condition
                result_array.append(-16777216/2 -1)
            else:
                result_array.append(result)


        
        return result_array

    def get_adc_voltage(self, channel):
        v = self.get_adc_conversion(channel, self.__LTC2499_config)
        v[:] = [float(3.3 * x/16777216.0) for x in v]
        return v

    def get_adc_temperature(self):
        """ 
        Perform a conversion on the internal temp sensor of the LTC2499 chip and return the
        temperature in degrees F.
        """
        t = ((self.get_adc_conversion(self.__LTC2499_temperature,self.__LTC2499_config)* 3.3/1570.0)-273.0)*9.0/5.0 + 32.0
        return t

    def convert2temp(self,v):
        """
        This conversion only works for the Vishay NTCLE203E3-100k thermistor. 
        It uses the Steinhart-Hart equation and data published in the NTCLE203 datasheet.
        The thermistor is connected from the bottom of RS to GND. The top of RS is connected
        to the reference voltage Vref. The value of RS is chosen for an inflection point of 25C. 
        """
        open_therm = 0
        AAA = 0.000821724 # first coefficient of S-H equation
        BBB = 0.000206985 # second coefficient
        CCC =  9.89368E-8 # third coefficient 
        Rs =  75000.0 # value of resistor tied from thermistor to reference voltage
        R0 = 100000.0 # Thermistor value @ T = 25C
        Vref = 3.3  # voltage at top of resistor divider
        x = -(Rs*(R0*v + Rs*v + R0*Vref))/(R0*v + Rs*v - Rs*Vref)
        if (x < 0):
            # thermistor is probably open circuit
            x = 0
            temperatureF = 150.0
        else :
            temperatureC = -273 + 1/(AAA + BBB*math.log(x)  + CCC*math.log(x)**3)
            temperatureF = temperatureC * 9.0/5.0 + 32
        return temperatureF

    def meanstdv(self,x):
        """
        Calculate mean and standard deviation of data x[]:
        mean = {\sum_i x_i \over n}
        std = math.sqrt(\sum_i (x_i - mean)^2 \over n-1)
        """
        n, mean, std = len(x), 0, 0
        for a in x:
            mean = mean + a
        mean = mean / float(n)
        for a in x:
            std = std + (a - mean)**2
        if(n > 1):
            std = math.sqrt(std / float(n-1))
        else:
            std = 0.0
        return mean, std

    def measure_temperature(self,channel):
        x = self.get_adc_voltage(self.__LTC2499_channel[channel])
        x[:] = [self.convert2temp(v) for v in x]
        mean,std = self.meanstdv(x)
        mean += self.__LTC2499_offset_temp[channel]
        return mean


    def measure_lux(self):
        """Convert voltage across CDS light sensor to Lux. """
        Rtop = 10000
        Rbot = 1000
        Rs = float(Rtop+Rbot)
        Vref = 3.3
        v = self.get_adc_voltage(self.__LTC2499_LightSensor)
        vcds,std = self.meanstdv(v)
        if (vcds < 0):
            # something is wrong
            lux = -1
        else :
            rcds = vcds*Rs/(Vref - vcds)
            lux = 4370639.808904*rcds**(-1.305050099)
        return lux


# test code:
if __name__=="__main__":

    adc = LTC2499(0x76) #initialize

    mean = adc.measure_temperature(0)
    print("Channel 0 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(1)
    print("Channel 1 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(2)
    print("Channel 2 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(3)
    print("Channel 3 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(4)
    print("Channel 4 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(5)
    print("Channel 5 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(6)
    print("Channel 6 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(7)
    print("Channel 7 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(10)
    print("Channel 10 Temperature: {0:.3f} F".format(mean))
    mean = adc.measure_temperature(11)
    print("Channel 11 Temperature: {0:.3f} F".format(mean))

    lux = adc.measure_lux()
    print("Light Intensity: {0:.3f} lux".format(lux))
    
Here's the Q2W_i2c.py code:

Code: Select all

#!/usr/bin/python

import quick2wire.i2c as i2c


# ===========================================================================
# Quick2Wire_I2C functions
# ===========================================================================


def Q2WreverseByteOrder(data):
    "Reverses the byte order of an int (16-bit) or long (32-bit) value"
    # Courtesy Vishal Sapre
    byteCount = len(hex(data)[2:].replace('L','')[::2])
    val       = 0
    for i in range(byteCount):
        val    = (val << 8) | (data & 0xff)
        data >>= 8
    return val

def Q2WerrMsg(address,message = "Check your I2C address"):
    print ("Error accessing 0x%02X: %s" % (address,message))
    return -1

def Q2Wwrite8(address, reg, value, debug = False):
    "Writes an 8-bit value to the specified register/address"
    try:
        with i2c.I2CMaster() as bus:
            bus.transaction(
                i2c.writing_bytes(address, reg, value))
        if debug:
            print ("Q2W_I2C: Device 0x%02x wrote 0x%02X to register 0x%02X" % (address,value, reg))
    except IOError as err:
        message = "Check your I2C address"
        return Q2WerrMsg(address,message)

def Q2Wwrite16(address, reg, value, debug = False):
    "Writes a 16-bit value to the specified register/address pair"
    if (value > 0xffff):
        return Q2WerrMsg(address,"Value exceeds 16 bits")
    lsb = (value & 0x00ff)  # strip off the upper byte
    msb = value >> 8 
    try:
        with i2c.I2CMaster() as bus:
            bus.transaction(
                i2c.writing_bytes(address,bytes([reg,msb,lsb])))
        if debug:
            print ("Q2W_I2C: Device 0x%02x wrote 0x%02X to register pair 0x%02X,0x%02X" %
            (address,value, reg, reg+1))
    except IOError as err:
        return Q2WerrMsg(address)

def Q2WwriteList(address, reg, byte_seq,debug = False):
    '''Writes an sequence of bytes (a list of bytes) using I2C format.
       byte_seq = bytes(byte_seq) '''    
    try:
        if debug:
            print ("Q2W_I2C: Device 0x%02x writing list to register 0x%02X:" % (address,reg))
            print (byte_seq)
        byte_seq.prepend(reg)
        with i2c.I2CMaster() as bus:
            bus.transaction(
                i2c.writing_bytes(address, byte_seq))
    except IOError as err:
        return Q2WerrMsg(address)

def Q2WreadList(address, reg, length,debug = False):
    "Read a list of bytes from the I2C device"
    # results=[]
    try:
        with i2c.I2CMaster() as bus:
            results = bus.transaction(
                i2c.writing_bytes(address,reg),
                i2c.reading(address,length))[0]
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned the following from reg 0x%02X" %
             (address, reg))
            print (results)
        return results
    except IOError as err:
      return Q2WerrMsg(address)

def Q2WreadListNoReg(address, length, debug = False):
    '''Read a list of bytes from the I2C device without designating a register'''
    # results=[]
    try:
        with i2c.I2CMaster() as bus:
            results = bus.transaction(
                i2c.reading(address,length))[0]
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned the following:" % (address))
            print (results)
        return results
    except IOError as err:
      return Q2WerrMsg(address)

def Q2WreadU8(address, reg, debug = False):
    "Read an unsigned byte from the I2C device"
    try:
        with i2c.I2CMaster() as bus:
            result = bus.transaction(
                i2c.writing_bytes(address, reg),
                i2c.reading(address, 1))[0][0]
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
                (address, result & 0xFF, reg))
        return result
    except IOError as err:
      return Q2WerrMsg(address)

def Q2WreadS8(address, reg, debug = False):
    "Reads a signed byte from the I2C device"
    try:
        with i2c.I2CMaster() as bus:
            result = bus.transaction(
                i2c.writing_bytes(address, reg),
                i2c.reading(address, 1))[0][0]
        if result > 127: result -= 256
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
            (address, result & 0xFF, reg))
        return result
    except IOError as err:
      return Q2WerrMsg(address)

def Q2WreadU16(address, reg, debug = False):
    "Reads an unsigned 16-bit value from the I2C device"
    try:
        with i2c.I2CMaster() as bus:
            msb,lsb = bus.transaction(
                i2c.writing_bytes(address, reg),
                i2c.reading(address, 2))[0]
            result =  (msb << 8) + lsb
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" %
             (address, result & 0xFFFF, reg))
        return result
    except IOError as err:
      return Q2WerrMsg(address)

def Q2WreadS16(address, reg,debug = False):
    "Reads a signed 16-bit value from the I2C device"
    try:
        with i2c.I2CMaster() as bus:
            msb,lsb = bus.transaction(
                i2c.writing_bytes(address, reg),
                i2c.reading(address, 2))[0]
            result =  (msb << 8) + lsb
        if result > 32767: result -= 65536
        if debug:
            print ("Q2W_I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" %
             (address, result & 0xFFFF, reg))
        return result
    except IOError as err:
      return Q2WerrMsg(address)


if __name__ == '__main__':
    try:
        Q2WreadU8(address=0x20,reg=0, debug = True)
        print ("Default I2C bus is accessible")
    except:
        print ("Error accessing default I2C bus")

    Q2WreadU8(0x20,reg=0, debug = True)
Disclaimer: I'm sure my code is not very pretty, but it works well enough for me.

I save the ADC outputs to a SQLite3 database and access it via a web server that displays the data using flot.js. I'll leave all of that to you.

Good Luck,

Bud.

etrash2000
Posts: 3
Joined: Sun Jul 20, 2014 6:19 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Wed Jul 23, 2014 11:01 pm

Thanks again Bud,

I will probably design my own board later using 8 or more LTC2499. It seems easier to program and handle in general without involving the MPC.

Thanks for the code :)

regards
etrash

Bobity
Posts: 2
Joined: Sun Oct 09, 2016 7:28 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Sun Oct 09, 2016 7:34 pm

Bud,

Quick2wire is no longer available - could you post a copy of what "import quick2wire.i2c as i2c" contains ?

Thanks

Rob

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Oct 11, 2016 2:06 pm

Quick2wire is still available on GitHub: https://github.com/quick2wire/quick2wire-python-api

I don't use their install script but simply put the quick2wire-python-api-master folder in a convenient location and then add the two export lines to my PYTHONPATH as they recommend.

-Bud.

Bobity
Posts: 2
Joined: Sun Oct 09, 2016 7:28 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Thu Nov 10, 2016 9:14 am

Much appreciated Bud - just tried that works fine :)
Rob

juanchosf
Posts: 2
Joined: Wed Apr 11, 2018 3:39 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Thu Apr 12, 2018 9:11 pm

Hello,

I hope I get an answer. Could you please give me a link to watch & buy this 24bit 16Channel ADC?

Thank you!

Juan Manuel

Karl122
Posts: 2
Joined: Fri Apr 20, 2018 9:45 am

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Fri Apr 20, 2018 9:47 am

+1 where can i get it?

emdiamond
Posts: 1
Joined: Fri Apr 20, 2018 11:28 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Fri Apr 20, 2018 11:31 pm

Are these for sale? If so where to buy?

Karl122
Posts: 2
Joined: Fri Apr 20, 2018 9:45 am

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Mon Apr 23, 2018 1:49 pm

You can find it on tindie.com...

Look for other adc aswell, fercsa is offering one aswell

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Apr 24, 2018 9:27 pm

Whoa!!! I haven't been watching this posting, since it was nearly 4 years ago. The module is not for sale, but you can order PCBs from OSH park and make one. Unfortunately, I don't have a bill of materials and I removed the schematic from my Dropbox account some time ago. Making this module is not for a NOOB. All of the components are surface mounted and the ADC is a no-lead package type with an exposed pad and can't be soldered with a soldering iron -- you must use a hot air gun or other indirect heating method.

I wasn't aware that you could get it from tindie.com...really?

If there is sufficient interest I could add a project to my Hackaday.io page and put all of the details there.

Bud.

juanchosf
Posts: 2
Joined: Wed Apr 11, 2018 3:39 pm

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Thu Apr 26, 2018 4:55 pm

Hi!

I'm very interested!
I'd like to know some more specs about it: Like de maximum SPS and effective bits for 16 single-ended channels or 8 differential channels.

Also, how much do you think it might cost?

Is it possible that you assemble and send them ready to work?

Thank you!

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Thu Apr 26, 2018 7:35 pm

The LTC2499 is S-L-O-W, with a conversion SPS of 7.5Hz. I believe that I stated previously that the ENOB was about 18 bits. It can be improved by averaging multiple samples, if you are measuring slow moving signals like temperature.

As for cost...the PCB is around $10. The LTC2499 is $7.25 and the LTC6078 is $3.05 (from Digikey) and about $1.00 for the MCP23017. The rest of the components are a few pennies each. So figure around $25 for the assembled board.

Things have progressed a lot since the LTC2499 was introduced (back in 2006, I believe, using an ancient 2 µm CMOS process technology). And my design is already 4-5 years old. You might do much better with a newer ADC (the LTC2380 is 24-bit @ 2Msps: http://www.analog.com/media/en/technica ... 238024.pdf). I made two boards -- one has been running 24/7 for 4 years, and the other is a backup. I have one unpopulated PCB laying around somewhere, but I doubt you would want me to build one for you -- I value my time above all else.

I did manage to attach the schematic. Apparently, the forum limits for images has increased a bit.
TempController-2.jpg
TempController-2.jpg (144.57 KiB) Viewed 3756 times
Bud.

MauiJerry
Posts: 5
Joined: Sat May 18, 2019 4:08 am

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Jun 11, 2019 1:07 am

Stumbled on this old forum thread after finding the LC2499 and Analog's DC1012A-A demo/eval board, which includes a voltage reference and eeprom. Trying to use this board to read some thermocouples on a Raspberry Pi 3B+.
I currently have the device running on 3.3v with only a simple pot on Channel 0 for testing.
Thermocouples will go thru a thermocouple amp board, which runs at 5vdc. I'm holding off on 5v setup until I get a level shifter to protect my (clients) pi.
While the device responds with correct 0x76 address on i2cdetect, and it does not work in the python code given above.

Given all the years since this thread started, perhaps using smbus2 would be better (and might fit in with other i2c device that use that).

the test loop fails in get_adc_conversion() on the first write8 saying
Error accessing 0x76: Check your I2C Address

Thought I'd quick post here to see if any of y'all are still around

BudBennett
Posts: 89
Joined: Fri May 17, 2013 2:45 pm
Location: Westcliffe, Colorado, USA

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Jun 11, 2019 2:22 am

Yes, It’s been a long while since I started this thread. There are a lot of other solutions now that might be better. My board has been running continuously over the last 4 years. I recently upgraded the Raspberry Pi to Stretch and there are no problems with the I2C communication.

It’s odd that the device responds to i2cdetect, but not to a write request on the same address. The “Check your address” error usually means that the device is not connected or not powered.

I used the Quick2Wire API because the LTC2499 uses a repeated start that was not implemented in the Python smbus code. It might be fixed now. Take the path of least resistance.

Bud.

MauiJerry
Posts: 5
Joined: Sat May 18, 2019 4:08 am

Re: 24-bit 16Channel ADC with 16 digital I/O expansion PCB

Tue Jun 11, 2019 2:58 am

Thanks for fast response Bud!
The response to i2cdetect, but fail with Q2W is perplexing. I was thinking maybe it really needs 5v to run, but i found a MSc thesis Low-Cost Evolution in Materio that references this thread (credits it with solving months of problems with LTC2499). It appears to use a 3.3v source to drive the chip. I do note the post above about quality of that 3.3v for use on AD converters.

So I'm kinda at a loss as to why this is failing. I may try using Q2W on a BME280 (adafruit temp/pressure/humidity) sensor that already runs well with the Adafruit libraries. just to prove Q2W works

I do see that the repeated start issue is dealt with in smbus2 using the i2c_rdwr command https://github.com/kplindegaard/smbus2/issues/25. So trying that is another option.

Return to “Automation, sensing and robotics”