ADS1299 SPI with Teensy 4.1 and SD card

clareto74

Member
Hi everyone
I am trying to get the ADS1299 data on an SD card on my teensy 4.1. I am using the included SD port provided by the teensy. The ADS uses SPI mode 1 while the SD card uses SPI mode 0.
When I run my code, the file is created but nothing is written in it. I can get the ADS data over the Serial but as soon as I try to save the data in the SD card it seems like it never enter mu RDATA_Update function.

Code:
/* main.cpp
 * This file contains demo code to test the ADS1299
 * 
 * Check configs.h file for configurations that can be set 
 * 
 * Authors: Mingye Chen
 */
#include <Arduino.h>
#include <SPI.h>
#include "CochlEEG.h"
#include "ADS1299.h"
#include <SD.h>



CochlEEG cochleeg;
ADS1299 ads;

int mode = 0;  // 0=stopped, 1=recording, 2=playing

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Serial starting");

  
  SPI.begin();
  SPI.setDataMode(SPI_MODE1); // use SPI mode for ADS
  SPI.setClockDivider(4000000);
  
  SPI2.begin();
  SPI2.setDataMode(SPI_MODE0);

    // Initialize the SD card
  if (!(SD.begin(BUILTIN_SDCARD))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  ads.initialize();
  
  ads.desactiveChannel(2);
  ads.desactiveChannel(3);
  ads.desactiveChannel(4);
  ads.desactiveChannel(5);
  ads.desactiveChannel(6);
  ads.desactiveChannel(7);

  ads.printAllRegisters();

  delay(2000);

}

void loop() {

    if (Serial.available() > 0) {
    char inChar = Serial.read();
    switch (inChar) {
      case 'r':
        Serial.println("Record Button Press");
        if (mode == 0) {
          ads.startRecording();
        }
        break;
      case 's':
        Serial.println("Stop Button Press");
        if (mode == 1) ads.stopRecording();
        break;
    }
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    ads.RDATA_update();
  }
  
}

and here is the ADS.cpp

Code:
/* ADS1299.cpp
 * This file contains the implementation of functions found in ADS1299.h
 * It is a driver library for the ADS1299 EEG/EMG/ECG signal aquisition chip from TI
 * 
 * Authors: Mingye Chen
 */

#include "ADS1299.h"
#include "configs.h"

//Other Global Variables :
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[] = FILE_BASE_NAME "00.txt";

File MyFile;

void ADS1299::initialize(){

    pinMode(SD_SS,OUTPUT);
    digitalWrite(SD_SS,HIGH);
    pinMode(ADS_SS, OUTPUT);
    digitalWrite(ADS_SS,HIGH);
    pinMode(ADS_DRDY, INPUT);
    

    delay(50);
    pinMode(ADS_RST, OUTPUT);
    digitalWrite(ADS_RST, LOW); 
    delay(4);
    delay(40);
    digitalWrite(8, HIGH);
    delay(1000);
    // reset pulse
    digitalWrite(ADS_RST,LOW);
    delayMicroseconds(15);
    digitalWrite(ADS_RST, HIGH);
    delay(40);
    RESET();
    SDATAC();
    delay(100);

    // DEFAULT CHANNEL SETTINGS FOR ADS
    defaultChannelSettings[POWER_DOWN] = NO;        // on = NO, off = YES
    defaultChannelSettings[GAIN_SET] = ADS_GAIN24;     // Gain setting
    defaultChannelSettings[INPUT_TYPE_SET] = ADSINPUT_NORMAL;// input muxer setting
    defaultChannelSettings[BIAS_SET] = YES;    // add this channel to bias generation
    defaultChannelSettings[SRB2_SET] = YES;       // connect this P side to SRB2
    defaultChannelSettings[SRB1_SET] = YES;        // don't use SRB1
    for(int i=0; i<8; i++){
      for(int j=0; j<6; j++){
        channelSettings[i][j] = defaultChannelSettings[j];
      }
    }
    for(int i=0; i<8; i++){
      useInBias[i] = true;    // keeping track of inputs used for Bias Generation
      useSRB2[i] = true;      // keeping track of inputs using SRB2
    }
      useSRB1 = false;
      
    writeChannelSettings();
    WREG(CONFIG3,0b11101100); delay(1);  // enable internal reference drive and etc.
    for(int i=0; i<8; i++){  // turn off the impedance measure signal
      leadOffSettings[i][PCHAN] = OFF;
      leadOffSettings[i][NCHAN] = OFF;
    }
    verbosity = false;      // when verbosity is true, there will be Serial feedback
    startADS();
}

