Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: ADS1299Manager.cpp
- Revision:
- 0:675506e540be
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299Manager.cpp Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,323 @@
+
+
+
+#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;
+}
+
\ No newline at end of file