This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADS1299-4: Problem obtaining internal test signals

Part Number: ADS1299-4
Other Parts Discussed in Thread: ADS1299

Hi,

This is my first time posting on the forum, so please bear with me if I leave information out.

I have read some of the other posts on how to obtain the internal test signals and I am struggling to get the square signal. I am using the ADS1299-4 with an ESP32 as an MCU. I will post my code below and the output I keep getting at the bottom. I followed the start-up procedure as stated in the datasheet and able to read and write the registers correctly. At first, I am only trying to get the test signals from channel 1 and shorten the other three channels. I am using Arduino IDE and built a custom PCB. As a reference, I followed openBCI schematic and opensource code to build the PCB.

The problem I have is the output of the test signals. As you can see in the figure below, the output is quite weird and not square as it should be. I don't know if I am just not configuring the registers correctly or maybe my SPI clock is just incorrect. The figure below is what I am getting on the Serial Plotter and on the Serial Monitor I get negative answers around -6000.

Can somebody please help me by showing me what I am doing wrong and what I can do to fix it? It would be much appreciated!

Here is the Serial Plotter output I got.

  

Here are some snippets of my code with my full code afterward:

This is how I write to the registers:

Update channel data:

And here is the function of how I want to print the data:

Full-text file containing the code.

testSignals.txt
#include "EEG.h"
#include "EEG_Definitions.h"
#include <SoftwareSerial.h>
#include <SPI.h>

// start of simulation declarations
#define ISR_TRIGGER_GPIO 39
#define PULSE_OUTPUT_GPIO 32

// Scale factor for data
#define GAIN 1
#define scaleFactor 1000000*((4.5/(2^23)-1)/GAIN)
#define testScale 0.001*4.5/2.4

// Clock Signal
#define tCLK 0.0005     // 2 MHz Clock signal, 500 ns

int sampleFreq = 250; // to 500. A value of 128 will give 512 samples
int factor = 1000/sampleFreq/2;

// generate output pulses to emulate DRDY from ADS
// this should be placed in multiple places to emulate an unsynchronised DRDY
#define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, (millis() / factor) % 2);
//#define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, HIGH)
// end of simulation declarations

EEG board;

// Functions 
const int  WAKEUP  = 0x02; // Wake-up from standby mode
const byte STANDBY = 0b00000100; // Enter Standby mode
const byte RESET   = 0b00000110; // Reset the device
const byte START   = 0b00001000; // Start and restart (synchronize) conversions
const byte STOP    = 0b00001010; // Stop conversion
const byte RDATAC  = 0b00010000; // Enable Read Data Continuous mode (default mode at power-up)
const byte SDATAC  = 0b00010001; // Stop Read Data Continuous mode
const byte RDATA   = 0b00010010; // Read data by command; supports multiple read back
const byte TEST1   = 0b00000000;
const byte RREG    = 0x20;// (also = 00100000) is the first opcode that the address must be added to for RREG communication
const byte WREG    = 0x40;// 01000000 in binary (Datasheet, pg. 35)

double data;
const byte led = 2;
byte registerData[24];         // array is used to mirror register data
byte channelDataRaw[24];
long channelData[5];
long ads[5];
int sampleCounter = 0;

 /* //SPI pin numbers:
 SCK   18  // Serial Clock.
 MISO  19  // Master In Slave Out.  (DOUT)
 MOSI  23  // Master Out Slave In.  (DIN)
 SS    5  // Slave Select*/

 //ADS1299-X Control Pin Declarations
 const int STARTPIN = 14;
 const int DRDYPIN = 4;
 const int RESETPIN = 0;
 const int PWDNPIN = 3;

void IRAM_ATTR ADS_DRDY_Service()
 {
  if (bitRead(ESP32_DRDY, 0) == 0)
  {
    board.channelDataAvailable = true;
  }
}

int devicebyte = 0;

void setup() {
  Serial.begin(115200);
  Serial.flush();
  delay(2000);

    while(!Serial);
  
  //SPI Communication Setup with ADS1299-X
  // Chip Select Pin 
  pinMode(CS_ADS, OUTPUT);
  digitalWrite(CS_ADS, HIGH);

  //Start Pin -- Pull Start Pin High in order to access ADS1299-X data - DRDY toggles at 250Hz
  pinMode(ADS_Start, OUTPUT);
  digitalWrite(ADS_Start, HIGH);

  //DRDY Pin - Drops LOW if Data is ready 
  pinMode(ADS_DRDY, INPUT);

  // Set up SPI
  SPI.begin();
  SPI.setFrequency(4000000);    // Set 4 MHz for ADS
  SPI.setDataMode(SPI_MODE1);

  //Powering Up Sequence
//  Serial.println("Powering up Device...");
  DeviceStartUp();
  Serial.println("Device power up complete...");
  delayMicroseconds(2);

  // Get Device ID 
  devicebyte = getDeviceID();
//  Serial.print("Device ID is: ");
//  Serial.print(devicebyte, BIN);
  Serial.println();

  // Print default settings for registers
  board.printADSregisters(CS_ADS);

  //Writing Channel Configuration
//  Serial.println("Writing Channel Settings...");
  writeRegisterConfig();
//  Serial.println("Writing Channel Settings complete...");
  delayMicroseconds(2);

  // Set up interrupt service routine
  pinMode(ADS_DRDY, INPUT_PULLUP);
  pinMode(ISR_TRIGGER_GPIO,INPUT_PULLUP);
// simulation - set up the pin for output of pulses that emulate DRDY from ADS
  pinMode(PULSE_OUTPUT_GPIO, OUTPUT); 
    
  attachInterrupt(ADS_DRDY, ADS_DRDY_Service, FALLING);
  attachInterrupt(ISR_TRIGGER_GPIO, ADS_DRDY_Service, FALLING);

  digitalWrite(CS_ADS, LOW);
  SPI.transfer(_START); // KEEP ON-BOARD AND ON-DAISY IN SYNC
  digitalWrite(CS_ADS, HIGH);
  delay(30);
  
  digitalWrite(CS_ADS, LOW);
  SPI.transfer(_RDATAC); // read data continuous
  digitalWrite(CS_ADS, HIGH);
  delay(1);

  testSignals();
}

// --------------------------- LOOP ---------------------------------------------------
void loop() {

  
  
//  Serial.println((0.001*4.5/2.4),DEC);
//  while(ADS_DRDY == LOW){
  GENERATE_DRDY
  updateChannelData();
//  printChannelData();
  GENERATE_DRDY
//    }

}

// ------------------------- Device Start up Procedure -------------------------------------

void DeviceStartUp(){
  //PWDN and RESET PIN set HIGH
  pinMode(ADS_RST, OUTPUT);
  digitalWrite(ADS_RST, HIGH);
  pinMode(PWDN, OUTPUT);
  digitalWrite(PWDN, HIGH);

  //Delay for 1 second, for power up sequence 
  delay(1000);

  //RESET PULSE
  digitalWrite(ADS_RST,LOW);
  delayMicroseconds(1); //2 tclk cycles
  digitalWrite(ADS_RST,HIGH);

  //Wait to Settle 16-18 tclk
  delayMicroseconds(18*tCLK);

  //Send SDATAC
  SPI.beginTransaction(SPISettings(2000000,MSBFIRST,SPI_MODE1));
  digitalWrite(CS_ADS,LOW); //Pull CS LOW to start communication
  
  SPI.transfer(SDATAC);
  delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
  
  digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
  SPI.endTransaction();
  }

  // ----------------------- Retrieving Device ID function ---------------------------------------------------
int getDeviceID(){
  //Delay 
  delay(100);
  // Begin SPI 
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE1));
  digitalWrite(CS_ADS,LOW); //Pull CS low to communicate
  SPI.transfer(RREG); // Read Register 00100000
  delayMicroseconds(2);
  
  SPI.transfer(ID_REG); //0x00 ID register 00000000
  delayMicroseconds(2);
  byte data = SPI.transfer(ADS_ID); // Binary value for device ID, 4CH = 0b???11100
  delayMicroseconds(2);
  
  digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
  SPI.endTransaction();
  return data;
  }

  // ------------------------- Write Registers Configuration -------------------------------------