void ADS1299::startRecording(){
    while (SD.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
     fileName[BASE_NAME_SIZE + 1]++;
    } 
    else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } 
    else {
      Serial.println(F("Can't create file name"));
      return;
    }
  }
  MyFile = SD.open(fileName, FILE_WRITE);
  MyFile.println("Chn1,Chn2,Chn3,Chn4,Chn5,Chn6,Chn7,Chn8");
  _record = true;
}


void ADS1299::stopRecording(){
  _record = false;
  MyFile.close();
}

// <<<<<<<<<<<<<<<<<<<<<<<<<  START OF ADS1299 FUNCTIONS  >>>>>>>>>>>>>>>>>>>>>>>>>

void ADS1299::CStoLOW(void)
{
  digitalWrite(SD_SS,HIGH);
  digitalWrite(ADS_SS, LOW);
}

void ADS1299::CStoHIGH(void)
{
  digitalWrite(ADS_SS, HIGH);
  digitalWrite(SD_SS,LOW);
}

void ADS1299::reportDefaultChannelSettings(void){

    Serial.write(defaultChannelSettings[POWER_DOWN] + '0');        // on = NO, off = YES
    Serial.write((defaultChannelSettings[GAIN_SET] >> 4) + '0');     // Gain setting
    Serial.write(defaultChannelSettings[INPUT_TYPE_SET] +'0');// input muxer setting
    Serial.write(defaultChannelSettings[BIAS_SET] + '0');    // include in bias generation
    Serial.write(defaultChannelSettings[SRB2_SET] + '0');       // connect this P side to SRB2
    Serial.write(defaultChannelSettings[SRB1_SET] + '0');        // don't use SRB1
}


void ADS1299::writeChannelSettings(void){
  byte setting;
  boolean use_SRB1 = false;
//proceed...first, disable any data collection
  SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS

  for(byte i=0; i<8; i++){ // write 8 channel settings
    setting = 0x00;
    if(channelSettings[i][POWER_DOWN] == YES) {setting |= 0x80;}
    setting |= channelSettings[i][GAIN_SET]; // gain
    setting |= channelSettings[i][INPUT_TYPE_SET]; // input code
    if(channelSettings[i][SRB2_SET] == YES){
      setting |= 0x08; // close this SRB2 switch
      useSRB2[i] = true;
    }else{
      useSRB2[i] = false;
    }
    WREG(CH1SET+i, setting);  // write this channel's register settings
    
      // add or remove from inclusion in BIAS generation
      setting = RREG_return(BIAS_SENSP);       //get the current P bias settings
      if(channelSettings[i][BIAS_SET] == YES){
        bitSet(setting,i);    //set this channel's bit to add it to the bias generation
        useInBias[i] = true;
      }else{
        bitClear(setting,i);  // clear this channel's bit to remove from bias generation
        useInBias[i] = false;
      }
      WREG(BIAS_SENSP,setting); delay(1); //send the modified byte back to the ADS
      setting = RREG_return(BIAS_SENSN);       //get the current N bias settings
      if(channelSettings[i][BIAS_SET] == YES){
        bitSet(setting,i);    //set this channel's bit to add it to the bias generation
      }else{
        bitClear(setting,i);  // clear this channel's bit to remove from bias generation
      }
      WREG(BIAS_SENSN,setting); delay(1); //send the modified byte back to the ADS
      
    if(channelSettings[i][SRB1_SET] == YES){
      useSRB1 = true;
    }
  }
    if(useSRB1){
      for(int i=0; i<8; i++){
        channelSettings[i][SRB1_SET] = YES;
      }
      WREG(MISC1,0x20);     // close all SRB1 swtiches
    }else{
      for(int i=0; i<8; i++){
        channelSettings[i][SRB1_SET] = NO;
      }
      WREG(MISC1,0x00);
    }
}

