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.
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.
#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
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?
#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.
#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.
#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 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?
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