void writeRegisterConfig() {
 // ADS1299-4 Datasheet pg 62:
  SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE1));
  digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
 
//  SPI.transfer(SDATAC);
//  delayMicroseconds(4*tCLK); //delay 4 tCLK cycles

  //Using internal reference i.e BIASREF siganl (AVDD + AVSS)/2
  WREGConf(CONFIG3, 0xE0); 
  delayMicroseconds(2);
  
 // Set Device for f_mod/4096 (250 SPS)
  WREGConf(CONFIG1, 0x96);    WREGConf(CONFIG2, 0xC0);

  // Set Channels to Input Short
  WREGConf(CH1SET, 0x01);  WREGConf(CH2SET, 0x01);
  WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);
  delayMicroseconds(2);
  
  board.RREGS(ID_REG, CONFIG4, CS_ADS);
  delayMicroseconds(2);
  digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
  }

// --------------------------- Write channel configuration procedure--------------------------
 
void WREGConf(byte _address, byte _regValue){
   
  byte opcodeW = _address + 0x40;     // Write to one register with address (WREG = 0x40),  WREG expects 010rrrrr where rrrrr = _address
  SPI.transfer(opcodeW);              // Send WREG command and address
  SPI.transfer(0x00);                 // Send dummy byte to ADS. Send number of registers to read -1
  SPI.transfer(_regValue);            // Write register value

  registerData[_address] = _regValue; // Update mirror array
} 

// -------------------------------- Setup Test Signals -----------------------------------------

void testSignals(){
//  SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE1));
  digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
 
  SPI.transfer(SDATAC);
  delayMicroseconds(4*tCLK); //delay 4 tCLK cycles

  // Set Device for f_mod/4096 (250 SPS)
  WREGConf(CONFIG2, 0xD0);

  // Set Channels to Test Signals
  WREGConf(CH1SET, 0x15);  WREGConf(CH2SET, 0x01);
  WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);

  // Lead Off Dectection
  WREGConf(LOFF, 0x00);

  // Bias
  WREGConf(BIAS_SENSP, 0x01);   // CH2 set as BIAS 
  WREGConf(BIAS_SENSN, 0x00);   // Disable all negative input

  //CONFIG3
  WREGConf(CONFIG3, 0xEC);      // Internal reference buffer & BIASREF signal generated internally
  

  SPI.transfer(RDATAC);
  delayMicroseconds(4*tCLK); //delay 4 tCLK cycles

  digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
//  SPI.endTransaction();
 }

  // ------------------------------- Update Channel Data -------------------------------------

void updateChannelData(){
  byte inByte;
  int byteCounter = 0; 
  
  int numChan = 4;  //assume 4 channel.  If needed, it automatically changes to 16 automatically in a later block.
  int stat_1, stat_2;

  long channelDat;
 
  digitalWrite(CS_ADS, LOW);        //  open SPI
  
  // READ CHANNEL DATA FROM FIRST ADS
  for(int i=0; i<3; i++){     //  read 3 byte status register from ADS 1 (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
    inByte = SPI.transfer(0x00);
    stat_1 = (stat_1<<8) | inByte;        
  }
  
  for(int i = 0; i < numChan; i++){
    for(int j=0; j < 3; j++){   //  read 24 bits of channel data from 1st ADS in 8 3 byte chunks
      inByte = SPI.transfer(0x00);
//      channelDataRaw[byteCounter] = inByte;             // Raw data array
//      byteCounter++;      
      channelData[i] = (channelData[i]<<8) | inByte;    // Int data array
    }

  }  
  digitalWrite(CS_ADS, HIGH);       //  close SPI
 
  //reformat the numbers
  for(int i = 0; i < numChan; i++){     // convert 3 byte 2's compliment to 4 byte 2's compliment 
    if(bitRead(channelData[i],23) == 1){  
      channelData[i] |= 0xFF000000;
      ads[i] = channelData[i];
    }else{
      channelData[i] &= 0x00FFFFFF;
      ads[i] = channelData[i];
    }    
  }

  printChannelData();
}


 // ------------------------------- Print Channel Data -------------------------------------
  