void ADS1299::writeChannelSettings(int N){
  byte setting;
  if ((N < 1) || (N > 8)) return;  // must be a legit channel number
  N = constrain(N-1,0,7);  //subtracts 1 so that we're counting from 0, not 1
//proceed...first, disable any data collection
  SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
  
    setting = 0x00;
    if(channelSettings[N][POWER_DOWN] == YES) {setting |= 0x80;}
    setting |= channelSettings[N][GAIN_SET]; // gain
    setting |= channelSettings[N][INPUT_TYPE_SET]; // input code
    if(channelSettings[N][SRB2_SET] == YES){
      setting |= 0x08; // close this SRB2 switch
      useSRB2[N] = true;  // keep track of SRB2 usage
    }else{
      useSRB2[N] = false;
    }
    WREG(CH1SET+N, setting);  // write this channel's register settings
    
      // add or remove from inclusion in BIAS generation
      setting = RREG_return(BIAS_SENSP);       //get the current P bias settings
      if(channelSettings[N][BIAS_SET] == YES){
        useInBias[N] = true;
        bitSet(setting,N);    //set this channel's bit to add it to the bias generation
      }else{
        useInBias[N] = false;
        bitClear(setting,N);  // clear this channel's bit to remove from bias generation
      }
      WREG(BIAS_SENSP,setting); delay(1); //send the modified byte back to the ADS
      setting = RREG_return(BIAS_SENSN);       //get the current N bias settings
      if(channelSettings[N][BIAS_SET] == YES){
        bitSet(setting,N);    //set this channel's bit to add it to the bias generation
      }else{
        bitClear(setting,N);  // clear this channel's bit to remove from bias generation
      }
      WREG(BIAS_SENSN,setting); delay(1); //send the modified byte back to the ADS
      
    if(channelSettings[N][SRB1_SET] == YES){
    for(int i=0; i<8; i++){
      channelSettings[i][SRB1_SET] = YES;
    }
    useSRB1 = true;
      WREG(MISC1,0x20);     // close all SRB1 swtiches
  }
  if((channelSettings[N][SRB1_SET] == NO) && (useSRB1 == true)){
    for(int i=0; i<8; i++){
      channelSettings[i][SRB1_SET] = NO;
    }
    useSRB1 = false;
    WREG(MISC1,0x00);
  }
}

void ADS1299::desactiveChannel(int N){
byte setting;  
  if ((N < 1) || (N > 8)) return;  //check the inputs  
  //proceed...first, disable any data collection
  SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
  //shut down the channel
  N = constrain(N-1,0,7);  //subtracts 1 so that we're counting from 0, not 1
//  reg = CH1SET+(byte)N;           // select the current channel
  setting = RREG_return(CH1SET+(byte)N); delay(1); // get the current channel settings
  bitSet(setting,7);              // set bit7 to shut down channel
  channelSettings[N][POWER_DOWN] = YES;  // keep track of channel on/off state
  bitClear(setting,3);    // clear bit3 to disclude from SRB2 
  WREG(CH1SET+(byte)N,setting); delay(1);     // write the new value to disable the channel
  
  //remove the channel from the bias generation...
  setting = RREG_return(BIAS_SENSP); delay(1); //get the current bias settings
  bitClear(setting,N);                  //clear this channel's bit to remove from bias generation
  WREG(BIAS_SENSP,setting); delay(1);   //send the modified byte back to the ADS

  setting = RREG_return(BIAS_SENSN); delay(1); //get the current bias settings
  bitClear(setting,N);                  //clear this channel's bit to remove from bias generation
  WREG(BIAS_SENSN,setting); delay(1);   //send the modified byte back to the ADS
  
  leadOffSettings[N][0] = leadOffSettings[N][1] = NO; // stop lead off detection 
  changeChannelLeadOffDetection();

}


