Shop OBEX P1 Docs P2 Docs Learn Events
Configuring A Programmable Gain Amplifier On An ADC - Page 2 — Parallax Forums

Configuring A Programmable Gain Amplifier On An ADC

2»

Comments

  • idbruceidbruce Posts: 6,197
    edited 2015-03-06 17:09
    Tracy and ksltd
    Okay, say 25°C minimum for the bed, and 100°C minimum for the extruder.
    Two temperature measurements, two thermistors?
    Take Wayne's suggestion and leave the ADC on the 2.048V pga setting.

    For the extruder, use a reference resistor of 4.12kΩ 1% or 0.1%, tied to 3.3V.
    At 100°C the thermistor resistance is 6338Ω and the ADC will see 2 volts. Use the PGA2 setting, 2.048V full scale.
    As the thermistor heats up to 260°C, the ADC will report values with a resolution of a bit better than 0.5°C. That should be fine.

    For the bed, use a reference resistor of 64.9kΩ 1% or 0.1% tied to 3.3V.
    At 25°C, the thermistor resistance is 100kΩ and the ADC will see 2 volts. Again the 2.048V pga scale.
    It will still have sufficient resolution for the 110°C hotbed.

    I now see why Tracy was talking about inverting R1 and R2, and I am now hoping that schematic is correct and that the board was made correctly.

    EDIT: On second hand perhaps I don't

    attachment.php?attachmentid=113412&d=1425690581
    456 x 240 - 27K
  • idbruceidbruce Posts: 6,197
    edited 2015-03-06 17:24
    Tracy and ksltd

    I thank you very very much for all your time and patience.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-03-07 10:34
    Hi Bruce, I'm sorry I confused the issue when I misspoke about inverting the arrangement of R1 and R2. I was miscontruing how the circuit was set up. Let's continue to talk about it as you set it up in post #1.
    attachment.php?attachmentid=113418&d=1425751751
    R2 is the reference connected to 3.3V and R1 is the thermistor, TDK #B57560G1104, 100kΩ at 25°C.

    Are there separate thermistors for the "extruder" and for the "bed"?

    The hotter it gets, the lower the resistance and the lower the voltage across R1 to be sensed by the ADC. The non-linear curve is such that the voltage steps become smaller and smaller as the temperature rises. The big advantage of an ADC with a PGA is that it is possible to write Prop code to change the gain factor dynamically. The system can maintain a high resolution of temperature across the whole range. Higher PGA gain at higher temperatures. That is the way I would do it, given that ADC. By the way, which ADC is it that you have? I use TI versions, ADSxxxx, but Microchip and others have similar parts.

    Once you have the voltage across the thermistor, what do you expect to do with it? My thought is that the Prop will then solve for the resistance, R1 = R2 * 3.3 / (3.3 - Vadc). Then use that to calculate the temperature or interpolate it in a table using the data provided by TDK.
    283 x 188 - 46K
  • idbruceidbruce Posts: 6,197
    edited 2015-03-07 12:09
    Tracy

    Thanks for getting back to me, because once again, I have been going in circles :)
    Are there separate thermistors for the "extruder" and for the "bed"?

    I don't have the bed made yet, but yes there will be a seperate thermistor for the bed.
    By the way, which ADC is it that you have?

    I have the ADS1015 from TI.
    Once you have the voltage across the thermistor, what do you expect to do with it?

    My intention was to use the Steinhart-Hart equation to turn the voltage reading into temperture, but I am rethinking this whole issue, however once again I may be barking up the wrong tree. It seems that a lot of people rely on tables, that further rely on the Steinhart-Hart equation. What is wrong with just calculating the output voltage from the values given in the datasheet along with the known value of the fixed resistor, and creating my own table from that data? In the source code below and in other source code that I have, when using the Steinhart-Hart equation, beta factors, thermal coefficients, and etc.. are brought in to determine the temperature. It appears that everything I need is right there in the datasheet, by just figuring out the output voltage from the voltage divide. Am I missing something???

    I hope you are decent with looking at C source code.

    C Source:
    /**************************************************************************/
    /*!
        @file     Adafruit_ADS1015.cpp
        @author   K.Townsend (Adafruit Industries)
        @license  BSD (see LICENSE.txt)
     
        Ported to mbed by Arve Seljebu - arve0.github.io
     
        Driver for the ADS1015/ADS1115 ADC
     
        This is a library for the Adafruit MPL115A2 breakout
        ----> https://www.adafruit.com/products/1083
     
        Adafruit invests time and resources providing this open source code,
        please support Adafruit and open-source hardware by purchasing
        products from Adafruit!
     
        @section  HISTORY
     
        v1.0 - First release
        v1.1.1 - Ported to mbed
    */
    /**************************************************************************/
    #include "simpletools.h"
    #include "simplei2c.h"
    #include "config.h"
    #include "Adafruit_ADS1015.h"
    #include <math.h>
    
    double extruder_celsius;
    double bed_celsius;
    
    /**************************************************************************/
    /*!
        @brief  Instantiates a new ADS1015 class w/appropriate properties
    */
    /**************************************************************************/
    void adc_init()
    {
        uint8_t i2cAddress;
        
        m_i2c = i2c_newbus(I2C_SCL,  I2C_SDA, I2C_MODE);
        i2cAddress = ADS1015_ADDRESS;    
        m_i2cAddress = i2cAddress;
        m_conversionDelay = ADS1015_CONVERSIONDELAY;
        m_bitShift = 4;
        m_gain = GAIN_TWO; // +/-2.048V range (default)
    }
    
    void UpdateTemp(uint8_t channel)
    {
         int samples[NUMSAMPLES];
    	uint8_t i;
    	double average;
    
    	// take N samples in a row, with a slight delay
    	for (i = 0; i < NUMSAMPLES; i++)
    	{
              // read channel the specified channel
    		samples[i] = readADC_SingleEnded(channel);
    		//pause(10);
    	}
    
    	// average all the samples out
    	average = 0;
    
    	for(i = 0; i < NUMSAMPLES; i++)
    	{
    		average += samples[i];
    	}
    
    	average /= NUMSAMPLES;
    
    	// convert the value to resistance
    	average = 4095 / average - 1;
    	average = SERIESRESISTOR / average;
    
    	double steinhart;
    
    	// (R/Ro)
    	steinhart = average / THERMISTORNOMINAL;
    
    	// ln(R/Ro)
    	steinhart = log(steinhart);
    
    	// 1/B * ln(R/Ro)
    	steinhart /= BCOEFFICIENT;
    
    	// + (1/To)
    	steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15);
    
    	// Invert
    	steinhart = 1.0 / steinhart;
    
    	// convert Kelvin to Celcius
    	steinhart -= 273.15;
     
    	if(channel == 0)
    	{
         	extruder_celsius = steinhart;
    	}
    	if(channel == 1)
    	{
         	bed_celsius = steinhart;
    	}
    }
     
    /**************************************************************************/
    /*!
        @brief  Writes 16-bits to the specified destination register
    */
    /**************************************************************************/
    void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value)
    {
        unsigned char cmd[3];
        cmd[0] = (char)reg;
        cmd[1] = (char)(value>>8);
        cmd[2] = (char)(value & 0xFF);
           
        i2c_out(m_i2c, i2cAddress, 0, 0, cmd, 3);
    } 
    
    /**************************************************************************/
    /*!
        @brief  Reads 16-bits from the specified register
    */
    /**************************************************************************/
    uint16_t readRegister(uint8_t i2cAddress, uint8_t reg)
    {
        unsigned char data[2];
        data[0] = reg; // temporary use this to send address to conversion register
        
        i2c_out(m_i2c, i2cAddress, 0, 0, data, 1);
        i2c_in(m_i2c, i2cAddress, 0, 0, data, 2);
    
        return (data[0] << 8 | data [1]);
    }
     
    /**************************************************************************/
    /*!
        @brief  Sets the gain and input voltage range
    */
    /**************************************************************************/
    void setGain(adsGain_t gain)
    {
        m_gain = gain;
    }
     
    /**************************************************************************/
    /*!
        @brief  Gets a gain and input voltage range
    */
    /**************************************************************************/
    adsGain_t getGain()
    {
        return m_gain;
    }
     
    /**************************************************************************/
    /*!
        @brief  Gets a single-ended ADC reading from the specified channel
    */
    /**************************************************************************/
    uint16_t readADC_SingleEnded(uint8_t channel)
    {
        if (channel > 3) {
            return 0;
        }
     
        // Start with default values
        uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
                          ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
                          ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                          ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
                          ADS1015_REG_CONFIG_DR_1600SPS   | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
                          ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)
     
        // Set PGA/voltage range
        config |= m_gain;
     
        // Set single-ended input channel
        switch (channel) {
            case (0):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
                break;
            case (1):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
                break;
            case (2):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
                break;
            case (3):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
                break;
        }
     
        // Set 'start single-conversion' bit
        config |= ADS1015_REG_CONFIG_OS_SINGLE;
     
        // Write config register to the ADC
        writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
     
        // Wait for the conversion to complete
        pause(m_conversionDelay);
     
        // Read the conversion results
        // Shift 12-bit results right 4 bits for the ADS1015
        return readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
    }
     
    /**************************************************************************/
    /*!
        @brief  Reads the conversion results, measuring the voltage
                difference between the P (AIN0) and N (AIN1) input.  Generates
                a signed value since the difference can be either
                positive or negative.
    */
    /**************************************************************************/
    int16_t readADC_Differential_0_1()
    {
        // Start with default values
        uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
                          ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
                          ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                          ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
                          ADS1015_REG_CONFIG_DR_1600SPS   | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
                          ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)
     
        // Set PGA/voltage range
        config |= m_gain;
     
        // Set channels
        config |= ADS1015_REG_CONFIG_MUX_DIFF_0_1;          // AIN0 = P, AIN1 = N
     
        // Set 'start single-conversion' bit
        config |= ADS1015_REG_CONFIG_OS_SINGLE;
     
        // Write config register to the ADC
        writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
     
        // Wait for the conversion to complete
        pause(m_conversionDelay);
     
        // Read the conversion results
        uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
        if (m_bitShift == 0) {
            return (int16_t)res;
        } else {
            // Shift 12-bit results right 4 bits for the ADS1015,
            // making sure we keep the sign bit intact
            if (res > 0x07FF) {
                // negative number - extend the sign to 16th bit
                res |= 0xF000;
            }
            return (int16_t)res;
        }
    }
     
    /**************************************************************************/
    /*!
        @brief  Reads the conversion results, measuring the voltage
                difference between the P (AIN2) and N (AIN3) input.  Generates
                a signed value since the difference can be either
                positive or negative.
    */
    /**************************************************************************/
    int16_t readADC_Differential_2_3()
    {
        // Start with default values
        uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
                          ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
                          ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                          ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
                          ADS1015_REG_CONFIG_DR_1600SPS   | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
                          ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)
     
        // Set PGA/voltage range
        config |= m_gain;
     
        // Set channels
        config |= ADS1015_REG_CONFIG_MUX_DIFF_2_3;          // AIN2 = P, AIN3 = N
     
        // Set 'start single-conversion' bit
        config |= ADS1015_REG_CONFIG_OS_SINGLE;
     
        // Write config register to the ADC
        writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
     
        // Wait for the conversion to complete
        pause(m_conversionDelay);
     
        // Read the conversion results
        uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
        if (m_bitShift == 0) {
            return (int16_t)res;
        } else {
            // Shift 12-bit results right 4 bits for the ADS1015,
            // making sure we keep the sign bit intact
            if (res > 0x07FF) {
                // negative number - extend the sign to 16th bit
                res |= 0xF000;
            }
            return (int16_t)res;
        }
    }
     
    /**************************************************************************/
    /*!
        @brief  Sets up the comparator to operate in basic mode, causing the
                ALERT/RDY pin to assert (go from high to low) when the ADC
                value exceeds the specified threshold.
     
                This will also set the ADC in continuous conversion mode.
    */
    /**************************************************************************/
    void startComparator_SingleEnded(uint8_t channel, int16_t threshold)
    {
        // Start with default values
        uint16_t config = ADS1015_REG_CONFIG_CQUE_1CONV   | // Comparator enabled and asserts on 1 match
                          ADS1015_REG_CONFIG_CLAT_LATCH   | // Latching mode
                          ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                          ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
                          ADS1015_REG_CONFIG_DR_1600SPS   | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
                          ADS1015_REG_CONFIG_MODE_CONTIN  | // Continuous conversion mode
                          ADS1015_REG_CONFIG_MODE_CONTIN;   // Continuous conversion mode
     
        // Set PGA/voltage range
        config |= m_gain;
     
        // Set single-ended input channel
        switch (channel) {
            case (0):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
                break;
            case (1):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
                break;
            case (2):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
                break;
            case (3):
                config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
                break;
        }
     
        // Set the high threshold register
        // Shift 12-bit results left 4 bits for the ADS1015
        writeRegister(m_i2cAddress, ADS1015_REG_POINTER_HITHRESH, threshold << m_bitShift);
     
        // Write config register to the ADC
        writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
    }
     
    /**************************************************************************/
    /*!
        @brief  In order to clear the comparator, we need to read the
                conversion results.  This function reads the last conversion
                results without changing the config value.
    */
    /**************************************************************************/
    int16_t getLastConversionResults()
    {
        // Wait for the conversion to complete
        pause(m_conversionDelay);
     
        // Read the conversion results
        uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
        if (m_bitShift == 0) {
            return (int16_t)res;
        } else {
            // Shift 12-bit results right 4 bits for the ADS1015,
            // making sure we keep the sign bit intact
            if (res > 0x07FF) {
                // negative number - extend the sign to 16th bit
                res |= 0xF000;
            }
            return (int16_t)res;
        }
    }
    

    H Source:
    /**************************************************************************/
    /*!
        @file       Adafruit_ADS1015.h
        @author     K. Townsend (Adafruit Industries)
        @license    BSD (see LICENSE.txt)
        
        Ported to mbed by Arve Seljebu - arve0.github.io
     
        This is a library for the Adafruit ADS1015 breakout board
        ----> https://www.adafruit.com/products/1083
     
        Adafruit invests time and resources providing this open source code,
        please support Adafruit and open-source hardware by purchasing
        products from Adafruit!
     
        @section  HISTORY
     
        v1.0  - First release
        v1.1  - Added ADS1115 support - W. Earl
        v1.1.1  - Ported to mbed - Arve Seljebu
    */
    /**************************************************************************/
    #include "simpletools.h"
    #include "simplei2c.h" 
    /*=========================================================================
        I2C ADDRESS/BITS
        -----------------------------------------------------------------------*/
        #define ADS1015_ADDRESS                 (0x48)    // 1001 000 (ADDR = GND)
    /*=========================================================================*/
     
    /*=========================================================================
        CONVERSION DELAY (in mS)
        -----------------------------------------------------------------------*/
        #define ADS1015_CONVERSIONDELAY         (1)
    /*=========================================================================*/
     
    /*=========================================================================
        POINTER REGISTER
        -----------------------------------------------------------------------*/
        #define ADS1015_REG_POINTER_MASK        (0x03)
        #define ADS1015_REG_POINTER_CONVERT     (0x00)
        #define ADS1015_REG_POINTER_CONFIG      (0x01)
        #define ADS1015_REG_POINTER_LOWTHRESH   (0x02)
        #define ADS1015_REG_POINTER_HITHRESH    (0x03)
    /*=========================================================================*/
     
    /*=========================================================================
        CONFIG REGISTER
        -----------------------------------------------------------------------*/
        #define ADS1015_REG_CONFIG_OS_MASK      (0x8000)
        #define ADS1015_REG_CONFIG_OS_SINGLE    (0x8000)  // Write: Set to start a single-conversion
        #define ADS1015_REG_CONFIG_OS_BUSY      (0x0000)  // Read: Bit = 0 when conversion is in progress
        #define ADS1015_REG_CONFIG_OS_NOTBUSY   (0x8000)  // Read: Bit = 1 when device is not performing a conversion
     
        #define ADS1015_REG_CONFIG_MUX_MASK     (0x7000)
        #define ADS1015_REG_CONFIG_MUX_DIFF_0_1 (0x0000)  // Differential P = AIN0, N = AIN1 (default)
        #define ADS1015_REG_CONFIG_MUX_DIFF_0_3 (0x1000)  // Differential P = AIN0, N = AIN3
        #define ADS1015_REG_CONFIG_MUX_DIFF_1_3 (0x2000)  // Differential P = AIN1, N = AIN3
        #define ADS1015_REG_CONFIG_MUX_DIFF_2_3 (0x3000)  // Differential P = AIN2, N = AIN3
        #define ADS1015_REG_CONFIG_MUX_SINGLE_0 (0x4000)  // Single-ended AIN0
        #define ADS1015_REG_CONFIG_MUX_SINGLE_1 (0x5000)  // Single-ended AIN1
        #define ADS1015_REG_CONFIG_MUX_SINGLE_2 (0x6000)  // Single-ended AIN2
        #define ADS1015_REG_CONFIG_MUX_SINGLE_3 (0x7000)  // Single-ended AIN3
     
        #define ADS1015_REG_CONFIG_PGA_MASK     (0x0E00)
        #define ADS1015_REG_CONFIG_PGA_6_144V   (0x0000)  // +/-6.144V range = Gain 2/3
        #define ADS1015_REG_CONFIG_PGA_4_096V   (0x0200)  // +/-4.096V range = Gain 1
        #define ADS1015_REG_CONFIG_PGA_2_048V   (0x0400)  // +/-2.048V range = Gain 2 (default)
        #define ADS1015_REG_CONFIG_PGA_1_024V   (0x0600)  // +/-1.024V range = Gain 4
        #define ADS1015_REG_CONFIG_PGA_0_512V   (0x0800)  // +/-0.512V range = Gain 8
        #define ADS1015_REG_CONFIG_PGA_0_256V   (0x0A00)  // +/-0.256V range = Gain 16
     
        #define ADS1015_REG_CONFIG_MODE_MASK    (0x0100)
        #define ADS1015_REG_CONFIG_MODE_CONTIN  (0x0000)  // Continuous conversion mode
        #define ADS1015_REG_CONFIG_MODE_SINGLE  (0x0100)  // Power-down single-shot mode (default)
     
        #define ADS1015_REG_CONFIG_DR_MASK      (0x00E0)  
        #define ADS1015_REG_CONFIG_DR_128SPS    (0x0000)  // 128 samples per second - ADS1115 8SPS
        #define ADS1015_REG_CONFIG_DR_250SPS    (0x0020)  // 250 samples per second - ADS1115 16SPS
        #define ADS1015_REG_CONFIG_DR_490SPS    (0x0040)  // 490 samples per second - ADS1115 32SPS
        #define ADS1015_REG_CONFIG_DR_920SPS    (0x0060)  // 920 samples per second - ADS1115 64SPS
        #define ADS1015_REG_CONFIG_DR_1600SPS   (0x0080)  // 1600 samples per second - ADS1115 250SPS (default)
        #define ADS1015_REG_CONFIG_DR_2400SPS   (0x00A0)  // 2400 samples per second - ADS1115 475SPS
        #define ADS1015_REG_CONFIG_DR_3300SPS   (0x00C0)  // 3300 samples per second - ADS1115 860SPS
     
        #define ADS1015_REG_CONFIG_CMODE_MASK   (0x0010)
        #define ADS1015_REG_CONFIG_CMODE_TRAD   (0x0000)  // Traditional comparator with hysteresis (default)
        #define ADS1015_REG_CONFIG_CMODE_WINDOW (0x0010)  // Window comparator
     
        #define ADS1015_REG_CONFIG_CPOL_MASK    (0x0008)
        #define ADS1015_REG_CONFIG_CPOL_ACTVLOW (0x0000)  // ALERT/RDY pin is low when active (default)
        #define ADS1015_REG_CONFIG_CPOL_ACTVHI  (0x0008)  // ALERT/RDY pin is high when active
     
        #define ADS1015_REG_CONFIG_CLAT_MASK    (0x0004)  // Determines if ALERT/RDY pin latches once asserted
        #define ADS1015_REG_CONFIG_CLAT_NONLAT  (0x0000)  // Non-latching comparator (default)
        #define ADS1015_REG_CONFIG_CLAT_LATCH   (0x0004)  // Latching comparator
     
        #define ADS1015_REG_CONFIG_CQUE_MASK    (0x0003)
        #define ADS1015_REG_CONFIG_CQUE_1CONV   (0x0000)  // Assert ALERT/RDY after one conversions
        #define ADS1015_REG_CONFIG_CQUE_2CONV   (0x0001)  // Assert ALERT/RDY after two conversions
        #define ADS1015_REG_CONFIG_CQUE_4CONV   (0x0002)  // Assert ALERT/RDY after four conversions
        #define ADS1015_REG_CONFIG_CQUE_NONE    (0x0003)  // Disable the comparator and put ALERT/RDY in high state (default)
    /*=========================================================================*/
     
    typedef enum {
        GAIN_TWOTHIRDS    = ADS1015_REG_CONFIG_PGA_6_144V,
        GAIN_ONE          = ADS1015_REG_CONFIG_PGA_4_096V,
        GAIN_TWO          = ADS1015_REG_CONFIG_PGA_2_048V,
        GAIN_FOUR         = ADS1015_REG_CONFIG_PGA_1_024V,
        GAIN_EIGHT        = ADS1015_REG_CONFIG_PGA_0_512V,
        GAIN_SIXTEEN      = ADS1015_REG_CONFIG_PGA_0_256V
    } adsGain_t;
     
        // Instance-specific properties
        uint8_t   m_i2cAddress;
        uint8_t   m_conversionDelay;
        uint8_t   m_bitShift;
        adsGain_t m_gain;
        i2c*      m_i2c;
        extern    double extruder_celsius;
        extern    double bed_celsius;
     
        uint16_t  readADC_SingleEnded(uint8_t channel);
        int16_t   readADC_Differential_0_1(void);
        int16_t   readADC_Differential_2_3(void);
        void      startComparator_SingleEnded(uint8_t channel, int16_t threshold);
        int16_t   getLastConversionResults();
        void      setGain(adsGain_t gain);
        adsGain_t getGain(void);
     
        uint16_t readRegister(uint8_t i2cAddress, uint8_t reg);
        void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value);
        
        void UpdateTemp(uint8_t channel);
        void adc_init(void);
    
    Software License Agreement (BSD License)
    
    Copyright (c) 2012, Adafruit Industries
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
    
  • idbruceidbruce Posts: 6,197
    edited 2015-03-07 14:29
    It appears that everything I need is right there in the datasheet, by just figuring out the output voltage from the voltage divide. Am I missing something???

    For instance, going back to the 4.7K resistor, here is how a table looks when made with the datasheet. Is there any reason I could not do this? However, there is the situation of thermistors self-heating, but I read that with the 4.7K resistor, it is not that bad. Do I really need all the other steinhart- blah blah blah?
    Table With A Fixed Resistor Of 4700 Ohms
    And 3.3V For VREF And VDD
    *V is the signal voltage going to the ADC
    
    °C	V	Resistance At Temperature
    20	3.181	125420
    25	3.152	100000
    30	3.117	80239
    35	3.077	64776
    40	3.029	52598
    45	2.975	42950
    50	2.912	35262
    55	2.841	29100
    60	2.762	24136
    65	2.675	20114
    70	2.58	16841
    75	2.478	14164
    80	2.369	11963
    85	2.255	10147
    90	2.137	8640.7
    95	2.017	7386.7
    100	1.895	6338.3
    105	1.773	5458.4
    110	1.653	4717
    115	1.536	4090.1
    120	1.422	3558.1
    125	1.313	3105
    130	1.209	2717.9
    135	1.111	2386.1
    140	1.019	2100.8
    145	0.934	1854.8
    150	0.854	1641.9
    155	0.781	1457.3
    160	0.714	1296.7
    165	0.652	1156.6
    170	0.595	1034.1
    175	0.543	926.64
    180	0.496	832.24
    185	0.454	749.07
    190	0.415	675.64
    195	0.379	610.64
    200	0.347	552.99
    205	0.318	501.75
    210	0.292	456.11
    215	0.268	415.37
    220	0.246	378.95
    225	0.226	346.31
    230	0.209	317.01
    235	0.192	290.67
    240	0.177	266.93
    245	0.164	245.51
    250	0.151	226.15
    255	0.14	208.62
    260	0.13	192.73
    265	0.121	178.29
    270	0.112	165.16
    275	0.104	153.19
    280	0.097	142.28
    

    EDIT: Now let's take it a step further. I don't see how Note (1) applies when using FS = ±4.096V(1):
    (1) This parameter expresses the full-scale range of the ADC scaling. In no event should more than VDD + 0.3V be applied to this device.

    I do not see how this can physically happen in my situation, so it should not be a concern. My question now becomes, if that table is acceptable, without needing special code to decipher the temperatures, is the 4.096V scale accurate enough to do comparisons against the values shown?
  • idbruceidbruce Posts: 6,197
    edited 2015-03-07 15:20
    I just found a very interesting piece of code, and since we are on the topics of 3D printers and thermistors, I thought I would share this link. The old script and old tables should now be history.

    http://blog.reprap.org/2012/06/say-goodby-to-thermistor-table-misery.html
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-03-07 17:17
    I think what you are proposing in post #36 is fine. The code can look up and interpolate in that table directly from voltage to temperature. If you don't want to mess with floating point and one of the calculation methods like Steinhart-Hart, then the table is much faster and easier with integer math. It could alternatively take it in two steps, from voltage to resistance then resistance to temperature.

    At 250°C, the voltage is changing at about 2.1 mV/°C, and the resolution of the ADC on the 4.096V range happens to be 2mV. So that is okay if +/- 1°C resolution is going to be in the right ballpark for the machine.

    The resolution is better at lower temperatures. 0.3 °C at room temperature, and 0.08 °C at around 100°C where the thermistor resistance is close to the 4.7kΩ reference.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-07 17:37
    Tracy

    Thanks for getting back to me. I have to make a run, but when I get back, I will be testings the table with a pot and see how it all works out in theory. And if everything seems fine, then I will test the thermistor within the next couple days.

    Anyhow, I really appreciate all of your input on this matter.

    By the way, which TI ADC do you use? And will that code work for you?
  • idbruceidbruce Posts: 6,197
    edited 2015-03-07 20:49
    Tracy

    It does not look like I will get around to testing tonight, because I have gone off on another tangent.
    The code can look up and interpolate in that table directly from voltage to temperature. If you don't want to mess with floating point and one of the calculation methods like Steinhart-Hart, then the table is much faster and easier with integer math. It could alternatively take it in two steps, from voltage to resistance then resistance to temperature.

    What I want to do now is precompute the signal voltages into RAW ADC values and put those values into the table, instead of the signal voltages. I will put those values on the left and the temperatures on the right. By doing that, I can quickly compare the actual RAW ADC values against the RAW table values.

    I will keep you posted.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-03-08 12:58
    Bruce, I use the ADS1115. It is the 16 bit version, otherwise much the same as the 12 bit ADS1015. The same set of PGA gains, down to 8 µV steps on the most sensitive range of ±256mV.

    While it is true that the inputs cannot be allowed to past the rails by more than 0.3V, it is also true and very useful in some applications (like power supply shunts) that the ADC will quite happily and accurately measure voltages that are outside the power supply limits by up to around 256mV.

    I'm sorry, I can't help with the C code.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-08 13:56
    Tracy
    I'm sorry, I can't help with the C code.

    That code was originally for both the ADS1015 and the ADS1115, but I removed the support for the ADS1115 during the port.

    Instead of waiting for a low tolerance resistor, I used the actual resistance of a 4.7K(4602 Ohms) to create my table. I believe this is correct and the leftmost items should match ADC input, whereas the rightmost items are the temperature at that ADC reading. However I am still working on this....
    // The number of value pairs in the table.
    #define NUMOFPAIRS 20
    
    // This is a table of ADC readings and corresponding temperature values for both the
    // extruder and heated build platform.  In determining the thermistor resistance values,
    // data was extracted from the EPCOS B57560G1104F000 datasheet, because that is the
    // thermistor being utilized for both the extruder and heated build platform.
    //
    // During the calculations of this table, a resistor having a nominal value of 4.7K,
    // with a 10% tolerance was utilized, however the calculations were performed and based
    // upon the actual resistance, which was 4,602 Ohms.
    const uint16_t valuetable[][NUMOFPAIRS] = 
    {	{3950,20},
    	{3915,25},
    	{3873,30},
    	{3823,35},
    	{3766,40},
    	{3699,45},
    	{3622,50},
    	{3535,55},
    	{3439,60},
    	{3333,65},
    	{3216,70},
    	{3091,75},
    	{2957,80},
    	{2817,85},
    	{2672,90},
    	{2523,95},
    	{2373,100},
    	{2221,105},
    	{2072,110},
    	{1927,115},
    	{1786,120},
    	{1650,125},
    	{1520,130},
    	{1398,135},
    	{1283,140},
    	{1176,145},
    	{1077,150},
    	{985,155},
    	{900,160},
    	{823,165},
    	{751,170},
    	{686,175},
    	{627,180},
    	{573,185},
    	{524,190},
    	{480,195},
    	{439,200},
    	{402,205},
    	{370,210},
    	{339,215},
    	{311,220},
    	{287,225},
    	{264,230},
    	{243,235},
    	{225,240},
    	{207,245},
    	{192,250},
    	{177,255},
    	{165,260},
    	{153,265},
    	{141,270},
    	{131,275},
    	{123,280}	};
    
  • idbruceidbruce Posts: 6,197
    edited 2015-03-09 08:33
    As mentioned many times before, math and datasheets are just not my thing..... And apparently it is coming back to bite, because the readings, results, and actual settings just don't jive.

    In my sample reading and lookup routine, I am receiving a sample of 1155, then looking it up in the table, which returns a temperature of 150 degrees for the table value of 1077. Using the sample value of 1155 and by looking at the table below, you will see that the lookup and table reading part is working correctly. According to the datasheet, at 150 degrees, the thermistor should have a resistance of 1641.9 Ohms, but when I disconnect all of the wires going to the potentiometer and take a reading, I get 10,300 Ohms. So apparently my math is off somewhere.

    Problem Summary: Resistance should be at 1641.9 Ohms, but is actually 10,300 Ohms.

    Sample Provided: 1155
    Table Value: 1077
    Return Temperature: 150
    Potentiometer Setting: 10,300 Ohms
    Datasheet Resistance at 150 degrees: 1641.9 Ohms (R1)
    Series Resistor In Voltage Divider: 4602 Ohms (R2)
    Signal Voltage Output To ADC: 0.854V (According to my calculations)
    VDD: 3.3V

    When calculating this particular table entry, this would have been the formula and parameters that I used:

    EDIT: This don't jive, because the table value should be 1059

    1077 = 0.854V * 4095 / 3.3V
    or
    Table Value = ADC Signal Voltage * Number of ADC Readings / VDD

    Is this the correct formula for obtaining the table value?

    attachment.php?attachmentid=113423&d=1425915183
    // The number of elements in the lookup table.
    #define TABLE_SIZE 53
    //
    // This is a table of ADC readings and corresponding temperature values for both the
    // extruder and heated build platform.  In determining the thermistor resistance values,
    // data was extracted from the EPCOS B57560G1104F000 datasheet, because that is the
    // thermistor being utilized for both the extruder and heated build platform.
    //
    // During the calculations of this table, a resistor having a nominal value of 4.7K,
    // with a 10% tolerance was utilized, however the calculations were performed and based
    // upon the actual resistance, which was 4,602 Ohms.
    LOOKUPTEMP lookup_temp[TABLE_SIZE] = {
    	{.sample = 3950, .temp = 20},
    	{.sample = 3915, .temp = 25},
    	{.sample = 3873, .temp = 30},
    	{.sample = 3823, .temp = 35},
    	{.sample = 3766, .temp = 40},
    	{.sample = 3699, .temp = 45},
    	{.sample = 3622, .temp = 50},
    	{.sample = 3535, .temp = 55},
    	{.sample = 3439, .temp = 60},
    	{.sample = 3333, .temp = 65},
    	{.sample = 3216, .temp = 70},
    	{.sample = 3091, .temp = 75},
    	{.sample = 2957, .temp = 80},
    	{.sample = 2817, .temp = 85},
    	{.sample = 2672, .temp = 90},
    	{.sample = 2523, .temp = 95},
    	{.sample = 2373, .temp = 100},
    	{.sample = 2221, .temp = 105},
    	{.sample = 2072, .temp = 110},
    	{.sample = 1927, .temp = 115},
    	{.sample = 1786, .temp = 120},
    	{.sample = 1650, .temp = 125},
    	{.sample = 1520, .temp = 130},
    	{.sample = 1398, .temp = 135},
    	{.sample = 1283, .temp = 140},
    	{.sample = 1176, .temp = 145},
    	{.sample = 1077, .temp = 150},
    	{.sample = 985, .temp = 155},
    	{.sample = 900, .temp = 160},
    	{.sample = 823, .temp = 165},
    	{.sample = 751, .temp = 170},
    	{.sample = 686, .temp = 175},
    	{.sample = 627, .temp = 180},
    	{.sample = 573, .temp = 185},
    	{.sample = 524, .temp = 190},
    	{.sample = 480, .temp = 195},
    	{.sample = 439, .temp = 200},
    	{.sample = 402, .temp = 205},
    	{.sample = 370, .temp = 210},
    	{.sample = 339, .temp = 215},
    	{.sample = 311, .temp = 220},
    	{.sample = 287, .temp = 225},
    	{.sample = 264, .temp = 230},
    	{.sample = 243, .temp = 235},
    	{.sample = 225, .temp = 240},
    	{.sample = 207, .temp = 245},
    	{.sample = 192, .temp = 250},
    	{.sample = 177, .temp = 255},
    	{.sample = 165, .temp = 260},
    	{.sample = 153, .temp = 265},
    	{.sample = 141, .temp = 270},
    	{.sample = 131, .temp = 275},
    	{.sample = 123, .temp = 280},
    };
    
    165 x 129 - 4K
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-03-09 09:40
    This is incorrect:
    [s]1077 = 0.854V * 4095 / 3.3V[/s]
    or
    [s]Table Value = ADC Signal Voltage * Number of ADC Readings / VDD[/s]
    

    When the 4.096V range is chosen, the resolution is 0.001V 0.002V per bit.
    TableValue = ADCSignalVoltage      
    ' expressed as millivolts;   854 mV is table value 854.
    
  • idbruceidbruce Posts: 6,197
    edited 2015-03-09 10:22
    Tracy

    Okay, if I understand you correctly.....

    I should take all of my calculated signal voltages and multiply them by 1000 and those results become my actual "sample" values in the table.

    Is that correct?

    I will create a new table in the meantime, like stated above.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-09 10:54
    I am still doing something wrong :(

    The program is now reporting a temp of 135 and according to my understanding of the datasheet, the thermistor should have a resistance of 2386.1 Ohms at that point. However the potentiometer is still set at about 10,300 Ohms.

    AAAARRRRGGGG

    EDIT: Adding a copy of the new table, although it is useless.
    EDIT: Assuming the current 10300 Ohm setting of the pot and 4602 Ohms for the fixed resistor, this is the formula I am using for obtainining the signal value. Is this correct?

    SIGNAL = R1 / (R1 + R2) * VDD
    2.281 = 10300 / (10300 + 4602) * 3.3
    LOOKUPTEMP lookup_temp[TABLE_SIZE] = {
    	{.sample = 3183, .temp = 20},
    	{.sample = 3155, .temp = 25},
    	{.sample = 3121, .temp = 30},
    	{.sample = 3081, .temp = 35},
    	{.sample = 3035, .temp = 40},
    	{.sample = 2981, .temp = 45},
    	{.sample = 2919, .temp = 50},
    	{.sample = 2849, .temp = 55},
    	{.sample = 2772, .temp = 60},
    	{.sample = 2686, .temp = 65},
    	{.sample = 2592, .temp = 70},
    	{.sample = 2491, .temp = 75},
    	{.sample = 2383, .temp = 80},
    	{.sample = 2270, .temp = 85},
    	{.sample = 2153, .temp = 90},
    	{.sample = 2033, .temp = 95},
    	{.sample = 1912, .temp = 100},
    	{.sample = 1790, .temp = 105},
    	{.sample = 1670, .temp = 110},
    	{.sample = 1553, .temp = 115},
    	{.sample = 1439, .temp = 120},
    	{.sample = 1330, .temp = 125},
    	{.sample = 1225, .temp = 130},
    	{.sample = 1127, .temp = 135},
    	{.sample = 1034, .temp = 140},
    	{.sample = 948, .temp = 145},
    	{.sample = 868, .temp = 150},
    	{.sample = 794, .temp = 155},
    	{.sample = 725, .temp = 160},
    	{.sample = 663, .temp = 165},
    	{.sample = 605, .temp = 170},
    	{.sample = 553, .temp = 175},
    	{.sample = 505, .temp = 180},
    	{.sample = 462, .temp = 185},
    	{.sample = 422, .temp = 190},
    	{.sample = 387, .temp = 195},
    	{.sample = 354, .temp = 200},
    	{.sample = 324, .temp = 205},
    	{.sample = 298, .temp = 210},
    	{.sample = 273, .temp = 215},
    	{.sample = 251, .temp = 220},
    	{.sample = 231, .temp = 225},
    	{.sample = 213, .temp = 230},
    	{.sample = 196, .temp = 235},
    	{.sample = 181, .temp = 240},
    	{.sample = 167, .temp = 245},
    	{.sample = 155, .temp = 250},
    	{.sample = 143, .temp = 255},
    	{.sample = 133, .temp = 260},
    	{.sample = 123, .temp = 265},
    	{.sample = 114, .temp = 270},
    	{.sample = 106, .temp = 275},
    	{.sample = 99, .temp = 280},
    };
    
  • edited 2015-03-09 11:38
    If you don't need a temperature display you could make use of the Vdd / 2 switching point of CMOS inputs. Adjust R1 to the value of the thermistor at the target temperature. Feed the output to a prop input pin instead of the ADC. Just feed the input pin signal, inverted, to an output pin. That should give you kind of an automatic PWM signal for your heating element.

    This scheme depends on the Vdd / 2 switching point being consistant. Might work.

    Sandy
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-03-09 12:11
    Sorry Bruce. On the 4096V range, it is 0.002V per bit. I have a knee-jerk response to the number 4096, but the full range if really 8192mV, covering from -4096 to +4096 mV. The raw ADCcount goes from -2048 to +2048.

    So ADCcount =signalVoltage * 500.
    milliVolts = ADCcount * 2 on the 4.096V range, PGA=1

    This formula is correct for voltage...
    signalVolts = 3.3 * R1 / (R1 + R2)
    2.281 = 3.3 * 10300 / (10300 + 4602) * 3.3

    Redo the table using the values for R1 at temperatures...
    ADCcount = 3.3 * R1 / (R1 + R2) * 500

    Example
    ADCcount = 500 * 3.3 * 2386 / (2386+4602) = 500 * 1.126 = 563 at 135°C

    ADCcount = 500 * 3.3 * 100000 / (100000+4602) = 500 * 3.155 = 1577 at 25°C
  • idbruceidbruce Posts: 6,197
    edited 2015-03-09 12:34
    Tracy

    Okay, it now looks like we have something, because I have 2.98V at the signal. So redoing the table one more time ought to do it. Thanks for looking that over and giving me guidance.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-09 14:38
    Tracy

    Yep, that did her...

    It was very difficult to adjust that pot.... Very sensitive :) But here are the results I came up with:
    25 Degrees	Potentiometer Setting: 82000 Ohms	Datasheet Specification: 100000 Ohms
    50 Degrees	Potentiometer Setting: 37980 Ohms	Datasheet Specification: 35262 Ohms
    75 Degrees	Potentiometer Setting: 14220 Ohms	Datasheet Specification: 14164 Ohms
    100 Degrees	Potentiometer Setting: 6550 Ohms	Datasheet Specification: 6338 Ohms
    125 Degrees	Potentiometer Setting: 3200 Ohms	Datasheet Specification: 3105 Ohms
    150 Degrees	Potentiometer Setting: 1500 Ohms	Datasheet Specification: 1642 Ohms
    175 Degrees	Potentiometer Setting: 935 Ohms		Datasheet Specification: 927 Ohms
    200 Degrees	Potentiometer Setting: 322 Ohms		Datasheet Specification: 553 Ohms
    225 Degrees	Potentiometer Setting: 330 Ohms		Datasheet Specification: 346 Ohms
    250 Degrees	Potentiometer Setting: 172 Ohms		Datasheet Specification: 226 Ohms
    275 Degrees	Potentiometer Setting: 146 Ohms		Datasheet Specification: 153 Ohms
    

    Thank you very much Tracy. I would have been going in circles for days. I truly appreciate all of your assistance, patience, and the fact that you stuck it through with me.

    Bruce
  • TappermanTapperman Posts: 319
    edited 2015-03-10 13:16
    Bruce,

    A lot of good suggestions ... to be sure ... here's one more (file it or flush it)

    I put it in a PDF because I don't know how to use the BB codes to integrate graphs?

    ... Tim
  • idbruceidbruce Posts: 6,197
    edited 2015-03-10 13:33
    Tim

    At this point, it is basically a non-issue, since my code is now working. However, I truly did not understand it, because I am not that smart pertaining to electronics. If I implement anything, I like to fully comprehend it. And in this case, I am choosing the least path of resistance :)

    However, please don't be discouraged, I am sure someone will find the information useful.
  • TappermanTapperman Posts: 319
    edited 2015-03-10 15:00
    idbruce wrote: »
    ... If I implement anything, I like to fully comprehend it...

    Amen to that ... I like to know what's going on under the hood. But for those interested?

    [post=1150885]This Post[/post] has details of theory for V2F.

    ... Tim
Sign In or Register to comment.