void printChannelData(){
//  Serial.println(" ");

 for(int i = 0; i < 4; i++){  

  Serial.println(channelData[i],DEC);
  
//  ads[i] =  channelData[i];
//  Serial.println(ads[i], DEC);

//  Serial.println(ads[i]*(4.5/(2^23 -1)),DEC);

//  if(ads[i] != 0){
//    Serial.println(ads[i],DEC);
//    Serial.println(ads[0]*(4.5/(2^23 -1)), DEC);
//    }

 }  

}

  • Hi Elke,

    Welcome to the E2E forum !!!

    I do not think you are setting the CONFIG2 register correctly for the internal test signal. To enable the internal test signal, you need to set CONFIG2.Bit1 to '1' and CONFIG2.Bit0 to '1' for a square wave internal test signal at 1 Hz. Depending on your hardware design, you need to ensure the reference voltage (VREF = VREFP - VREFN) is set up correctly.

    Thanks

    -TC

  • Thanks for the quick reply!

    I changed both CONFIG2.Bit1 and CONFIG2.Bit0 to '1', thus writing CONFIG2 as 0xD3. I also checked my reference voltage now and when measuring with reference to AVSS, I get the following:

    VREFP = 4.51 V

    VREFN = 0 V

    VCAP1 = 1.2 V

    VCAP2 = 2.52 V

    VCAP3 = 6.9 V

    VCAP4 = 2.25 V

    And with reference to ground:

    AVDD = 2.51 V

    AVSS = -2.51 V

    I am still getting the weird output I got earlier. So I don't know what seems to be the problem. I will attach the schematic I am using if you can help me check to see if I am missing something? I just want to add as I forgot to add it in the original post, I am using a bipolar power supply for the ADS.

  • Hi Elke,

    Thanks for the information.

    The schematic and all the device voltages look fine. For debugging, you may want to look at the SPI interface with a logic analyzer or oscilloscope to see if you are getting the proper output data bits. Please also verify you are getting the correct preamble (0xC0) for the 24 status bits in the stat_1 output. Another option is to provide known signals (DC or sinewave) in the "normal electrode" mode and check if the results match your input.

    Thanks

    -TC

  • I played around with my code to print out the status register and I got FFC00000. Can this be seen as correct or not?

  • Hi Elke,

    The last 3 bytes of the status bits seem correct if you are padding it to 4-byte data. I am not familiar with your code that prints out the decimal value for the ADC output. Please verify that the conversion for the ADC binary twos complement data format is correct.

    Thanks

    -TC

  • Thanks, will do!

    I am busy looking at the SPI communication to see if there is something incorrect. Do you have any advice in where I should look? What are the most occurring problems you have come across when dealing with SPI? I could maybe help me narrow down my search. I know the ADS works with SPI mode 1 according to the datasheet and have my SPI frequency at 2 MHz. 

    If it helps the MCU I am using is the ESP32 Wroom module. 

    Really appreciate all the help!

  • Hi Elke,

    I did not go through your code completely, so I do not know exactly where the issue is. One of the easiest debug methodology is to apply a DC voltage to the input and look at the SPI digital output on the scope. Compare the output from your code to the SPI data observed on the oscilloscope. Start by applying a DC voltage VIN ≥ +VREF or VIN  ≤ -VREF and verify you can get the output code of 0x7FFFFFF and 0x800000, respectively. You can switch to the DC internal test signal (CAL_FREQ[1:0] = 0b11) for a fix DC test signal.

    If you are getting the correct output for the DC test, then the next debug step will be looking at how your uC ISR is processing the DRDY signal.

    I hope this helps you to find the issue you are seeing.

    Thanks

    -TC

  • Hi, 

    Sorry for only replying now. I just want to check something with you. When I am checking the status register, My code does nothing for a long time, then plots a lot of zeros, then followed by FFC00000 with zeros in between (as seen in the attached picture.)

    I will also attach the code giving this output. I just want to know if this is normal to the zeros in between the status register or should the status register output constantly be C0?

    newtestSignals.txt
    #include "EEG.h"
    #include "EEG_Definitions.h"
    #include <SoftwareSerial.h>
    #include <SPI.h>
    
    // start of simulation declarations
    #define ISR_TRIGGER_GPIO 39
    #define PULSE_OUTPUT_GPIO 32
    
    // Scale factor for data
    #define GAIN 1
    #define scaleFactor 1000000*((4.5/(2^23)-1)/GAIN)
    #define testScale 0.001*4.5/2.4
    
    // Clock Signal
    #define tCLK 0.0005     // 2 MHz Clock signal, 500 ns
    
    int sampleFreq = 250; // to 500. A value of 128 will give 512 samples
    int factor = 1000/sampleFreq/2;
    
    // generate output pulses to emulate DRDY from ADS
    // this should be placed in multiple places to emulate an unsynchronised DRDY
    #define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, (millis() / factor) % 2);
    //#define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, FALLING)
    // end of simulation declarations
    
    EEG board;
    
    // Functions 
    const int  WAKEUP  = 0x02; // Wake-up from standby mode
    const byte STANDBY = 0b00000100; // Enter Standby mode
    const byte RESET   = 0b00000110; // Reset the device
    const byte START   = 0b00001000; // Start and restart (synchronize) conversions
    const byte STOP    = 0b00001010; // Stop conversion
    const byte RDATAC  = 0b00010000; // Enable Read Data Continuous mode (default mode at power-up)
    const byte SDATAC  = 0b00010001; // Stop Read Data Continuous mode
    const byte RDATA   = 0b00010010; // Read data by command; supports multiple read back
    const byte TEST1   = 0b00000000;
    const byte RREG    = 0x20;// (also = 00100000) is the first opcode that the address must be added to for RREG communication
    const byte WREG    = 0x40;// 01000000 in binary (Datasheet, pg. 35)
    
    double data;
    const byte led = 2;
    byte registerData[24];         // array is used to mirror register data
    byte channelDataRaw[24];
    long channelData[4];
    long ads[4];
    int sampleCounter = 0;
    
    bool channelDataAvailable;
    
     /* //SPI pin numbers:
     SCK   18  // Serial Clock.
     MISO  19  // Master In Slave Out.  (DOUT)
     MOSI  23  // Master Out Slave In.  (DIN)
     SS    5  // Slave Select*/
    
     //ADS1299-X Control Pin Declarations
     const int STARTPIN = 14;
     const int DRDYPIN = 4;
     const int RESETPIN = 0;
     const int PWDNPIN = 3;
    
    void IRAM_ATTR ADS_DRDY_Service()
     {
    //  if (bitRead(ESP32_DRDY, 0) == 0)
      if (bitRead(ADS_DRDY, 0) == 0)
      {
    //    board.channelDataAvailable = true;
          channelDataAvailable = true;
      }
    }
    
    int devicebyte = 0;
    
    void setup() {
      Serial.begin(115200);
      Serial.flush();
      delay(2000);
    
        while(!Serial);
      
      //SPI Communication Setup with ADS1299-X
      // Chip Select Pin 
      pinMode(CS_ADS, OUTPUT);
      digitalWrite(CS_ADS, HIGH);
    
      //Start Pin -- Pull Start Pin High in order to access ADS1299-X data - DRDY toggles at 250Hz
      pinMode(ADS_Start, OUTPUT);
      digitalWrite(ADS_Start, HIGH);
    
      //DRDY Pin - Drops LOW if Data is ready 
      pinMode(ADS_DRDY, INPUT);
    
      // Set up SPI
      SPI.begin();
      SPI.setFrequency(2000000);    // Set 4 MHz for ADS
      SPI.setDataMode(SPI_MODE1);   // Datasheet pg. 12: CPOL = 0 and CPHA = 1 (SPI MODE 1)
    
      //Powering Up Sequence
    //  Serial.println("Powering up Device...");
      DeviceStartUp();
      Serial.println("Device power up complete...");
      delayMicroseconds(2);
    
      // Get Device ID 
      devicebyte = getDeviceID();
    //  Serial.print("Device ID is: ");
    //  Serial.print(devicebyte, BIN);
      Serial.println();
    
      Serial.println();
      // Print default settings for registers
      board.printADSregisters(CS_ADS);
    
      //Writing Channel Configuration
    //  Serial.println("Writing Channel Settings...");
      writeRegisterConfig();
    //  Serial.println("Writing Channel Settings complete...");
      delayMicroseconds(2);
    
      // Set up interrupt service routine
      pinMode(ADS_DRDY, INPUT_PULLUP);
      pinMode(ISR_TRIGGER_GPIO,INPUT_PULLUP);
    // simulation - set up the pin for output of pulses that emulate DRDY from ADS
      pinMode(PULSE_OUTPUT_GPIO, OUTPUT); 
        
      attachInterrupt(ADS_DRDY, ADS_DRDY_Service, FALLING);
      attachInterrupt(ISR_TRIGGER_GPIO, ADS_DRDY_Service, FALLING);
    
      digitalWrite(CS_ADS, LOW);
      SPI.transfer(_START); // KEEP ON-BOARD AND ON-DAISY IN SYNC
      digitalWrite(CS_ADS, HIGH);
      delay(30);
      
      digitalWrite(CS_ADS, LOW);
      SPI.transfer(_RDATAC); // read data continuous
      digitalWrite(CS_ADS, HIGH);
      delay(1);
    
      testSignals();
      Serial.println("Register Print After TestSignals:");
      board.printADSregisters(CS_ADS);
    }
    
    // --------------------------- LOOP ---------------------------------------------------
    void loop() {
    
      GENERATE_DRDY;
    
      updateChannelData();
      
      GENERATE_DRDY
    
    }
    
    // ------------------------- Device Start up Procedure -------------------------------------
    
    void DeviceStartUp(){
      //PWDN and RESET PIN set HIGH
      pinMode(ADS_RST, OUTPUT);
      digitalWrite(ADS_RST, HIGH);
      pinMode(PWDN, OUTPUT);
      digitalWrite(PWDN, HIGH);
    
      //Delay for 1 second, for power up sequence 
      delay(1000);
    
      //RESET PULSE
      digitalWrite(ADS_RST,LOW);
      delayMicroseconds(1); //2 tclk cycles
      digitalWrite(ADS_RST,HIGH);
    
      //Wait to Settle 16-18 tclk
      delayMicroseconds(18*tCLK);
    
      //Send SDATAC
      SPI.beginTransaction(SPISettings(2000000,MSBFIRST,SPI_MODE1));
      digitalWrite(CS_ADS,LOW); //Pull CS LOW to start communication
      
      SPI.transfer(SDATAC);
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
      
      digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
      SPI.endTransaction();
      }
    
      // ----------------------- Retrieving Device ID function ---------------------------------------------------
    int getDeviceID(){
      //Delay 
      delay(100);
      // Begin SPI 
      SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE1));
      digitalWrite(CS_ADS,LOW); //Pull CS low to communicate
      SPI.transfer(RREG); // Read Register 00100000
      delayMicroseconds(2);
      
      SPI.transfer(ID_REG); //0x00 ID register 00000000
      delayMicroseconds(2);
      byte data = SPI.transfer(ADS_ID); // Binary value for device ID, 4CH = 0b???11100
      delayMicroseconds(2);
      
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      SPI.endTransaction();
      return data;
      }
    
      // ------------------------- Write Registers Configuration -------------------------------------
    
    void writeRegisterConfig() {
     // ADS1299-4 Datasheet pg 62:
      SPI.beginTransaction(SPISettings(2000000,MSBFIRST,SPI_MODE1));
      digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
     
      SPI.transfer(SDATAC);
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      //Using internal reference i.e BIASREF siganl (AVDD + AVSS)/2
      WREGConf(CONFIG3, 0xE0); 
      delayMicroseconds(2);
      
     // Set Device for f_mod/4096 (250 SPS)
      WREGConf(CONFIG1, 0x96);    WREGConf(CONFIG2, 0xC0);
      delayMicroseconds(2);
      
      // Set Channels to Input Short
      WREGConf(CH1SET, 0x01);  WREGConf(CH2SET, 0x01);
      WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);
      delayMicroseconds(2);
    
      WREGConf(MISC1, 0x00);
      delayMicroseconds(2);
      
      board.RREGS(ID_REG, CONFIG4, CS_ADS);
      delayMicroseconds(2);
      digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
      }
    
    // --------------------------- Write channel configuration procedure--------------------------
     
    void WREGConf(byte _address, byte _regValue){
       
      byte opcodeW = _address + 0x40;     // Write to one register with address (WREG = 0x40),  WREG expects 010rrrrr where rrrrr = _address
      SPI.transfer(opcodeW);              // Send WREG command and address
      SPI.transfer(0x00);                 // Send dummy byte to ADS. Send number of registers to read -1
      SPI.transfer(_regValue);            // Write register value
    
      registerData[_address] = _regValue; // Update mirror array
    } 
    
    // -------------------------------- Setup Test Signals -----------------------------------------
    
    void testSignals(){
    //  SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE1));
      digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
     
      SPI.transfer(SDATAC);
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
      
      // Set Device for f_mod/4096 (250 SPS)
      WREGConf(CONFIG2, 0xD3);              // For Internal square signals: Set CONFIG2.BIT1 and CONFIG2.BIT0 to '1' (TI Forum reply)
    //  WREGConf(CONFIG2, 0xD0);
    
      // CONFIG3
      WREGConf(CONFIG3, 0xEC);      // Internal reference buffer & BIASREF signal generated internally
      
      // Set Channels to Test Signals: Changed 0D to 05
      WREGConf(CH1SET, 0x0D);  WREGConf(CH2SET, 0x01);
      WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);
    
      // Lead Off Dectection
      WREGConf(LOFF, 0x00);
    
    //  // Bias
      WREGConf(BIAS_SENSP, 0x01);   // CH1 set as BIAS 
      WREGConf(BIAS_SENSN, 0x00);   // Disable all negative input
    
      SPI.transfer(RDATAC);
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
     }
    
      // ------------------------------- Update Channel Data -------------------------------------
    
    void updateChannelData(){
      byte inByte;
      int byteCounter = 0; 
      
      int numChan = 4;  //assume 4 channel.  If needed, it automatically changes to 16 automatically in a later block.
      long stat_1[1];
    
      long channelDat;
    
     if(digitalRead(DRDYPIN) == LOW){
      digitalWrite(CS_ADS, LOW);        //  open SPI
      delayMicroseconds(2);
      
      // READ CHANNEL DATA FROM FIRST ADS
      for(int i=0; i<3; i++){     //  read 3 byte status register from ADS 1 (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
        inByte = SPI.transfer(0x00);
        stat_1[i] = (stat_1[i] << 8) | inByte;
      } 
      
      for(int i = 0; i < 4; i++){
        for(int j=0; j < 3; j++){   //  read 24 bits of channel data from 1st ADS in 8 3 byte chunks
          inByte = SPI.transfer(0x00);
    //      channelDataRaw[byteCounter] = inByte;             // Raw data array
    //      byteCounter++;      
          channelData[i] = (channelData[i]<<8) | inByte;    // Int data array
        }
      }  
      
      digitalWrite(CS_ADS, HIGH);       //  close SPI
      delayMicroseconds(2);
     
      //reformat the numbers
      for(int i = 0; i < 4; i++){     // convert 3 byte 2's compliment to 4 byte 2's compliment 
        if(bitRead(channelData[i],23) == 1){  
          channelData[i] |= 0xFF000000;
          ads[i] = channelData[i];
        }else{
          channelData[i] &= 0x00FFFFFF;
          ads[i] = channelData[i];
        }    
      }
    
    for(int i = 0; i < 1; i++){
        if(bitRead(stat_1[i],23) == 1){  
        stat_1[i] |= 0xFF000000;
          } else{
        stat_1[i] &= 0x00FFFFFF;
          } 
       Serial.println(stat_1[i], HEX);
        }
     
    //    printChannelData();
      }
    }
    
     // ------------------------------- Print Channel Data -------------------------------------
      
    void printChannelData(){
    
     for(int i = 0; i < 4; i++){  
    
      Serial.println(channelData[i],HEX);
    
     }  
    }

  • Hi Elke,

    The ADC output will always start with the 24 status bits, followed by the 24-bit data for each channel. The 24 status bit will always begin with 0b1100. The output of the ADC should not be all zero even if you let the channel input floating.  

    Thanks

    -TC

  • Hi TCT,

    Sorry I am only replying now. I am able to output the status register value 0xC0 continuously now (with no zeros in-between as in my previous post). However, my registers are not reading and writing correctly. How can I get the correct status register output with my registers being written incorrectly?

    I will post my code below for you to check. The EEG. functions are just a header file where I pull CS high and low to send the command bytes. 

    Thanks.

    testSignal.txt
    #include "EEG1.h"
    #include "EEG1_Definitions.h"
    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <FunctionalInterrupt.h>
    
    // start of simulation declarations
    #define ISR_TRIGGER_GPIO 39
    #define PULSE_OUTPUT_GPIO 32
    
    // Scale factor for data
    #define GAIN 1
    #define scaleFactor 1000000*((4.5/(2^23)-1)/GAIN)
    #define testScale 0.001*4.5/2.4
    
    // Clock Signal
    #define tCLK 0.0005     // 2 MHz Clock signal, 500 ns
    
    int sampleFreq = 250; // to 500. A value of 128 will give 512 samples
    int factor = 1000/sampleFreq/2;
    
    // generate output pulses to emulate DRDY from ADS
    // this should be placed in multiple places to emulate an unsynchronised DRDY
    #define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, (millis() / factor) % 2);
    //#define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, FALLING)
    // end of simulation declarations
    
    EEG1 EEG;
    
    // Functions 
    const int  WAKEUP  = 0x02; // Wake-up from standby mode
    const byte STANDBY = 0b00000100; // Enter Standby mode
    const byte RESET   = 0b00000110; // Reset the device
    const byte START   = 0b00001000; // Start and restart (synchronize) conversions
    const byte STOP    = 0b00001010; // Stop conversion
    const byte RDATAC  = 0b00010000; // Enable Read Data Continuous mode (default mode at power-up)
    const byte SDATAC  = 0b00010001; // Stop Read Data Continuous mode
    const byte RDATA   = 0b00010010; // Read data by command; supports multiple read back
    const byte TEST1   = 0b00000000;
    const byte RREG    = 0x20;// (also = 00100000) is the first opcode that the address must be added to for RREG communication
    const byte WREG    = 0x40;// 01000000 in binary (Datasheet, pg. 35)
    
    double data;
    const byte led = 2;
    byte registerData[24];         // array is used to mirror register data
    byte channelDataRaw[24];
    long channelData[4];
    long ads[4];
    int sampleCounter = 0;
    //byte RREG(byte,int);            // read one ADS register
    
    bool channelDataAvailable;
    
     /* //SPI pin numbers:
     SCK   18  // Serial Clock.
     MISO  19  // Master In Slave Out.  (DOUT)
     MOSI  23  // Master Out Slave In.  (DIN)
     SS    5  // Slave Select*/
    
     //ADS1299-X Control Pin Declarations
     const int STARTPIN = 12;
     const int DRDYPIN = 4;
     const int RESETPIN = 0;
     const int PWDNPIN = 3;
    
    void IRAM_ATTR ADS_DRDY_Service()
     {
    //  if (bitRead(ESP32_DRDY, 0) == 0)
      if (bitRead(ADS_DRDY, 0) == 0)
      {
    //    board.channelDataAvailable = true;
          channelDataAvailable = true;
      }
    }
    
    int devicebyte = 0;
    
    void setup() {
      Serial.begin(115200);
      Serial.flush();
      delay(1000);
    
        while(!Serial);
    
      //Start Pin -- Pull Start Pin High in order to access ADS1299-X data - DRDY toggles at 250Hz
      EEG.START();
      delayMicroseconds(10);
    
      //SPI Communication Setup with ADS1299-X
      // Chip Select Pin 
      pinMode(CS_ADS, OUTPUT);
      digitalWrite(CS_ADS, HIGH);
    
      //DRDY Pin - Drops LOW if Data is ready 
      pinMode(ADS_DRDY, INPUT);
      digitalWrite(ADS_DRDY, HIGH);
      
      // Set up SPI
      SPI.begin();
      SPI.setFrequency(2000000);    // Set 4 MHz for ADS
      SPI.setDataMode(SPI_MODE1);   // Datasheet pg. 12: CPOL = 0 and CPHA = 1 (SPI MODE 1)
    
      //Powering Up Sequence
      DeviceStartUp();
      Serial.println("Device power up complete...");
      delayMicroseconds(2);
    
      // Get Device ID 
      devicebyte = getDeviceID();
      Serial.println();
    
      //Writing Channel Configuration
      writeRegisterConfig();
      delayMicroseconds(2);
    
       Serial.println();
      
      // Print default settings for registers
      EEG.printADSregisters(CS_ADS); 
    
      // Set up interrupt service routine
      pinMode(ADS_DRDY, INPUT_PULLUP);
      pinMode(ISR_TRIGGER_GPIO,INPUT_PULLUP);
    // simulation - set up the pin for output of pulses that emulate DRDY from ADS
      pinMode(PULSE_OUTPUT_GPIO, OUTPUT); 
    
      EEG.START();
      delay(30);
      EEG.RDATAC();
      delay(1);
    
      testSignals();
      Serial.println("Register Print After TestSignals:");
      EEG.printADSregisters(CS_ADS);
    
    }
    
    // --------------------------- LOOP ---------------------------------------------------
    void loop() {
      
      updateChannelData();
    
    }
    
    // ------------------------- Device Start up Procedure -------------------------------------
    
    void DeviceStartUp(){
      
      //PWDN and RESET PIN set HIGH
      
      // Toggle RESET Pin
      EEG.RESET();
      
      pinMode(PWDN, OUTPUT);
      digitalWrite(PWDN, HIGH);
    
      //Delay for 1 second, for power up sequence 
      delay(1000);
    
      //RESET PULSE
      EEG.RESET();
    
      //Wait to Settle 16-18 tclk
      delayMicroseconds(18*tCLK);
    
      //Send SDATAC
      EEG.SDATAC();
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      }
    
      // ----------------------- Retrieving Device ID function ---------------------------------------------------
    int getDeviceID(){
      
      //Delay 
      delay(100);
     
      // Begin SPI 
      digitalWrite(CS_ADS,LOW); //Pull CS low to communicate
      SPI.transfer(RREG); // Read Register 00100000
      delayMicroseconds(2);
      
      SPI.transfer(ID_REG); //0x00 ID register 00000000
      delayMicroseconds(2);
      byte data = SPI.transfer(ADS_ID); // Binary value for device ID, 4CH = 0b???11100
      delayMicroseconds(2);
      
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      return data;
      }
    
      // ------------------------- Write Registers Configuration -------------------------------------
    
    void writeRegisterConfig() {
     // ADS1299-4 Datasheet pg 62:
     
     digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
     
      SPI.transfer(SDATAC);
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      //Using internal reference i.e BIASREF siganl (AVDD + AVSS)/2
      WREGConf(CONFIG3, 0xE0); 
      delayMicroseconds(2);
      
     // Set Device for f_mod/4096 (250 SPS)
      WREGConf(CONFIG1, 0x96);    WREGConf(CONFIG2, 0xC0);
      delayMicroseconds(2);
      
      // Set Channels to Input Short
      WREGConf(CH1SET, 0x01);  WREGConf(CH2SET, 0x01);
      WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);
      delayMicroseconds(2);
    
      WREGConf(MISC1, 0x00);
      delayMicroseconds(2);
      
      EEG.RREGS(ID_REG, CONFIG4, CS_ADS);
      delayMicroseconds(2);
      digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
      }
    
    // --------------------------- Write channel configuration procedure--------------------------
     
    void WREGConf(byte _address, byte _regValue){
       
      byte opcodeW = _address + 0x40;     // Write to one register with address (WREG = 0x40),  WREG expects 010rrrrr where rrrrr = _address
      SPI.transfer(opcodeW);              // Send WREG command and address
      SPI.transfer(0x00);                 // Send dummy byte to ADS. Send number of registers to read -1
      SPI.transfer(_regValue);            // Write register value
    
      registerData[_address] = _regValue; // Update mirror array
    } 
    
    // -------------------------------- Setup Test Signals -----------------------------------------
    
    void testSignals(){
    
      EEG.SDATAC();
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
      
      // Set Device for f_mod/4096 (250 SPS)
      WREGConf(CONFIG2, 0xD3);              // For Internal square signals: Set CONFIG2.BIT1 and CONFIG2.BIT0 to '1' (TI Forum reply)
    //  WREGConf(CONFIG2, 0xD0);
    
      // CONFIG3
      WREGConf(CONFIG3, 0xEC);      // Internal reference buffer & BIASREF signal generated internally
      
      // Set Channels to Test Signals: Changed 0D to 05
      WREGConf(CH1SET, 0x0D);  WREGConf(CH2SET, 0x0D);
      WREGConf(CH3SET, 0x0D);  WREGConf(CH4SET, 0x0D);
    
      // Lead Off Dectection
      WREGConf(LOFF, 0x00);
    
      // Bias
      WREGConf(BIAS_SENSP, 0x02);   // CH1 set as BIAS 
      WREGConf(BIAS_SENSN, 0x00);   // Disable all negative input
    
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      EEG.RDATAC();
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
     
     }
    
      // ------------------------------- Update Channel Data -------------------------------------
    
    void updateChannelData(){
      byte inByte;
      int byteCounter = 0; 
      
      int numChan = 4;  //assume 4 channel.  If needed, it automatically changes to 16 automatically in a later block.
      long stat_1[1];
    
      long channelDat;
    
     if(digitalRead(DRDYPIN) == LOW){
      digitalWrite(CS_ADS, LOW);        //  open SPI
      delayMicroseconds(2);
      
      // READ CHANNEL DATA FROM FIRST ADS
      for(int i=0; i<3; i++){     //  read 3 byte status register from ADS 1 (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
        inByte = SPI.transfer(0x00);
        stat_1[i] = (stat_1[i] << 8) | inByte;
      } 
      
    //  for(int i = 0; i < 4; i++){
    //    for(int j=0; j < 3; j++){   //  read 24 bits of channel data from 1st ADS in 8 3 byte chunks
    //      inByte = SPI.transfer(0x00);
    //      channelDataRaw[byteCounter] = inByte;             // Raw data array
    //	    byteCounter++;      
    //      channelData[i] = (channelData[i]<<8) | inByte;    // Int data array
    //    }
    //  }  
      
      digitalWrite(CS_ADS, HIGH);       //  close SPI
      delayMicroseconds(2);
     
    //  //reformat the numbers
    //  for(int i = 0; i < 4; i++){     // convert 3 byte 2's compliment to 4 byte 2's compliment 
    //    if(bitRead(channelData[i],23) == 1){  
    //      channelData[i] |= 0xFF000000;
    //      ads[i] = channelData[i];
    //    }else{
    //      channelData[i] &= 0x00FFFFFF;
    //      ads[i] = channelData[i];
    //    }    
    //  }
    
    for(int i = 0; i < 24; i++){
        if(bitRead(stat_1[i],23) == 1){  
        stat_1[i] |= 0xFF000000;
          } else{
        stat_1[i] &= 0x00FFFFFF;
          } 
       Serial.println(stat_1[i], HEX);
        }
     
    //    printChannelData();
      }
    }
    
     // ------------------------------- Print Channel Data -------------------------------------
      
    void printChannelData(){
    
     for(int i = 0; i < 4; i++){  
    
      Serial.println(channelData[i],HEX);
      
     }  
    }

  • Hi Elke,

    Thanks for update. 

    Please refer to the E2E FAQ below for debugging a digital issue.

    Thanks

    -TC

  • Hi,

    Thank you for referring to this post, but I have read that post in the early stages when I was just trying to read and write to my registers. I am just really confused about how I can get the correct output for the status register if all my other registers are not correct? Surely, I am not supposed to get the correct status register output if my registers are not correct? 

    I will look at the debugging steps again in the post you referred me to and see if I can get my registers to write correctly again while giving me the correct status register output.

    Thanks!

  • Hi,

    I got my registers reading and writing correctly again, but now I sit with the same problem again where I only get zero as an output. I used an oscilloscope to see what happens with my DRDY pin. During the startup process my DRDY pin pulses as it should, but once it reaches the updateChannelData() function, my DRDY pin goes HIGH and stays there. 

    Have you come across such a problem in the past? 

    Thanks!

  • Hi Elke,

    It may be useful to use the oscilloscope to check the SPI interface pins (DRDYB, CSB, SCLK, DIN, DOUT) during the updateChannelData() function. By default, the device should be in the RDATAC mode, which the DRDYB will be toggling even though the data is not retrieved. Please refer to Figure 41 for the Data Ready (DRDYB) behavior. The initial flow at power-up (Figure 67) should be a good starting point to verify the device's functionality.

    Please make sure you are sending the correct command (RDATAC) after writing and reading the device registers to enable conversion data output on each DRDYB.

    Thanks

    -TC

  • Hi,

    So if I am understanding Figure 41 correctly, DRDY should be HIGH the whole time while in the RDATAC mode? I did follow the power-up process as described in Figure 67 and I got my registers to plot correctly again, but now I can't get the correct status register output back again.

    In the start-up procedure described in Figure 67, it says "Issue Reset Pulse" will that be possible by just sending the RESET command to the chip, or should the Reset pin be pulsed by using the PinMode function (PinMode( RESET, LOW); PinMode(RESET, HIGH) )?

  • Hi Elke,

    Thanks for the post.

    In RDATAC mode, the DRDYB will be pulled high when the START pin is pulled high (See Figure 40). The next DRDYB falling edge indicates the data are ready for retrieval. Figure 41 shows the DRDYB pin being pulled high at the first falling edge of the SCLK. This operation is regardless of whether the data are retrieved from the device or command is sent through the DIN pin.

    You can either send the RESET command or toggle the RESET pin to reset the device.

    -TC 

  • Hi TC,

    I looked at Figure 41 and understand how DRDYB should operate. When I start my system, DRDYB is already HIGH before sending the START command and after the registers are written START goes low. I followed the power-up sequence in Figure 67, but it seems START doesn't respond to the START command I send it. If I understand it correctly START is supposed to go HIGH if I send it the START command right?

    Thanks!

  • Hi Elke,

    As indicated in the datasheet, you will need to hold the START pin low when using the START command to control the conversion. If you are in RDATAC mode, the DRDYB will indicate whether the conversion is initiated. 

    Thanks

    -TC

  • Hi,

    I am able to get DRDYB to pulse at 250 Hz, START and RESET are high once updating data, but I am still getting 0 for my status register output. I will attach the oscilloscope output of DRDYB and the code. Is there anything that can you wrong otherwise? Should I pull RESET back low after the start-up procedure when I want to obtain the status register output?

    Sorry about the rotation of the photo. I can't seem to upload it in the correct rotation.

    1222.New Text Document.txt
    #include "EEG1.h"
    #include "EEG1_Definitions.h"
    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <FunctionalInterrupt.h>
    
    // start of simulation declarations
    #define ISR_TRIGGER_GPIO 39
    #define PULSE_OUTPUT_GPIO 32
    
    // Scale factor for data
    #define GAIN 1
    #define scaleFactor 1000000*((4.5/(2^23)-1)/GAIN)
    #define testScale 0.001*4.5/2.4
    
    // Clock Signal
    #define tCLK 0.0005     // 2 MHz Clock signal, 500 ns
    
    int sampleFreq = 250; // to 500. A value of 128 will give 512 samples
    int factor = 1000/sampleFreq/2;
    
    // generate output pulses to emulate DRDY from ADS
    // this should be placed in multiple places to emulate an unsynchronised DRDY
    #define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, (millis() / factor) % 2);
    //#define GENERATE_DRDY  digitalWrite(PULSE_OUTPUT_GPIO, FALLING)
    // end of simulation declarations
    
    EEG1 EEG;
    
    // Functions 
    const int  WAKEUP  = 0x02; // Wake-up from standby mode
    const byte STANDBY = 0b00000100; // Enter Standby mode
    const byte RESET   = 0b00000110; // Reset the device
    const byte START   = 0b00001000; // Start and restart (synchronize) conversions
    const byte STOP    = 0b00001010; // Stop conversion
    const byte RDATAC  = 0b00010000; // Enable Read Data Continuous mode (default mode at power-up)
    const byte SDATAC  = 0b00010001; // Stop Read Data Continuous mode
    const byte RDATA   = 0b00010010; // Read data by command; supports multiple read back
    const byte TEST1   = 0b00000000;
    const byte RREG    = 0x20;// (also = 00100000) is the first opcode that the address must be added to for RREG communication
    const byte WREG    = 0x40;// 01000000 in binary (Datasheet, pg. 35)
    
    double data;
    const byte led = 2;
    byte registerData[24];         // array is used to mirror register data
    byte channelDataRaw[24];
    long channelData[4];
    long ads[4];
    int sampleCounter = 0;
    //byte RREG(byte,int);            // read one ADS register
    
    bool channelDataAvailable;
    
     /* //SPI pin numbers:
     SCK   18  // Serial Clock.
     MISO  19  // Master In Slave Out.  (DOUT)
     MOSI  23  // Master Out Slave In.  (DIN)
     SS    5  // Slave Select*/
    
     //ADS1299-X Control Pin Declarations
     const int SCLK = 30;  // Serial Clock.
     const int STARTPIN = 12;
     const int ADS_RST = 13;
     const int PWDN = 3;
     const int ADS_DRDY = 4;         // ADS data ready GPIO 36
    
    void IRAM_ATTR ADS_DRDY_Service()
     {
    //  if (bitRead(ESP32_DRDY, 0) == 0)
      if (bitRead(ADS_DRDY, 0) == 0)
      {
    //    board.channelDataAvailable = true;
          channelDataAvailable = true;
      }
    }
    
    int devicebyte = 0;
    
    void setup() {
      Serial.begin(115200);
      Serial.flush();
      delay(1000);
    
        while(!Serial);
    
    //  Start Pin -- Pull Start Pin High in order to access ADS1299-X data - DRDY toggles at 250Hz
      pinMode(STARTPIN,OUTPUT);
      digitalWrite(STARTPIN,HIGH); 
    
      //SPI Communication Setup with ADS1299-X  
      // Chip Select Pin 
      pinMode(CS_ADS, OUTPUT);
      digitalWrite(CS_ADS, HIGH);
    
      //DRDY Pin - Drops LOW if Data is ready 
      pinMode(ADS_DRDY, INPUT);
      digitalWrite(ADS_DRDY, HIGH);
      
      // Set up SPI
      SPI.begin();
      SPI.setFrequency(2000000);    // Set 4 MHz for ADS
      SPI.setDataMode(SPI_MODE1);   // Datasheet pg. 12: CPOL = 0 and CPHA = 1 (SPI MODE 1)
    
      //Powering Up Sequence
      DeviceStartUp();
      Serial.println("Device power up complete...");
      delayMicroseconds(2);
    
      // Get Device ID 
      devicebyte = getDeviceID();
      Serial.println();
    
      // Print default settings for registers
      EEG.printADSregisters(CS_ADS);
    
      Serial.println();
    
      //Writing Channel Configuration
      writeRegisterConfig();
      delayMicroseconds(2);
    
       Serial.println();
    
      EEG.START();
      delay(1);
      EEG.RDATAC();
      delay(1);
    
      testSignals();
      Serial.println("Register Print After TestSignals:");
      EEG.printADSregisters(CS_ADS);
    
    }
    
    // --------------------------- LOOP ---------------------------------------------------
    void loop() {
    
      updateChannelData();
    
    }
    
    // ------------------------- Device Start up Procedure -------------------------------------
    
    void DeviceStartUp(){
      
      //PWDN and RESET PIN set HIGH
     
      pinMode(PWDN, OUTPUT);
      digitalWrite(PWDN, HIGH);
      
      pinMode(ADS_RST, OUTPUT);
      digitalWrite(ADS_RST, HIGH);
      
      //Delay for 1 second, for power up sequence 
      delay(1000);
    
      //RESET PULSE
      digitalWrite(ADS_RST,LOW);
      delayMicroseconds(1); //2 tclk cycles
      digitalWrite(ADS_RST,HIGH);
    
      //Wait to Settle 16-18 tclk
      delayMicroseconds(18*tCLK);
    
      //Send SDATAC
      EEG.SDATAC();
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
      
      }
    
      // ----------------------- Retrieving Device ID function ---------------------------------------------------
    int getDeviceID(){
      
      //Delay 
      delay(100);
     
      // Begin SPI 
      digitalWrite(CS_ADS,LOW); //Pull CS low to communicate
      SPI.transfer(RREG); // Read Register 00100000
      delayMicroseconds(2);
      
      SPI.transfer(ID_REG); //0x00 ID register 00000000
      delayMicroseconds(2);
      byte data = SPI.transfer(ADS_ID); // Binary value for device ID, 4CH = 0b???11100
      delayMicroseconds(2);
      
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      return data;
      }
    
      // ------------------------- Write Registers Configuration -------------------------------------
    
    void writeRegisterConfig() {
     // ADS1299-4 Datasheet pg 62:
     
      SPI.beginTransaction(SPISettings(2000000,MSBFIRST,SPI_MODE1));
      digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
     
      SPI.transfer(SDATAC);
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      //Using internal reference i.e BIASREF siganl (AVDD + AVSS)/2
      WREGConf(CONFIG3, 0xE0); 
      delayMicroseconds(2);
      
     // Set Device for f_mod/4096 (250 SPS)
      WREGConf(CONFIG1, 0x96);    WREGConf(CONFIG2, 0xC0);
      delayMicroseconds(2);
      
      // Set Channels to Input Short
      WREGConf(CH1SET, 0x01);  WREGConf(CH2SET, 0x01);
      WREGConf(CH3SET, 0x01);  WREGConf(CH4SET, 0x01);
      delayMicroseconds(2);
    
      WREGConf(MISC1, 0x00);
      delayMicroseconds(2);
      
      EEG.RREGS(ID_REG, CONFIG4, CS_ADS);
      delayMicroseconds(2);
      digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
      }
    
    // --------------------------- Write channel configuration procedure--------------------------
     
    void WREGConf(byte _address, byte _regValue){
       
      byte opcodeW = _address + 0x40;     // Write to one register with address (WREG = 0x40),  WREG expects 010rrrrr where rrrrr = _address
      SPI.transfer(opcodeW);              // Send WREG command and address
      SPI.transfer(0x00);                 // Send dummy byte to ADS. Send number of registers to read -1
      SPI.transfer(_regValue);            // Write register value
    
      registerData[_address] = _regValue; // Update mirror array
    } 
    
    // -------------------------------- Setup Test Signals -----------------------------------------
    
    void testSignals(){
      digitalWrite(CS_ADS,LOW);           //Pull CS LOW to start communication
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
      SPI.transfer(SDATAC);
    
      // Set Device for f_mod/4096 (250 SPS)
    //  WREGConf(CONFIG2, 0xD3);              // For Internal square signals: Set CONFIG2.BIT1 and CONFIG2.BIT0 to '1' (TI Forum reply)
      WREGConf(CONFIG2, 0xD0);
    
      // CONFIG3
      WREGConf(CONFIG3, 0xEC);      // Internal reference buffer & BIASREF signal generated internally
    
      // CONFIG1: Multiple readback mode
      WREGConf(CONFIG1, 0x96);
     
    //  // Set Channels to Test Signals: Changed 0D to 05
      WREGConf(CH1SET, 0x05);  WREGConf(CH2SET, 0x05);
      WREGConf(CH3SET, 0x05);  WREGConf(CH4SET, 0x05);
    
      digitalWrite(CS_ADS, HIGH); // Pull CS HIGH to disable communication between Teensy and ADS1299-X
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles
    
      EEG.RDATAC();
      delayMicroseconds(4*tCLK); //delay 4 tCLK cycles 
     }
    
      // ------------------------------- Update Channel Data -------------------------------------
    
    void updateChannelData(){
      byte inByte;
      int byteCounter = 0; 
      
      int numChan = 4;  //assume 4 channel.  If needed, it automatically changes to 16 automatically in a later block.
      long stat_1[1];
    
      long channelDat;
    
     if(digitalRead(ADS_DRDY) == LOW){
      digitalWrite(CS_ADS, LOW);        //  open SPI
      delayMicroseconds(2);
      
      // READ CHANNEL DATA FROM FIRST ADS
      for(int i=0; i<3; i++){     //  read 3 byte status register from ADS 1 (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
        inByte = SPI.transfer(0x00);
        stat_1[i] = (stat_1[i] << 8) | inByte;
      } 
      
      for(int i = 0; i < 4; i++){
        for(int j=0; j < 3; j++){   //  read 24 bits of channel data from 1st ADS in 8 3 byte chunks
          inByte = SPI.transfer(0x00);
    //      channelDataRaw[byteCounter] = inByte;             // Raw data array
    //      byteCounter++;      
          channelData[i] = (channelData[i]<<8) | inByte;    // Int data array
        }
      }  
      
      digitalWrite(CS_ADS, HIGH);       //  close SPI
      delayMicroseconds(2);
     
      //reformat the numbers
    //  for(int i = 0; i < 4; i++){     // convert 3 byte 2's compliment to 4 byte 2's compliment 
    //    if(bitRead(channelData[i],23) == 1){  
    //      channelData[i] |= 0xFF000000;
    //      ads[i] = channelData[i];
    //    }else{
    //      channelData[i] &= 0x00FFFFFF;
    //      ads[i] = channelData[i];
    //    }    
    //	printChannelData();
    //  }
    
    for(int i = 0; i < 24; i++){
        if(bitRead(stat_1[i],23) == 1){  
        stat_1[i] |= 0xFF000000;
          } else{
        stat_1[i] &= 0x00FFFFFF;
          } 
       Serial.println(stat_1[i], HEX);
        }
     
      }
    }
    
     // ------------------------------- Print Channel Data -------------------------------------
      
    void printChannelData(){
    //  Serial.println(" ");
     for(int i = 0; i < 4; i++){  
    
      Serial.println(ads[i],HEX);
     }  
    }

  • I checked now and my DOUT voltage is in the mV range. RESET and START are high and DRDYB is pulsing at 250 Hz. What can be keeping DOUT low? I think that is the reason I am getting a zero output for my status register.

  • Hi Elke,

    Have you verified that the uC SPI interface is not interfering with the DOUT pin of the ADS1299? Please follow some of the debugging suggestions I listed in the earlier posts.

    Thanks

    -TC

  • Hi,

    Yes, I followed the debugging suggestions you gave me and I can get an output now. Thanks! I noticed that my channel 1 pin is HIGH and once it should start outputting data, the pin goes low. Have you come across a problem like this before? The values I get are quite large negative values. The code is the same as my previous post and the picture shows the serial output I get. The three zeros represent channels 2-4 which I have written as power down.

  • Hi Elke,

    Thanks for the test data. 

    Can you please clarify which pin you refer to for channel 1 pin?

    Once you are able to capture the data with the correct status bit, you can verify the validity of the data by using the internal test signal as you have done in your initial post. Again, this will help to ensure all the hardware and software are functioning correctly.

    Thanks

    -TC

  • Apologies. For channel 1, I am using IN1P (pin 16).

    I printed out the registers and I got the default register setting as stated on pg 44 of the datasheet, followed the start-up procedure on pg 62 (fig 67), and got the registers to print correctly according to that. However, I write to my registers as stated in fig 67 but the registers return wrong when I output it back out. With these wrong registers, I do get the correct status register and a block wave between the values of 2500 and -4500 and my DRDYB is pulsing as it should. All registers write out as 00h until BIAS_SENSP and then it rest writes random values for the registers.

    When I output the correct test signal register output, I get a 0 output for everything. How can I get the correct status register value (C0h) and a block test signal when my registers are writing incorrectly? I followed the SPI debugging steps you provided early and everything is correct, except for the test signal registers writing incorrectly. The default settings and the start-up procedure registers write correctly and then followed by the incorrect test signal registers.

    Hopes this makes sense what I am trying to explain? Cause I am assuming these incorrectly written registers will also prohibit me from obtaining data when I apply a signal to the channel 1 (IN1P) pin?

  • Hi Elke,

    Thanks for the clarification.

    The IN1P pin is a high impedance input pin and should not be affected by the device's operation. Please check if there is an issue with the IN1N causing the external input to behave incorrectly.

    The device registers read/write operation shares the same SPI interface as the read/write operation. Therefore, it is crucial that you do not corrupt or mix the operation. The RDATAC and the SDATAC commands are in place to avoid any miscommunication when operating in the continuous conversion mode. As long as the critical registers are not corrupted and causing the DRDY signal to misbehave, you will always see the correct preamble for the status bit during data retrieval. However, you may see the incorrect channel data in this case.

    Thanks

    -TC

  • If I want to put my ADS into normal electrode mode, will this function be correct? Is SDATAC and RDATAC in the correct place? I am trying to see if there is any functions or code interfering with the SPI interface, so maybe I am missing something?

    void ADSMode() {
    // ADS1299-4 Datasheet pg 62:

    digitalWrite(CS_ADS,LOW); //Pull CS LOW to start communication

    SPI.transfer(_SDATAC);
    delayMicroseconds(4*tCLK); //delay 4 tCLK cycles

    //Using internal reference i.e BIASREF siganl (AVDD + AVSS)/2
    WREGConf(CONFIG2, 0xD0);
    delayMicroseconds(2);

    // Set Device for f_mod/4096 (250 SPS)
    WREGConf(CONFIG1, 0x96); WREGConf(CONFIG3, 0xE8);

    // Set Channels to Input Short
    WREGConf(CH1SET, 0x60); WREGConf(CH2SET, 0x89);
    WREGConf(CH3SET, 0x89); WREGConf(CH4SET, 0x89);
    delayMicroseconds(2);

    // MISC1
    WREGConf(MISC1, 0x20); // COnnects SRB1 to all 4 channels inverting inputs

    SPI.transfer(_RDATAC);
    delayMicroseconds(2);
    digitalWrite(CS_ADS,HIGH); //Pull CS HIGH to stop communication
    SPI.endTransaction();
    }

  • Hi Elke,

    The function looks fine to me. 

    Thanks

    -TC

  • Hi TC,

    Thanks! I am also just going to ask you to please check my update channel function, I just want to make sure I am doing the ADC code to volt conversion correctly as I understood it from the forum post (https://e2e.ti.com/support/data-converters-group/data-converters/f/data-converters-forum/772488/faq-ads129x-how-do-i-convert-adc-output-codes-to-volts/2856893#2856893).

    Then I want to ask about the output I am getting. I am currently using a DC power supply and giving channel 1 (IN1P) 4.7V and 20 mA. The registers seem to print out correctly, channel 1 is set to 0x60 (Gain = 24 and normal electrode input), and channel 2-4 is set to 0x89 (Channel power down, gain = 1, input shorten). The picture I attached is a piece of the output I am obtaining after the conversion in the order status register followed by channels 1-4 in that order.  As you can see, my channel 1's value does not change and is also 2.38 V, and isn't channel 2 also supposed to be 0? There is an electrode attached to channel 2 but nothing was connected to it. 

    It also bothers me that the numbers are not changing but just staying constant. The output is the same when I apply the DC voltage when there is no power supplied when I tap my finger on it.

    I am going to attach a picture of my electrode input sub-circuit and a close-up of my ADS1299-4 schematic. The entire schematic can be seen in one of my first posts. Do you think that the resistor and capacitor can somehow be blocking the signal to get through from the electrode to the chip? I followed the openBCI's schematic and they had these components on their Cyton board, so surely it is supposed to be able to cross?

    My other registers are configured as followed:

    CONFIG1 0x96

    CONFIG2 0xD3

    CONFIG3 0xe8

    CH1SET 0x60

    CH2-4SET 0x89

    MISC1 0x20

    and the rest 0x00.

    I am only using my positive channel inputs and all the negative channel inputs are connected to AVDD as stated in the datasheet. 

    Might you know the cause of this problem?

    5483.Code.txt
    void updateChannelData(){
      byte inByte;
      int byteCounter = 0; 
      int numChan = 4;  //assume 4 channel.  If needed, it automatically changes to 16 automatically in a later block.
      long stat_1[1];
    
      channelData[4] = 0;
      
    if(digitalRead(ADS_DRDY) == LOW){ 
      digitalWrite(CS_ADS, LOW);        //  open SPI
      
      // READ CHANNEL DATA FROM FIRST ADS
      for(int i=0; i<1; i++){     //  read 3 byte status register from ADS 1 (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
        inByte = SPI.transfer(0x00);
        stat_1[i] = (stat_1[i]<<8) | inByte;        
      }
      
      for(int i = 0; i < numChan; i++){
        for(int j=0; j < 3; j++){   //  read 24 bits of channel data from 1st ADS in 8 3 byte chunks
          inByte = SPI.transfer(0x00);    
          channelData[i] = (channelData[i]<<8) | inByte;    // Int data array
        }
    
      }  
      digitalWrite(CS_ADS, HIGH);       //  close SPI
     
      //reformat the numbers
      for(int i = 0; i < numChan; i++){
        if(((channelData[i] & 0x800000) >> 23) == 1){
          channelData[i] = channelData[i] - 2^24;
          channelData[i] |= 0xFF000000;
          ads[i] = channelData[i];
          }else{
            channelData[i] &= 0x00FFFFFF;
            ads[i] = channelData[i];
            }   
        }
    
    for(int i = 0; i < 1; i++){
        if(bitRead(stat_1[i],23) == 1){  
        stat_1[i] |= 0xFF000000;
          } else{
        stat_1[i] &= 0x00FFFFFF;
          }   
       Serial.println(stat_1[i], HEX);
        }
      printChannelData();
      }
    }
    
    
     // ------------------------------- Print Channel Data -------------------------------------
      
    void printChannelData(){
    //  Serial.println(" ");
    
     for(int i = 0; i < 4; i++){  
    
    //  Serial.println(channelData[i],HEX);
    //  Serial.println(channelData[i],DEC);
      Serial.println(ads[i]*((2*4.5/24)/(2^23 - 1)),DEC);
        }  
      Serial.println(); 
    }

  • Hi Elke,

    Please see some of my previous suggestions to make sure your signal chain is working properly by utilizing the internal test signal. The digital output code should be zero if the channel is powered down. I am not sure why you are getting non-zero data for this particular channel. Please verify the SPI output with an oscilloscope.

    Please note that if you set the channel gain to 24, the input signal for the channel will be limited. What you are seeing is the ADC output saturates to the full scale with the DC input. Please refer to the Analog Input section of the datasheet for more detailed information.

    Thanks

    -TC