void ADS1299::activateChannel(int N){
    byte setting;  
   
  if ((N < 1) || (N > 8)) return; //check the inputs
  N = constrain(N-1,0,7);  //shift down by one
  //proceed...first, disable any data collection
  SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
  setting = 0x00;
  channelSettings[N][POWER_DOWN] = NO; // keep track of channel on/off state
  setting |= channelSettings[N][GAIN_SET]; // gain
  setting |= channelSettings[N][INPUT_TYPE_SET]; // input code
  if(useSRB2[N] == true){channelSettings[N][SRB2_SET] = YES;}else{channelSettings[N][SRB2_SET] = NO;}
  if(channelSettings[N][SRB2_SET] == YES) bitSet(setting,3); // close this SRB2 switch
  WREG(CH1SET+N, setting);
  // add or remove from inclusion in BIAS generation
    if(useInBias[N]){channelSettings[N][BIAS_SET] = YES;}else{channelSettings[N][BIAS_SET] = NO;}
    setting = RREG_return(BIAS_SENSP);       //get the current P bias settings
    if(channelSettings[N][BIAS_SET] == YES){
      bitSet(setting,N);    //set this channel's bit to add it to the bias generation
    }else{
      bitClear(setting,N);  // clear this channel's bit to remove from bias generation
    }
    WREG(BIAS_SENSP,setting); delay(1); //send the modified byte back to the ADS
    setting = RREG_return(BIAS_SENSN);       //get the current N bias settings
    if(channelSettings[N][BIAS_SET] == YES){
      bitSet(setting,N);    //set this channel's bit to add it to the bias generation
    }else{
      bitClear(setting,N);  // clear this channel's bit to remove from bias generation
    }
    WREG(BIAS_SENSN,setting); delay(1); //send the modified byte back to the ADS
    
  setting = 0x00;
  if(useSRB1) setting = 0x20;
  WREG(MISC1,setting);     // close all SRB1 swtiches if desired
}


// Query to see if data is available from the ADS1299...return TRUE is data is available
boolean ADS1299::isDataAvailable(void)
{
  return (!(digitalRead(ADS_DRDY)));
}


void ADS1299::RDATA_update(){

        CStoLOW();
        // RDATA command - ONLY DIFFERENCE BETWEEN THIS FUNC AND THE updateDATA()
        SPI.transfer(_RDATA);

        long output[9];
        uint32_t dataPacket;
        for(int i = 0; i<9; i++){
            for(int j = 0; j<3; j++){
                byte dataByte = SPI.transfer(0x00);
                dataPacket = (dataPacket<<8) | dataByte; // constructing the 24 bit binary
            }
            output[i] = dataPacket;
            dataPacket = 0;
        }
        CStoHIGH();
        for(int i=1; i<9; i++){
            if(bitRead(output[i],23) == 1){    // convert 3 byte 2's compliment to 4 byte 2's compliment
                output[i] |= 0xFF000000;
            }
            else{
                output[i] &= 0x00FFFFFF;
            }  
        }
        
        
        // conversions to microvolts 
        
        double outputvolts[9];
        for(int i = 1; i < 9; i++){
            outputvolts[i] = 1000* double(output[i])*2*4.5/24/(pow(2,24));
        }
        
        
        // changed to get data from 4 channels of ads1299-4
        // i=0;i<9 was original
        for (int i=1;i<9; i++) {
            Serial.print(outputvolts[i]);
            MyFile.print(outputvolts[i]);
            MyFile.print(",");
            //Serial.print(output[i]);
            if(i!=8) Serial.print("\t");
            
        }
        Serial.println();
        MyFile.println();
    
    
}


//write as binary each channel's 3 bytes of data
void ADS1299::writeADSchannelData(void)
{
    for (int i=0;i<8; i++) {
        Serial.print(channelData[i]);
        if(i!=8) Serial.print("\t");
            
    }
    Serial.println();
}


void ADS1299::changeChannelLeadOffDetection(void){
  byte P_setting = RREG_return(LOFF_SENSP);
  byte N_setting = RREG_return(LOFF_SENSN);
  
  for(int i=0; i<8;i++){
    if(leadOffSettings[i][0] == YES){
      bitSet(P_setting,i);
    }else{
      bitClear(P_setting,i);
    }
    if(leadOffSettings[i][1] == YES){
      bitSet(N_setting,i);
    }else{
      bitClear(N_setting,i);
    }
  }
   WREG(LOFF_SENSP,P_setting);
   WREG(LOFF_SENSN,N_setting);
  
}

void ADS1299::configureLeadOffDetection(byte amplitudeCode, byte freqCode)
{
	amplitudeCode &= 0b00001100;  //only these two bits should be used
	freqCode &= 0b00000011;  //only these two bits should be used

	byte setting = 0x00;	
	setting = ADS1299::RREG_return(LOFF); //get the current bias settings
	
	//reconfigure the byte to get what we want
	setting &= 0b11110000;  //clear out the four ls bits
	setting |= amplitudeCode;  //set the amplitude
	setting |= freqCode;    //set the frequency
	
	//send the config byte back to the hardware
	ADS1299::WREG(LOFF,setting); delay(1);  //send the modified byte back to the ADS
	
}

//Configure the test signals that can be inernally generated by the ADS1299
void ADS1299::configureInternalTestSignal(byte amplitudeCode, byte freqCode)
{
	if (amplitudeCode == ADSTESTSIG_NOCHANGE) amplitudeCode = (RREG_return(CONFIG2) & (0b00000100));
	if (freqCode == ADSTESTSIG_NOCHANGE) freqCode = (RREG_return(CONFIG2) & (0b00000011));
	freqCode &= 0b00000011;  		//only the last two bits are used
	amplitudeCode &= 0b00000100;  	//only this bit is used
	byte message = 0b11010000 | freqCode | amplitudeCode;  //compose the code
	
	WREG(CONFIG2,message); delay(1);
	
}


//  <<<<<<  SYSTEM COMMANDS  >>>>>>

void ADS1299::RESET() {
    CStoLOW();
    SPI.transfer(_RESET);
    delay(10);
//    delay(18.0*tCLK); //must wait 18 tCLK cycles to execute this command (Datasheet, pg. 35)
    CStoHIGH();
}
void ADS1299::START() {
    CStoLOW();
    SPI.transfer(_START);
    CStoHIGH();
}
void ADS1299::STOP() {
    CStoLOW();
    SPI.transfer(_STOP);
    CStoHIGH();
}
//Data Read Commands
void ADS1299::RDATAC() {
    CStoLOW();
    SPI.transfer(_RDATAC);
    CStoHIGH();
}
void ADS1299::SDATAC() {
    CStoLOW();
    SPI.transfer(_SDATAC);
    CStoHIGH();
}
void ADS1299::RDATA() {
    CStoLOW();
    SPI.transfer(_RDATA);
    CStoHIGH();
}

void ADS1299::resetADS(void)
{
  RESET();             // send RESET command to default all registers
  SDATAC();            // exit Read Data Continuous mode to communicate with ADS
  delay(100);
}

// Stop the continuous data acquisition
void ADS1299::stopADS(void)
{
    STOP();
  delay(1);       // start the data acquisition
    SDATAC();
  delay(1);       // exit Read Data Continuous mode to communicate with ADS
    isRunning = false;
}

// Start continuous data acquisition
void ADS1299::startADS(void)
{
    RDATAC(); 
  delay(1);   // enter Read Data Continuous mode
    START();        // start the data acquisition
  delay(1);
    isRunning = true;
}

// REGISTER READ/WRITE COMMANDS

byte ADS1299::RREG_return(byte _address) {   //  reads ONE register at _address
    byte opcode1 = _address + 0x20;   //  RREG expects 001rrrrr where rrrrr = _address
    CStoLOW();        //  open SPI
    SPI.transfer(opcode1);          //  opcode1
    SPI.transfer(0x00);           //  opcode2
    regData[_address] = SPI.transfer(0x00);//  update mirror location with returned byte
    CStoHIGH();       //  close SPI 
  if (verbosity){           //  verbosity output
    printRegisterName(_address);
    printHex(_address);
    Serial.print(F(", "));
    printHex(regData[_address]);
    Serial.print(F(", "));
    for(byte j = 0; j<8; j++){
      Serial.print(bitRead(regData[_address], 7-j));
      if(j!=7) Serial.print(F(", "));
    }
    
    Serial.println();
  }
  return regData[_address];     // return requested register value
}

void ADS1299::RREG(byte _address, byte _numRegistersMinusOne) {
    byte opcode1 = _RREG + _address; //001rrrrr; _RREG = 00100000 and _address = rrrrr
    CStoLOW(); //Low to communicated
    SPI.transfer(_SDATAC); //SDATAC
    SPI.transfer(opcode1); //RREG
    SPI.transfer(_numRegistersMinusOne); //opcode2
    for(byte i = 0; i <= _numRegistersMinusOne; i++){
        byte data = SPI.transfer(0x00); // returned byte should match default of register map unless previously edited manually (Datasheet, pg.39)
        printRegisterName(i);
        Serial.print("0x");
        if(i<16) Serial.print("0"); //lead with 0 if value is between 0x00-0x0F to ensure 2 digit format
        Serial.print(i, HEX);
        Serial.print(", ");
        Serial.print("0x");
        if(data<16) Serial.print("0"); //lead with 0 if value is between 0x00-0x0F to ensure 2 digit format
        Serial.print(data, HEX);
        Serial.print(", ");
        for(byte j = 0; j<8; j++){
            Serial.print(bitRead(data, 7-j), BIN);
            if(j!=7) Serial.print(", ");
        }
        Serial.println();
    }
    SPI.transfer(_RDATAC); //turn read data continuous back on
    CStoHIGH(); //High to end communication
}

void ADS1299::WREG(byte _address, byte _value) {
    byte opcode1 = _WREG + _address; //001rrrrr; _RREG = 00100000 and _address = rrrrr
    CStoLOW(); //Low to communicated
    SPI.transfer(_SDATAC); //SDATAC
    SPI.transfer(opcode1);
    SPI.transfer(0x00);
    SPI.transfer(_value);
    SPI.transfer(_RDATAC);
    CStoHIGH(); //Low to communicated
    Serial.print("Register 0x");
    Serial.print(_address, HEX);
    Serial.println(" modified.");
}
//Register Read/Write Commands
void ADS1299::getID() {
    CStoLOW(); //Low to communicated
    SPI.transfer(_SDATAC); //SDATAC
    SPI.transfer(_RREG); //RREG
    SPI.transfer(0x00); //Asking for 1 byte
    byte data = SPI.transfer(0x00); // byte to read (hopefully 0b???11110)
    SPI.transfer(_RDATAC); //turn read data continuous back on
    CStoHIGH(); //Low to communicated
    Serial.println(data, BIN);
}


//print out the state of all the control registers
void ADS1299::printAllRegisters(void)   
{
    boolean wasRunning = false;
    boolean prevverbosityState = verbosity;
    if (isRunning){ stopADS(); wasRunning = true; }
        verbosity = true;           // set up for verbosity output
        RREG(0x00,0x17);       // read out the first registers
//        delay(10);              // stall to let all that data get read by the PC
//        RREGS(0x11,0x17-0x11);  // read out the rest
        verbosity = prevverbosityState;
    if (wasRunning){ startADS(); }
}

// String-Byte converters for RREG and WREG
void ADS1299::printRegisterName(byte _address) {
    if(_address == ID){
        Serial.print("ID, ");
    }
    else if(_address == CONFIG1){
        Serial.print("CONFIG1, ");
    }
    else if(_address == CONFIG2){
        Serial.print("CONFIG2, ");
    }
    else if(_address == CONFIG3){
        Serial.print("CONFIG3, ");
    }
    else if(_address == LOFF){
        Serial.print("LOFF, ");
    }
    else if(_address == CH1SET){
        Serial.print("CH1SET, ");
    }
    else if(_address == CH2SET){
        Serial.print("CH2SET, ");
    }
    else if(_address == CH3SET){
        Serial.print("CH3SET, ");
    }
    else if(_address == CH4SET){
        Serial.print("CH4SET, ");
    }
    else if(_address == CH5SET){
        Serial.print("CH5SET, ");
    }
    else if(_address == CH6SET){
        Serial.print("CH6SET, ");
    }
    else if(_address == CH7SET){
        Serial.print("CH7SET, ");
    }
    else if(_address == CH8SET){
        Serial.print("CH8SET, ");
    }
    else if(_address == BIAS_SENSP){
        Serial.print("BIAS_SENSP, ");
    }
    else if(_address == BIAS_SENSN){
        Serial.print("BIAS_SENSN, ");
    }
    else if(_address == LOFF_SENSP){
        Serial.print("LOFF_SENSP, ");
    }
    else if(_address == LOFF_SENSN){
        Serial.print("LOFF_SENSN, ");
    }
    else if(_address == LOFF_FLIP){
        Serial.print("LOFF_FLIP, ");
    }
    else if(_address == LOFF_STATP){
        Serial.print("LOFF_STATP, ");
    }
    else if(_address == LOFF_STATN){
        Serial.print("LOFF_STATN, ");
    }
    else if(_address == GPIO){
        Serial.print("GPIO, ");
    }
    else if(_address == MISC1){
        Serial.print("MISC1, ");
    }
    else if(_address == MISC2){
        Serial.print("MISC2, ");
    }
    else if(_address == CONFIG4){
        Serial.print("CONFIG4, ");
    }

}

    // Used for printing HEX in verbosity feedback mode
void ADS1299::printHex(byte _data){
    Serial.print(F("0x"));
    if(_data < 0x10) Serial.print(F("0"));
    Serial.print(_data, HEX);
}

I use the normal SPI pins exept for CS of ADS -> pin 37
And for SD card I use BUILTIN_SDCARD

Does anyone know what could be the probleme ? I know it might be the SPI communication that must switch between mode 0 and 1 but I do that in my CStoLOW and HIGH function.
 
Does anyone know what could be the probleme ? I know it might be the SPI communication that must switch between mode 0 and 1 but I do that in my CStoLOW and HIGH function.

You don't have to configure SPI2 to use the SD card. Just use SD.begin(BUILTIN_SDCARD), and then you are ready to open a file to read or write. The code below is from the ReadWrite example for the SD library. See this example.

Code:
  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  
  // open the file. 
  myFile = SD.open("test.txt", FILE_WRITE);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
	// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
  
  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
    
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
    	Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
 
Ok I have managed to write to my SD card, but I have an issue. I am opening and closing my file every time I change the SPI mode communication (CStoHIGH/LOW). I'm guessing every time I put the SD CS pin to HIGH to communicate with the ADS I have to close the file...
Would it be possible to communicate with the SD module on another SPI bus so I would have to separate SPI communication ?
Here is the code I modified. I put the changes into comment cause if I run the program for a long time the acquiring rate decrease
Code:
void ADS1299::CStoLOW(void)
{
  //MyFile.close();
  //digitalWrite(SD_SS, HIGH);
  digitalWrite(ADS_SS, LOW);
}

void ADS1299::CStoHIGH(void)
{
  digitalWrite(ADS_SS, HIGH);
  //digitalWrite(SD_SS, LOW);
  //MyFile = SD.open(fileName, FILE_WRITE);
}
 
If you are initializing your SD card with SD.begin(BUILTIN_SDCARD), it will not interfere with the SPI port using pins 10,11,12 and 13. Once you have initialized the SD card and have opened your data file, there is no need to close the file until you are finished collecting your data. Since you're not using SPI for the SD card, you should not be changing the SD_CS pin. You should only be changing the SPI mode if you are using more than one device on the SPI port. If your ADS1299 is the only SPI device, you should be able to set it up once at the start of your program.

NOTE: if you're going to collect to a single file for a long time (hours to days), you may want to call the sync() function every few minutes or so to update the directory information for the file. This will ensure that you can recover data up the time of the last sync() call if you lose power or trip over a cord and disconnect the system.
 
Back
Top