Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: TI ADS1256 8 CH, 24 bit A/D

  1. #1
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354

    TI ADS1256 8 CH, 24 bit A/D

    I recently purchased an ADS1256 Module for $17.
    The TI ADS1256 is a 24 Bit 8 Channel ADC with SPI communication. It offers a PGA with up to 64x gain and acquisition rates up to 30kHz.
    It does not support negative voltages relative to ground for analog input as I learned (disappointingly).

    Here is an image of the module:
    Click image for larger version. 

Name:	IMG_20180119_173046.jpg 
Views:	819 
Size:	173.7 KB 
ID:	12787

    I started working with the code developed by dariosalvi on the arduino forums here
    I modified that code to get it working properly and added lots of comments and info from the datasheet.

    I'm using it with a Teensy 3.2 running at 120 MHz and 2.5 MHz SPI
    Currently compiling under Arduino 1.8.5 with Teensyduino 1.40

    Here is the working code for reading all 8 single ended channels (AINx-AINCOM)
    Code:
    /* ADS1256, datasheet: http://www.ti.com/lit/ds/sbas288j/sbas288j.pdf
      compare: https://github.com/Flydroid/ADS12xx-Library/blob/master/ads12xx.cpp
    
      Reads all 8 Single Ended Channels AINx-AINCOM
      Arduino 1.8.5, Teensyduino 1.40
      
        Connections to Teensy 3.2
    
        ADS1256       Teensy 3.2
        DVDD (5V)   - 5V
        DGND (GND}  - GND
        SCLK        - pin 13 (SCK)
        DIN         - pin 11 (MOSI)
        DOUT        - pin 12 (MISO)
        DRDY        - pin 9
        CS          - pin 10 (CS)
        RESET       - pin 8 (or tie HIGH?)
    */
    
    #define cs 10 // chip select
    #define rdy 9 // data ready, input
    #define rst 8 // may omit
    
    #define SPISPEED 2500000   // Teensy 3.2 @120 mhz
    
    #include <SPI.h>
    
    void setup()
    {
      Serial.begin(115200);
      
      pinMode(cs, OUTPUT);
      digitalWrite(cs, LOW); // tied low is also OK.
      pinMode(rdy, INPUT);
      pinMode(rst, OUTPUT);
      digitalWrite(rst, LOW);
      delay(1); // LOW at least 4 clock cycles of onboard clock. 100 microseconds is enough
      digitalWrite(rst, HIGH); // now reset to default values
      
      delay(500);
      SPI.begin(); //start the spi-bus
      delay(500);
    
      //init
      while (digitalRead(rdy)) {}  // wait for ready_line to go low
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      digitalWrite(cs, LOW);
      delayMicroseconds(100);
    
      //Reset to Power-Up Values (FEh)
      SPI.transfer(0xFE);
      delay(5);
    
    /******************************************************************************************************************
    STATUS : STATUS REGISTER (ADDRESS 00h)
    Reset Value = x1h
      BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
      ID       ID       ID       ID       ORDER    ACAL     BUFEN    DRDY
    Bits 7-4 ID3, ID2, ID1, ID0 Factory Programmed Identification Bits (Read Only)
    Bit 3 ORDER: Data Output Bit Order
    0 = Most Significant Bit First (default)
    1 = Least Significant Bit First
    Input data is always shifted in most significant byte and bit first. Output data is always shifted out most significant
    byte first. The ORDER bit only controls the bit order of the output data within the byte.
    Bit 2 ACAL: Auto-Calibration
    0 = Auto-Calibration Disabled (default)
    1 = Auto-Calibration Enabled
    When Auto-Calibration is enabled, self-calibration begins at the completion of the WREG command that changes
    the PGA (bits 0-2 of ADCON register), DR (bits 7-0 in the DRATE register) or BUFEN (bit 1 in the STATUS register)
    values.
    Bit 1 BUFEN: Analog Input Buffer Enable
    0 = Buffer Disabled (default)
    1 = Buffer Enabled
    Bit 0 DRDY: Data Ready (Read Only)
    This bit duplicates the state of the DRDY pin.
    **************************************************************************************************************/
      byte status_reg = 0x00 ;  // address (datasheet p. 30)
      byte status_data = 0x01; // 01h = 0000 0 0 0 1 => status: Most Significant Bit First, Auto-Calibration Disabled, Analog Input Buffer Disabled
      //byte status_data = 0x07; // 01h = 0000 0 1 1 1 => status: Most Significant Bit First, Auto-Calibration Enabled, Analog Input Buffer Enabled
      SPI.transfer(0x50 | status_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(status_data);   // write the databyte to the register
      delayMicroseconds(100);
      
    /***************************************************************************************************************
    ADCON: A/D Control Register (Address 02h)
    Reset Value = 20h
    BIT 7   BIT 6   BIT 5   BIT 4   BIT 3   BIT 2   BIT 1   BIT 0
    0       CLK1    CLK0    SDCS1   SDCS0   PGA2    PGA1    PGA0
    Bit 7 Reserved, always 0 (Read Only)
    Bits 6-5 CLK1, CLK0: D0/CLKOUT Clock Out Rate Setting
    00 = Clock Out OFF
    01 = Clock Out Frequency = fCLKIN (default)
    10 = Clock Out Frequency = fCLKIN/2
    11 = Clock Out Frequency = fCLKIN/4
    When not using CLKOUT, it is recommended that it be turned off. These bits can only be reset using the RESET pin.
    Bits 4-2 SDCS1, SCDS0: Sensor Detect Current Sources
    00 = Sensor Detect OFF (default)
    01 = Sensor Detect Current = 0.5μA
    10 = Sensor Detect Current = 2μA
    11 = Sensor Detect Current = 10μA
    The Sensor Detect Current Sources can be activated to verify the integrity of an external sensor supplying a signal to the
    ADS1255/6. A shorted sensor produces a very small signal while an open-circuit sensor produces a very large signal.
    Bits 2-0 PGA2, PGA1, PGA0: Programmable Gain Amplifier Setting
    
    PGA SETTING
    00h = 000 = 1   5V(default)
    01h = 001 = 2   2.5V
    02h = 010 = 4   1.25V
    03h = 011 = 8   0.625V
    04h = 100 = 16  312.5mV
    05h = 101 = 32  156.25mV
    06h = 110 = 64  78.125mV
    07h = 111 = 64  78.125mV
    **********************************************************************************************************************/
      byte adcon_reg = 0x02; //A/D Control Register (Address 02h)
      //byte adcon_data = 0x20; // 0 01 00 000 => Clock Out Frequency = fCLKIN, Sensor Detect OFF, gain 1
      byte adcon_data = 0x00; // 0 00 00 000 => Clock Out = Off, Sensor Detect OFF, gain 1
      //byte adcon_data = 0x01;   // 0 00 00 001 => Clock Out = Off, Sensor Detect OFF, gain 2
      SPI.transfer(0x50 | adcon_reg);  // 52h = 0101 0010
      SPI.transfer(0x00);              // 2nd command byte, write one register only
      SPI.transfer(adcon_data);        // write the databyte to the register
      delayMicroseconds(100);
    
    /*********************************************************************************************************
    DRATE: A/D Data Rate (Address 03h)
    Reset Value = F0h
    BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
    DR7     DR6       DR5      DR4      DR3      DR2      DR1      DR0
    The 16 valid Data Rate settings are shown below. Make sure to select a valid setting as the invalid settings may produce
    unpredictable results.
    Bits 7-0 DR[7: 0]: Data Rate Setting(1)
    F0h = 11110000 = 30,000SPS (default)
    E0h = 11100000 = 15,000SPS
    D0h = 11010000 = 7,500SPS
    C0h = 11000000 = 3,750SPS
    B0h = 10110000 = 2,000SPS
    A1h = 10100001 = 1,000SPS
    92h = 10010010 = 500SPS
    82h = 10000010 = 100SPS
    72h = 01110010 = 60SPS
    63h = 01100011 = 50SPS
    53h = 01010011 = 30SPS
    43h = 01000011 = 25SPS
    33h = 00110011 = 15SPS
    23h = 00100011 = 10SPS
    13h = 00010011 = 5SPS
    03h = 00000011 = 2.5SPS
    (1) for fCLKIN = 7.68MHz. Data rates scale linearly with fCLKIN. 
     ***********************************************************************************************/
      byte drate_reg = 0x03; //DRATE: A/D Data Rate (Address 03h)
      byte drate_data = 0xF0; // F0h = 11110000 = 30,000SPS
      SPI.transfer(0x50 | drate_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(drate_data);   // write the databyte to the register
      delayMicroseconds(100);
    
      // Perform Offset and Gain Self-Calibration (F0h)
      SPI.transfer(0xF0);     
      delay(400);
      digitalWrite(cs, HIGH);
      SPI.endTransaction();
      
      while (!Serial && (millis ()  <=  5000));  // WAIT UP TO 5000 MILLISECONDS FOR SERIAL OUTPUT CONSOLE
      Serial.println("configured, starting");
      Serial.println("");
      Serial.println("AIN0-AINCOM  AIN1-AINCOM  AIN2-AINCOM  AIN3-AINCOM  AIN4-AINCOM  AIN5-AINCOM  AIN6-AINCOM  AIN7-AINCOM");
    }
    
    
    void loop()
    {
    
      //Single ended Measurements
      unsigned long adc_val[8] = {0,0,0,0,0,0,0,0}; // store readings in array
      byte mux[8] = {0x08,0x18,0x28,0x38,0x48,0x58,0x68,0x78};
      
      int i = 0;
    
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      digitalWrite(cs, LOW);
      delayMicroseconds(2);
      
     /***************************************************************************
     Settling Time Using the Input Multiplexer
    The most efficient way to cycle through the inputs is to
    change the multiplexer setting (using a WREG command
    to the multiplexer register MUX) immediately after DRDY
    goes low. Then, after changing the multiplexer, restart the
    conversion process by issuing the SYNC and WAKEUP
    commands, and retrieve the data with the RDATA
    command. Changing the multiplexer before reading the
    data allows the ADS1256 to start measuring the new input
    channel sooner. Figure 19 demonstrates efficient input
    cycling. There is no need to ignore or discard data while
    cycling through the channels of the input multiplexer
    because the ADS1256 fully settles before DRDY goes low,
    indicating data is ready.
    
    Step 1: When DRDY goes low, indicating that data is ready for retrieval,
    update the multiplexer register MUX using the WREG command. For example,
    setting MUX to 23h gives AINP = AIN2, AINN = AIN3.
    
    Step 2: Restart the conversion process by issuing a SYNC command
    immediately followed by a WAKEUP command.
    Make sure to follow timing specification t11 between commands.
    
    Step 3: Read the data from the previous conversion using the RDATA command.
    
    Step 4: When DRDY goes low again, repeat the cycle by first
    updating the multiplexer register, then reading the previous data.
    ***************************************************************************************/
      for (i=0; i <= 7; i++){         // read all 8 Single Ended Channels AINx-AINCOM
      byte channel = mux[i];             // analog in channels # 
      
      while (digitalRead(rdy)) {} ;                          
    
    /*
     WREG: Write to Register
    Description: Write to the registers starting with the register specified as part of the command. The number of registers that
    will be written is one plus the value of the second byte in the command.
    1st Command Byte: 0101 rrrr where rrrr is the address to the first register to be written.
    2nd Command Byte: 0000 nnnn where nnnn is the number of bytes to be written – 1.
    Data Byte(s): data to be written to the registers. 
     */
      //byte data = (channel << 4) | (1 << 3); //AIN-channel and AINCOM   // ********** Step 1 **********
      //byte data = (channel << 4) | (1 << 1)| (1); //AIN-channel and AINCOM   // ********** Step 1 **********
      //Serial.println(channel,HEX);
      SPI.transfer(0x50 | 0x01); // 1st Command Byte: 0101 0001  0001 = MUX register address 01h
      SPI.transfer(0x00);     // 2nd Command Byte: 0000 0000  1-1=0 write one byte only
      SPI.transfer(channel);     // Data Byte(s): xxxx 1000  write the databyte to the register(s)
      delayMicroseconds(2);
    
      //SYNC command 1111 1100                               // ********** Step 2 **********
      SPI.transfer(0xFC);
      delayMicroseconds(2);
    
      //while (!digitalRead(rdy)) {} ;
      //WAKEUP 0000 0000
      SPI.transfer(0x00);
      delayMicroseconds(250);   // Allow settling time
    
    /*
    MUX : Input Multiplexer Control Register (Address 01h)
    Reset Value = 01h
    BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
    PSEL3    PSEL2    PSEL1    PSEL0    NSEL3    NSEL2    NSEL1    NSEL0
    Bits 7-4 PSEL3, PSEL2, PSEL1, PSEL0: Positive Input Channel (AINP) Select
    0000 = AIN0 (default)
    0001 = AIN1
    0010 = AIN2 (ADS1256 only)
    0011 = AIN3 (ADS1256 only)
    0100 = AIN4 (ADS1256 only)
    0101 = AIN5 (ADS1256 only)
    0110 = AIN6 (ADS1256 only)
    0111 = AIN7 (ADS1256 only)
    1xxx = AINCOM (when PSEL3 = 1, PSEL2, PSEL1, PSEL0 are “don’t care”)
    NOTE: When using an ADS1255 make sure to only select the available inputs.
    
    Bits 3-0 NSEL3, NSEL2, NSEL1, NSEL0: Negative Input Channel (AINN)Select
    0000 = AIN0
    0001 = AIN1 (default)
    0010 = AIN2 (ADS1256 only)
    0011 = AIN3 (ADS1256 only)
    0100 = AIN4 (ADS1256 only)
    0101 = AIN5 (ADS1256 only)
    0110 = AIN6 (ADS1256 only)
    0111 = AIN7 (ADS1256 only)
    1xxx = AINCOM (when NSEL3 = 1, NSEL2, NSEL1, NSEL0 are “don’t care”)
    NOTE: When using an ADS1255 make sure to only select the available inputs.
     */
    
      SPI.transfer(0x01); // Read Data 0000  0001 (01h)       // ********** Step 3 **********
      delayMicroseconds(5);
      
      adc_val[i] = SPI.transfer(0);
      adc_val[i] <<= 8; //shift to left
      adc_val[i] |= SPI.transfer(0);
      adc_val[i] <<= 8;
      adc_val[i] |= SPI.transfer(0);
      delayMicroseconds(2);
      }                                // Repeat for each channel ********** Step 4 **********
      
      digitalWrite(cs, HIGH);
      SPI.endTransaction();
    
      //The ADS1255/6 output 24 bits of data in Binary Two's
      //Complement format. The LSB has a weight of
      //2VREF/(PGA(223 − 1)). A positive full-scale input produces
      //an output code of 7FFFFFh and the negative full-scale
      //input produces an output code of 800000h. 
    
      for (i=0; i <= 7; i++){   // Single ended Measurements 
      if(adc_val[i] > 0x7fffff){   //if MSB == 1
        adc_val[i] = adc_val[i]-16777216; //do 2's complement
      }
    
      Serial.print(adc_val[i]);   // Raw ADC integer value +/- 23 bits
      Serial.print("      ");
      }
      Serial.println();
      delay(250);
    
    }
    And here is the working code for reading all 4 differential channel pairs (AIN0-AIN1, AIN2-AIN3, AIN4-AIN5, AIN6-AIN7)
    Code:
    /* ADS1256, datasheet: http://www.ti.com/lit/ds/sbas288j/sbas288j.pdf
      compare: https://github.com/Flydroid/ADS12xx-Library/blob/master/ads12xx.cpp
    
    Reads all 4 differential channel pairs AIN0-AIN1, AIN2-AIN3, AIN4-AIN5, AIN6-AIN7
    Arduino 1.8.5, Teensyduino 1.40
    
        Connections to Teensy 3.2
    
        ADS1256       Teensy 3.2
        DVDD (5V)   - 5V
        DGND (GND}  - GND
        SCLK        - pin 13 (SCK)
        DIN         - pin 11 (MOSI)
        DOUT        - pin 12 (MISO)
        DRDY        - pin 9
        CS          - pin 10 (CS)
        RESET       - pin 8 (or tie HIGH?)
    */
    
    #define cs 10 // chip select
    #define rdy 9 // data ready, input
    #define rst 8 // may omit
    
    #define SPISPEED 2500000   // Teensy 3.2 @120 mhz
    
    #include <SPI.h>
    
    void setup()
    {
      Serial.begin(9600);
      
      pinMode(cs, OUTPUT);
      digitalWrite(cs, LOW); // tied low is also OK.
      pinMode(rdy, INPUT);
      pinMode(rst, OUTPUT);
      digitalWrite(rst, LOW);
      delay(1); // LOW at least 4 clock cycles of onboard clock. 100 microseconds is enough
      digitalWrite(rst, HIGH); // now reset to default values
      
      delay(500);
      SPI.begin(); //start the spi-bus
      delay(500);
    
      //init
      while (digitalRead(rdy)) {}  // wait for ready_line to go low
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      digitalWrite(cs, LOW);
      delayMicroseconds(100);
    
      //Reset to Power-Up Values (FEh)
      SPI.transfer(0xFE);
      delay(5);
    
    /******************************************************************************************************************
    STATUS : STATUS REGISTER (ADDRESS 00h)
    Reset Value = x1h
      BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
      ID       ID       ID       ID       ORDER    ACAL     BUFEN    DRDY
    Bits 7-4 ID3, ID2, ID1, ID0 Factory Programmed Identification Bits (Read Only)
    Bit 3 ORDER: Data Output Bit Order
    0 = Most Significant Bit First (default)
    1 = Least Significant Bit First
    Input data is always shifted in most significant byte and bit first. Output data is always shifted out most significant
    byte first. The ORDER bit only controls the bit order of the output data within the byte.
    Bit 2 ACAL: Auto-Calibration
    0 = Auto-Calibration Disabled (default)
    1 = Auto-Calibration Enabled
    When Auto-Calibration is enabled, self-calibration begins at the completion of the WREG command that changes
    the PGA (bits 0-2 of ADCON register), DR (bits 7-0 in the DRATE register) or BUFEN (bit 1 in the STATUS register)
    values.
    Bit 1 BUFEN: Analog Input Buffer Enable
    0 = Buffer Disabled (default)
    1 = Buffer Enabled
    Bit 0 DRDY: Data Ready (Read Only)
    This bit duplicates the state of the DRDY pin.
    **************************************************************************************************************/
      byte status_reg = 0x00 ;  // address (datasheet p. 30)
      byte status_data = 0x01; // 01h = 0000 0 0 0 1 => status: Most Significant Bit First, Auto-Calibration Disabled, Analog Input Buffer Disabled
      //byte status_data = 0x07; // 01h = 0000 0 1 1 1 => status: Most Significant Bit First, Auto-Calibration Enabled, Analog Input Buffer Enabled
      SPI.transfer(0x50 | status_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(status_data);   // write the databyte to the register
      delayMicroseconds(100);
      
    /***************************************************************************************************************
    ADCON: A/D Control Register (Address 02h)
    Reset Value = 20h
    BIT 7   BIT 6   BIT 5   BIT 4   BIT 3   BIT 2   BIT 1   BIT 0
    0       CLK1    CLK0    SDCS1   SDCS0   PGA2    PGA1    PGA0
    Bit 7 Reserved, always 0 (Read Only)
    Bits 6-5 CLK1, CLK0: D0/CLKOUT Clock Out Rate Setting
    00 = Clock Out OFF
    01 = Clock Out Frequency = fCLKIN (default)
    10 = Clock Out Frequency = fCLKIN/2
    11 = Clock Out Frequency = fCLKIN/4
    When not using CLKOUT, it is recommended that it be turned off. These bits can only be reset using the RESET pin.
    Bits 4-2 SDCS1, SCDS0: Sensor Detect Current Sources
    00 = Sensor Detect OFF (default)
    01 = Sensor Detect Current = 0.5μA
    10 = Sensor Detect Current = 2μA
    11 = Sensor Detect Current = 10μA
    The Sensor Detect Current Sources can be activated to verify the integrity of an external sensor supplying a signal to the
    ADS1255/6. A shorted sensor produces a very small signal while an open-circuit sensor produces a very large signal.
    Bits 2-0 PGA2, PGA1, PGA0: Programmable Gain Amplifier Setting
    
    PGA SETTING
    00h = 000 = 1   5V(default)
    01h = 001 = 2   2.5V
    02h = 010 = 4   1.25V
    03h = 011 = 8   0.625V
    04h = 100 = 16  312.5mV
    05h = 101 = 32  156.25mV
    06h = 110 = 64  78.125mV
    07h = 111 = 64  78.125mV
    **********************************************************************************************************************/
      byte adcon_reg = 0x02; //A/D Control Register (Address 02h)
      //byte adcon_data = 0x20; // 0 01 00 000 => Clock Out Frequency = fCLKIN, Sensor Detect OFF, gain 1
      byte adcon_data = 0x00; // 0 00 00 000 => Clock Out = Off, Sensor Detect OFF, gain 1
      //byte adcon_data = 0x01;   // 0 00 00 001 => Clock Out = Off, Sensor Detect OFF, gain 2
      SPI.transfer(0x50 | adcon_reg);  // 52h = 0101 0010
      SPI.transfer(0x00);              // 2nd command byte, write one register only
      SPI.transfer(adcon_data);        // write the databyte to the register
      delayMicroseconds(100);
    
    /*********************************************************************************************************
    DRATE: A/D Data Rate (Address 03h)
    Reset Value = F0h
    BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
    DR7     DR6       DR5      DR4      DR3      DR2      DR1      DR0
    The 16 valid Data Rate settings are shown below. Make sure to select a valid setting as the invalid settings may produce
    unpredictable results.
    Bits 7-0 DR[7: 0]: Data Rate Setting(1)
    F0h = 11110000 = 30,000SPS (default)
    E0h = 11100000 = 15,000SPS
    D0h = 11010000 = 7,500SPS
    C0h = 11000000 = 3,750SPS
    B0h = 10110000 = 2,000SPS
    A1h = 10100001 = 1,000SPS
    92h = 10010010 = 500SPS
    82h = 10000010 = 100SPS
    72h = 01110010 = 60SPS
    63h = 01100011 = 50SPS
    53h = 01010011 = 30SPS
    43h = 01000011 = 25SPS
    33h = 00110011 = 15SPS
    23h = 00100011 = 10SPS
    13h = 00010011 = 5SPS
    03h = 00000011 = 2.5SPS
    (1) for fCLKIN = 7.68MHz. Data rates scale linearly with fCLKIN. 
     ***********************************************************************************************/
      byte drate_reg = 0x03; //DRATE: A/D Data Rate (Address 03h)
      byte drate_data = 0xF0; // F0h = 11110000 = 30,000SPS
      SPI.transfer(0x50 | drate_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(drate_data);   // write the databyte to the register
      delayMicroseconds(100);
    
      // Perform Offset and Gain Self-Calibration (F0h)
      SPI.transfer(0xF0);     
      delay(400);
      digitalWrite(cs, HIGH);
      SPI.endTransaction();
    
      while (!Serial && (millis ()  <=  5000));  // WAIT UP TO 5000 MILLISECONDS FOR SERIAL OUTPUT CONSOLE
      Serial.println("configured, starting");
      Serial.println("");
      Serial.println("AIN0-AIN1    AIN2-AIN3    AIN4-AIN5    AIN6-AIN7");
    }
    
    void loop(){
    
      // Differential Measurements
      long adc_val[4] = {0,0,0,0}; // store readings in array
      byte mux[4] = {0x01,0x23,0x45,0x67};
      
      int i = 0;
    
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      digitalWrite(cs, LOW);
      delayMicroseconds(2);
      
     /***************************************************************************
     Settling Time Using the Input Multiplexer
    The most efficient way to cycle through the inputs is to
    change the multiplexer setting (using a WREG command
    to the multiplexer register MUX) immediately after DRDY
    goes low. Then, after changing the multiplexer, restart the
    conversion process by issuing the SYNC and WAKEUP
    commands, and retrieve the data with the RDATA
    command. Changing the multiplexer before reading the
    data allows the ADS1256 to start measuring the new input
    channel sooner. Figure 19 demonstrates efficient input
    cycling. There is no need to ignore or discard data while
    cycling through the channels of the input multiplexer
    because the ADS1256 fully settles before DRDY goes low,
    indicating data is ready.
    
    Step 1: When DRDY goes low, indicating that data is ready for retrieval,
    update the multiplexer register MUX using the WREG command. For example,
    setting MUX to 23h gives AINP = AIN2, AINN = AIN3.
    
    Step 2: Restart the conversion process by issuing a SYNC command
    immediately followed by a WAKEUP command.
    Make sure to follow timing specification t11 between commands.
    
    Step 3: Read the data from the previous conversion using the RDATA command.
    
    Step 4: When DRDY goes low again, repeat the cycle by first
    updating the multiplexer register, then reading the previous data.
    ***************************************************************************************/
      for (i=0; i <= 3; i++){            // read all 4 differential channel pairs AIN0-AIN1, AIN2-AIN3, AIN4-AIN5, AIN6-AIN7
      byte channel = mux[i];             // analog in channels # 
      
      while (digitalRead(rdy)) {} ;                          
    
    /*
     WREG: Write to Register
    Description: Write to the registers starting with the register specified as part of the command. The number of registers that
    will be written is one plus the value of the second byte in the command.
    1st Command Byte: 0101 rrrr where rrrr is the address to the first register to be written.
    2nd Command Byte: 0000 nnnn where nnnn is the number of bytes to be written – 1.
    Data Byte(s): data to be written to the registers. 
     */
      //byte data = (channel << 4) | (1 << 3); //AIN-channel and AINCOM   // ********** Step 1 **********
      //byte data = (channel << 4) | (1 << 1)| (1); //AIN-channel and AINCOM   // ********** Step 1 **********
      //Serial.println(channel,HEX);
      SPI.transfer(0x50 | 0x01); // 1st Command Byte: 0101 0001  0001 = MUX register address 01h
      SPI.transfer(0x00);     // 2nd Command Byte: 0000 0000  1-1=0 write one byte only
      SPI.transfer(channel);     // Data Byte(s): xxxx 1000  write the databyte to the register(s)
      delayMicroseconds(2);
    
      //SYNC command 1111 1100                               // ********** Step 2 **********
      SPI.transfer(0xFC);
      delayMicroseconds(2);
    
      //while (!digitalRead(rdy)) {} ;
      //WAKEUP 0000 0000
      SPI.transfer(0x00);
      delayMicroseconds(250);   // Allow settling time
    
    /*
    MUX : Input Multiplexer Control Register (Address 01h)
    Reset Value = 01h
    BIT 7    BIT 6    BIT 5    BIT 4    BIT 3    BIT 2    BIT 1    BIT 0
    PSEL3    PSEL2    PSEL1    PSEL0    NSEL3    NSEL2    NSEL1    NSEL0
    Bits 7-4 PSEL3, PSEL2, PSEL1, PSEL0: Positive Input Channel (AINP) Select
    0000 = AIN0 (default)
    0001 = AIN1
    0010 = AIN2 (ADS1256 only)
    0011 = AIN3 (ADS1256 only)
    0100 = AIN4 (ADS1256 only)
    0101 = AIN5 (ADS1256 only)
    0110 = AIN6 (ADS1256 only)
    0111 = AIN7 (ADS1256 only)
    1xxx = AINCOM (when PSEL3 = 1, PSEL2, PSEL1, PSEL0 are “don’t care”)
    NOTE: When using an ADS1255 make sure to only select the available inputs.
    
    Bits 3-0 NSEL3, NSEL2, NSEL1, NSEL0: Negative Input Channel (AINN)Select
    0000 = AIN0
    0001 = AIN1 (default)
    0010 = AIN2 (ADS1256 only)
    0011 = AIN3 (ADS1256 only)
    0100 = AIN4 (ADS1256 only)
    0101 = AIN5 (ADS1256 only)
    0110 = AIN6 (ADS1256 only)
    0111 = AIN7 (ADS1256 only)
    1xxx = AINCOM (when NSEL3 = 1, NSEL2, NSEL1, NSEL0 are “don’t care”)
    NOTE: When using an ADS1255 make sure to only select the available inputs.
     */
    
        
      SPI.transfer(0x01); // Read Data 0000  0001 (01h)       // ********** Step 3 **********
      delayMicroseconds(5);
      
      adc_val[i] = SPI.transfer(0);
      adc_val[i] <<= 8; //shift to left
      adc_val[i] |= SPI.transfer(0);
      adc_val[i] <<= 8;
      adc_val[i] |= SPI.transfer(0);
      delayMicroseconds(2);
      }                                // Repeat for each channel ********** Step 4 **********
      
      digitalWrite(cs, HIGH);
      SPI.endTransaction();
    
      //The ADS1255/6 output 24 bits of data in Binary Two's
      //Complement format. The LSB has a weight of
      //2VREF/(PGA(223 − 1)). A positive full-scale input produces
      //an output code of 7FFFFFh and the negative full-scale
      //input produces an output code of 800000h. 
    
      for (i=0; i <= 3; i++){
      if(adc_val[i] > 0x7fffff){   //if MSB == 1
        adc_val[i] = adc_val[i]-16777216; //do 2's complement
      }
    
      Serial.print(adc_val[i]);   // Raw ADC integer value +/- 23 bits
      Serial.print("      ");
      }
      Serial.println();
      delay(250);
    
    }
    Next I want to try to get the Data Ready signal working correctly using interrupts on the Teensy.
    All in all it's not a bad ADC module. The AINCOM on the module is tied to ground so it can't be used as a floating differential channel even though it's independently muxed to the ADC.
    Also you can't use all 24 bits if you have a +/-5V signal without shifting and attenuating the signals. otherwise this module works very well and is well designed with seemingly high quality components.

    There's also a library by Flydroid with many features for the ADS1256
    Last edited by Wozzy; 01-29-2018 at 03:40 AM.

  2. #2
    Junior Member
    Join Date
    May 2018
    Posts
    3

    Updated Code for ADS1256 and Teensy

    Hi Wozzy (and all)!

    I have been working a lot with the Teensy and ADS1256 lately and have compiled my own library/code using the work of the others listed in your post plus some interpretation of the datasheet myself. It implements interrupt-based data ready and seems to get the maximum speed available out of the chip.

    Videos on how it works and the code are available on my website here: https://mattbilsky.com/mediawiki/ind...nd_the_ADS1256

    Hopefully this helps!

  3. #3
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    Curious if you find this ADS1256 chip occasionally resets when you didn't want it to. Among other things, the reset means it returns to its default of 30 kHz sampling rate, so that can be a problem if you were using a different rate. It can be reset by the hardware RESET/ pin and by the software reset command and by a special pattern on the SPI clock line, so that's three different ways. In my circuit with my code that is maybe one too many :-)

  4. #4
    Junior Member
    Join Date
    May 2018
    Posts
    3

    Thoughts...

    Quote Originally Posted by JBeale View Post
    Curious if you find this ADS1256 chip occasionally resets when you didn't want it to. Among other things, the reset means it returns to its default of 30 kHz sampling rate, so that can be a problem if you were using a different rate. It can be reset by the hardware RESET/ pin and by the software reset command and by a special pattern on the SPI clock line, so that's three different ways. In my circuit with my code that is maybe one too many :-)
    I personally haven't had the issue, but I also haven't been collecting data for more than a few minutes at a time. My colleagues who have started to implement the ADS in their data systems said they had it reset. Their solution was to just eliminate using the gain.

    I would be curious if you could add a line of code that would detect the data doing something weird then would poll the register values to see if they changed. Then maybe put a system that changed the register values back to the proper gain.

    We have a grant to investigate using these tools more (i.e. we're hiring a student to do this for us) and we can see in the next few months if we can generate a solution ourselves.

    Thanks! Matt

  5. #5
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    I'm using the ADS1256 code from Matt Bilsky at https://github.com/mbilsky/TeensyADS1256 on a Teensy 4 and it is working well at the moment. In the past I used a different hardware setup, using T3.2 that sometimes had a reset issue with many-hour-long acquisition sessions. With the new setup I have not (yet) seen that. It may have just been some wiring problem with the old configuration, or something that happens only with long term acquisition.

    A question: I want to acquire two input channels with very nearly simultaneous sampling. This chip has 8 inputs, but only one ADC and multiplexing takes time, so instead of just switching between inputs, I want to use two separate ADS1256 boards. I don't see any hardware reason why not, but as it is, I think the Bilsky code is not setup to address more than one hardware chip. Has anyone tried to use two ADS1256 boards, and if so what kind of code did you use?

  6. #6
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    It appears that this library https://github.com/Flydroid/ADS12xx-Library may allow for more than one ADC instance, each with separate control pins, so I'll try that.

  7. #7
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    Just in case of interest, I did modify the Flydroid library to allow using two different ADS1256 devices on one Teensy. It "usually" works, not yet 100% confident it always does.

    Separate from the dual-device change, I discovered that I needed a significantly longer delay between dropping _CS low, and sending the read command in ads12xx::GetConversion(). I am using 50 microseconds delay now and SPI clock = 960kHz. So far OK after 3M readings (1.7 hours at 500 Hz). The Flydroid library used 10 usec delay, which after a while (maybe thousands of readings) may cause a strange mode where the output word is left-shifted 1 or 2 bytes. The TI data sheet for ADS1256 on p.6, figure 1 claims that there is no setup time, specifically that the minimum interval "t3" from CS low to first SCLK is 0 nsec, but that is not my observation.

    Click image for larger version. 

Name:	2019-09-14_ADS1256-setup.png 
Views:	35 
Size:	24.3 KB 
ID:	17605

    EDIT: Nope. Flaked out again after 3.8 million reads. Sigh.
    Last edited by JBeale; 09-15-2019 at 07:21 AM.

  8. #8
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    Here is my code (modified library + sample dual-ADC application) which has now run 8 hours sampling two ADC chips, each one at 500 Hz, without any obvious error: https://github.com/jbeale1/ADS1256_DUAL

    This 8-hour run (over 14 million reads) was using a regular Teensy 4 board and Teensyduino 1.47.
    The previous fault after 3.8 million reads was using a T4-Beta1 board and Teensyduino 1.46-beta9.

    They ran the same code, although with a fault that rare I'm still unsure of the root cause of the problem.
    You could also describe my breadboard wiring setup as "haphazard".

    Click image for larger version. 

Name:	20190915_DualADC.jpg 
Views:	43 
Size:	134.6 KB 
ID:	17607
    Last edited by JBeale; 09-15-2019 at 05:09 PM.

  9. #9
    Junior Member
    Join Date
    May 2018
    Posts
    3
    Hi All,

    I'm glad to see this thread is alive and well.

    For the past couple of months I've had a grad student, Tyler, at Lehigh working on making the ADS1256 library more robust (and an actual object-based library). He's nearly finished then we will be releasing it.

    I have also experimented in the past with running 2 ADS on the same SPI bus on a teensy 3.2 getting about 20KSPS from each board. We hope to include this in the new library.

    We are also running robustness tests (like your 8 hour tests).

    I hope to release the code in the next week or two along with a video discussing the progress.

    Then we are moving on to other ADS boards. This is all part of a grant I wrote to support the development of open source engineering tools. Lots of exciting things to come!

  10. #10
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354
    Quote Originally Posted by mbilsky View Post
    ...Then we are moving on to other ADS boards. This is all part of a grant I wrote to support the development of open source engineering tools. Lots of exciting things to come!
    That's a terrific project that can greatly benefit the open source community.

    I'd really love to see an open source hardware & software solution for an instrument grade DAC with voltage and 4-20ma output capability like the Analog Devices AD5755

  11. #11
    cool project, folowing this thread!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •