Joel Murphy / Mbed 2 deprecated ADS_StreamRawData

Dependencies:   mbed

ADS1299Manager.cpp

Committer:
biomurph
Date:
2015-03-23
Revision:
0:675506e540be

File content as of revision 0:675506e540be:




#include <ADS1299Manager.h>

typedef long int32;
//typedef byte uint8_t;


void ADS1299Manager::initialize(void) {
    initialize(OPENBCI_V2);
}

//Initilize the ADS1299 controller...call this once
void ADS1299Manager::initialize(const int version) 
{
  setVersionOpenBCI(version);
  ADS1299::initialize(PIN_DRDY,PIN_RST,PIN_CS,SCK_MHZ); // (DRDY pin, RST pin, CS pin, SCK frequency in MHz);
  delay(100);
  
  verbose = false;      // when verbose is true, there will be Serial feedback 
  reset();
  
  //set default state for internal test signal
  //ADS1299::WREG(CONFIG2,0b11010000);delay(1);   //set internal test signal, default amplitude, default speed, datasheet PDF Page 41
  //ADS1299::WREG(CONFIG2,0b11010001);delay(1);   //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41
  configureInternalTestSignal(ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41

};

void ADS1299Manager::setVersionOpenBCI(const int version)
{
  if (version == OPENBCI_V1) {
      use_neg_inputs = false;
      for (int i=0; i < OPENBCI_NCHAN; i++) {
          use_SRB2[i] = false;
      }
  } else {
      use_neg_inputs = true;
      for (int i=0; i < OPENBCI_NCHAN; i++) {
          use_SRB2[i] = true;
      }
  }
  //setSRB1(use_SRB1());  //set whether SRB1 is active or not
}

//reset all the ADS1299's settings.  Call however you'd like.  Stops all data acquisition
void ADS1299Manager::reset(void)
{
  ADS1299::RESET();             // send RESET command to default all registers
  ADS1299::SDATAC();            // exit Read Data Continuous mode to communicate with ADS
  
  delay(100);
    
  // turn off all channels
  for (int chan=1; chan <= 8; chan++) {
    deactivateChannel(chan);
  }
  
  setSRB1(use_SRB1());  //set whether SRB1 is active or not
  
};


//deactivate the given channel...note: stops data colleciton to issue its commands
//  N is the channel number: 1-8
// 
void ADS1299Manager::deactivateChannel(int N)
{
  byte reg, config;
    
  //check the inputs
  if ((N < 1) || (N > 8)) return;
  
  //proceed...first, disable any data collection
  ADS1299::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;
  config = ADS1299::RREG(reg); delay(1);
  bitSet(config,7);  //left-most bit (bit 7) = 1, so this shuts down the channel
  if (use_neg_inputs) bitClear(config,3);  //bit 3 = 0 disconnects SRB2
  ADS1299::WREG(reg,config); delay(1);
  
  //remove the channel from the bias generation...
  reg = BIAS_SENSP; if (use_neg_inputs) reg = BIAS_SENSN;  //are we using the P inptus or the N inputs?
  config = ADS1299::RREG(reg); delay(1);//get the current bias settings
  bitClear(config,N);          //clear this channel's bit to remove from bias generation
  ADS1299::WREG(reg,config); delay(1);  //send the modified byte back to the ADS
  
}; 
    
        
//Active a channel in single-ended mode  
//  N is 1 through 8
//  gainCode is defined in the macros in the header file
//  inputCode is defined in the macros in the header file
void ADS1299Manager::activateChannel(int N,byte gainCode,byte inputCode) 
{
   //check the inputs
  if ((N < 1) || (N > 8)) return;
  
  //proceed...first, disable any data collection
  ADS1299::SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS

  //active the channel using the given gain.  Set MUX for normal operation
  //see ADS1299 datasheet, PDF p44
  N = constrain(N-1,0,7);  //shift down by one
  byte configByte = 0b00000000;  //left-most zero (bit 7) is to activate the channel
  gainCode = gainCode & 0b01110000;  //bitwise AND to get just the bits we want and set the rest to zero
  configByte = configByte | gainCode; //bitwise OR to set just the gain bits high or low and leave the rest alone
  inputCode = inputCode & 0b00000111;  //bitwise AND to get just the bits we want and set the rest to zero
  configByte = configByte | inputCode; //bitwise OR to set just the gain bits high or low and leave the rest alone
  if (use_SRB2[N]) configByte |= 0b00001000;  //set the SRB2 flag...p44 in the data sheet
  ADS1299::WREG(CH1SET+(byte)N,configByte); delay(1);

  //add this channel to the bias generation
  //see ADS1299 datasheet, PDF p44
  byte reg = BIAS_SENSP; if (use_neg_inputs) reg = BIAS_SENSN;  //are we using the P inptus or the N inputs?
  byte biasSettings = ADS1299::RREG(reg); //get the current bias settings
  bitSet(biasSettings,N);                   //set this channel's bit
  ADS1299::WREG(reg,biasSettings); delay(1);  //send the modified byte back to the ADS
  
  // // Now, these actions are necessary whenever there is at least one active channel
  // // though they don't strictly need to be done EVERY time we activate a channel.
  // // just once after the reset.
  
  //activate SRB1 as the Negative input for all channels, if needed
  setSRB1(use_SRB1());

  //Finalize the bias setup...activate buffer and use internal reference for center of bias creation, datasheet PDF p42
  ADS1299::WREG(CONFIG3,0b11101100); delay(1); 
};

void ADS1299Manager::setSRB1(boolean desired_state) {
    if (desired_state) {
        ADS1299::WREG(MISC1,0b00100000); delay(1);  //ADS1299 datasheet, PDF p46
    } else {
        ADS1299::WREG(MISC1,0b00000000); delay(1);  //ADS1299 datasheet, PDF p46
    }
}


//Configure the test signals that can be inernally generated by the ADS1299
void ADS1299Manager::configureInternalTestSignal(byte amplitudeCode, byte freqCode)
{
    if (amplitudeCode == ADSTESTSIG_NOCHANGE) amplitudeCode = (ADS1299::RREG(CONFIG2) & (0b00000100));
    if (freqCode == ADSTESTSIG_NOCHANGE) freqCode = (ADS1299::RREG(CONFIG2) & (0b00000011));
    freqCode &= 0b00000011;  //only the last two bits should be used
    amplitudeCode &= 0b00000100;  //only this bit should be used
    byte message = 0b11010000 | freqCode | amplitudeCode;  //compose the code
    
    ADS1299::WREG(CONFIG2,message); delay(1);
    
       //ADS1299::WREG(CONFIG2,0b11010000);delay(1);   //set internal test signal, default amplitude, default speed, datasheet PDF Page 41
      //ADS1299::WREG(CONFIG2,0b11010001);delay(1);   //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41
      //ADS1299::WREG(CONFIG2,0b11010101);delay(1);   //set internal test signal, 2x amplitude, 2x speed, datasheet PDF Page 41
      //ADS1299::WREG(CONFIG2,0b11010011); delay(1);  //set internal test signal, default amplitude, at DC, datasheet PDF Page 41
      //ADS1299::WREG(CONFIG3,0b01101100); delay(1);  //use internal reference for center of bias creation, datasheet PDF p42 
}
 
//Start continuous data acquisition
void ADS1299Manager::start(void)
{
    ADS1299::RDATAC(); delay(1);           // enter Read Data Continuous mode
    ADS1299::START();    //start the data acquisition
}
  
//Query to see if data is available from the ADS1299...return TRUE is data is available
int ADS1299Manager::isDataAvailable(void)
{
  return (!(digitalRead(PIN_DRDY)));
}
  
//Stop the continuous data acquisition
void ADS1299Manager::stop(void)
{
    ADS1299::STOP(); delay(1);   //start the data acquisition
    ADS1299::SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
}
  
//print as text each channel's data
//   print channels 1-N (where N is 1-8...anything else will return with no action)
//   sampleNumber is a number that, if greater than zero, will be printed at the start of the line
void ADS1299Manager::printChannelDataAsText(int N, long int sampleNumber)
{
    //check the inputs
    if ((N < 1) || (N > 8)) return;
    
    //print the sample number, if not disabled
    if (sampleNumber > 0) {
        pc.printf(sampleNumber);
        pc.printf(", ");
    }

    //print each channel
    for (int chan = 0; chan < N; chan++ )
    {
        pc.printf(channelData[chan]);
        pc.printf(", ");
    }
    
    //print end of line
    pc.printf("\n");
    
};


//write as binary each channel's data
//   print channels 1-N (where N is 1-8...anything else will return with no action)
//   sampleNumber is a number that, if greater than zero, will be printed at the start of the line
int32 val;
byte *val_ptr = (byte *)(&val);
void ADS1299Manager::writeChannelDataAsBinary(int N, long sampleNumber)
{
    //check the inputs
    if ((N < 1) || (N > 8)) return;
    
    // Write header
    pc.printf( (byte) PCKT_START);
    //byte byte_val = (1+8)*4;
    pc.printf((1+N)*4);  //length of data payload, bytes
    
    
        
    //print the sample number, if not disabled
    //val = sampleNumber;
    val = 1L;   
    //ptr = (uint8_t *)(&val);  //pretend that it is a string buffer so that Serial.write works easier
    //if (sampleNumber >= 0) {
        pc.printf(val_ptr,4); //4 bytes long
    //}

    //print each channel
    for (int chan = 0; chan < N; chan++ )
    {
        val = (long)(channelData[chan]);
        pc.printf(val_ptr,4); //4 bytes long
    }
    
    // Write footer
    pc.printf((byte)PCKT_END);
    
    // force everything out
    //Serial.flush();   
};

//write channel data using binary format of ModularEEG so that it can be used by BrainBay (P2 protocol)
//this only sends 6 channels of data, per the P2 protocol
//http://www.shifz.org/brainbay/manuals/brainbay_developer_manual.pdf
#define max_int16 (32767)
#define min_int16 (-32767)
void ADS1299Manager::writeChannelDataAsOpenEEG_P2(long sampleNumber) {
    ADS1299Manager::writeChannelDataAsOpenEEG_P2(sampleNumber,false);
}
#define synthetic_amplitude_counts (8950)   //counts peak-to-peak...should be 100 uV pk-pk  (100e-6 / (4.5 / 24 / 2^24))
void ADS1299Manager::writeChannelDataAsOpenEEG_P2(long sampleNumber,boolean useSyntheticData) {
    byte sync0 = 0xA5;
    byte sync1 = 0x5A;
    byte version = 2;
    
    pc.printf(sync0);
    pc.printf(sync1);
    pc.printf(version);
    pc.printf((byte) sampleNumber);
    
    long val32; //32-bit
    int val_i16;  //16-bit
    unsigned int val_u16;  //16-bit
    byte *val16_ptr = (byte *)(&val_u16);  //points to the memory for the variable above
    for (int chan = 0; chan < 6; chan++ )
    {
        //get this channel's data
        if (useSyntheticData) {
            //generate 10 uV pk-pk signal
            long time_samp_255 = (long)((sampleNumber) & (0x000000FF));  //make an 8-bit ramp waveform
            time_samp_255 = (long)((time_samp_255*(long)(chan+1)) & (0x000000FF)); //each channel is faster than the previous
            time_samp_255 += 256L*2L;  //make zero mean...empirically tuned via BrainBay visualization
            val32 = (synthetic_amplitude_counts * time_samp_255) / 255L; //scaled zero-mean ramp 
        } else {
            //get the real EEG data for this channel
            val32 = channelData[chan];
        }           
                        
        //prepare the value for transmission
        val32 = val32 / (32);  //shrink to fit within a 16-bit number
        val32 = constrain(val32,min_int16,max_int16);  //constrain to fit in 16 bits
        val_u16 = (unsigned int) (val32 & (0x0000FFFF));  //truncate and cast
    
        //pc.printf(val16_ptr,2); //low byte than high byte on Arduino
        pc.printf((byte)((val_u16 >> 8) & 0x00FF)); //high byte
        pc.printf((byte)(val_u16 & 0x00FF)); //low byte
        
    }
    byte switches = 0b00000000;  //the last thing required by the P2 data protocol
    pc.printf(switches);
}
    


//print out the state of all the control registers
void ADS1299Manager::printAllRegisters(void)   
{
    boolean prevVerboseState = verbose;
    
        verbose = true;
        ADS1299::RREGS(0x00,0x10);     // write the first registers
        delay(100);  //stall to let all that data get read by the PC
        ADS1299::RREGS(0x11,0x17-0x11);     // write the rest
        verbose = prevVerboseState;
}

//only use SRB1 if all use_SRB2 are set to false
boolean ADS1299Manager::use_SRB1(void) {
    for (int Ichan=0; Ichan < OPENBCI_NCHAN; Ichan++) {
        if (use_SRB2[Ichan]) {
            return false;
        }
    }
    return true;
}