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.
Revision 0:675506e540be, committed 2015-03-23
- Comitter:
- biomurph
- Date:
- Mon Mar 23 19:22:04 2015 +0000
- Commit message:
- Publishing this old ADS1299 code for the first time!
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299.cpp Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,330 @@
+//
+// ADS1299.cpp mbed LIBRARY FOR COMMUNICATING WITH ADS1299
+//
+// Created by Joel Murphy. Fall, 2013 Ported from Arduino library by Conor, Joel, and Luke, Summer 2013
+//
+
+
+#include "ADS1299.h"
+
+void ADS1299::initialize(){
+ DRDY = _DRDY;
+ CS = _CS;
+ int FREQ = _FREQ;
+ int RST = _RST;
+ DigitalOut CS (PTD0); // arduino pin 10 - chip select
+ DigitalOut RST(PTD5); // arduino pin 9 - reset pin
+ DigitalIn DRDY(PTA13); // arduino pin 8 - data ready pin
+
+ delay(50); // recommended power up sequence requiers Tpor (~32mS)
+ pinMode(RST,OUTPUT);
+ wait_ms(50); // wait Tpor
+ RST = 0; // put the ADS into reset mode
+ wait_us(8); // wait Trst
+ RST = 1; // take ADS out of reset mode
+ wait_us(20); // advertised to wait 18 Tclk before operation
+
+
+
+}
+
+//System Commands
+void ADS1299::WAKEUP() {
+ digitalWrite(CS, LOW);
+ transfer(_WAKEUP);
+ digitalWrite(CS, HIGH);
+ delayMicroseconds(3); //must wait 4 tCLK cycles before sending another command (Datasheet, pg. 35)
+}
+
+void ADS1299::STANDBY() { // only allowed to send WAKEUP after sending STANDBY
+ digitalWrite(CS, LOW);
+ transfer(_STANDBY);
+ digitalWrite(CS, HIGH);
+}
+
+void ADS1299::RESET() { // reset all the registers to default settings
+ digitalWrite(CS, LOW);
+ transfer(_RESET);
+ delayMicroseconds(12); //must wait 18 tCLK cycles to execute this command (Datasheet, pg. 35)
+ digitalWrite(CS, HIGH);
+}
+
+void ADS1299::START() { //start data conversion
+ digitalWrite(CS, LOW);
+ transfer(_START);
+ digitalWrite(CS, HIGH);
+}
+
+void ADS1299::STOP() { //stop data conversion
+ digitalWrite(CS, LOW);
+ transfer(_STOP);
+ digitalWrite(CS, HIGH);
+}
+
+void ADS1299::RDATAC() {
+ digitalWrite(CS, LOW);
+ transfer(_RDATAC);
+ digitalWrite(CS, HIGH);
+ delayMicroseconds(3);
+}
+void ADS1299::SDATAC() {
+ digitalWrite(CS, LOW);
+ transfer(_SDATAC);
+ digitalWrite(CS, HIGH);
+ delayMicroseconds(3); //must wait 4 tCLK cycles after executing this command (Datasheet, pg. 37)
+}
+
+
+// Register Read/Write Commands
+byte ADS1299::getDeviceID() { // simple hello world com check
+ byte data = RREG(0x00);
+ if(verbose){ // verbose otuput
+ Serial.print(F("Device ID "));
+ printHex(data);
+ }
+ return data;
+}
+
+byte ADS1299::RREG(byte _address) { // reads ONE register at _address
+ byte opcode1 = _address + 0x20; // RREG expects 001rrrrr where rrrrr = _address
+ digitalWrite(CS, LOW); // open SPI
+ transfer(opcode1); // opcode1
+ transfer(0x00); // opcode2
+ regData[_address] = transfer(0x00);// update mirror location with returned byte
+ digitalWrite(CS, HIGH); // close SPI
+ if (verbose){ // verbose output
+ printRegisterName(_address);
+ printHex(_address);
+ Serial.print(", ");
+ printHex(regData[_address]);
+ Serial.print(", ");
+ for(byte j = 0; j<8; j++){
+ Serial.print(bitRead(regData[_address], 7-j));
+ if(j!=7) Serial.print(", ");
+ }
+
+ Serial.println();
+ }
+ return regData[_address]; // return requested register value
+}
+
+// Read more than one register starting at _address
+void ADS1299::RREGS(byte _address, byte _numRegistersMinusOne) {
+// for(byte i = 0; i < 0x17; i++){
+// regData[i] = 0; // reset the regData array
+// }
+ byte opcode1 = _address + 0x20; // RREG expects 001rrrrr where rrrrr = _address
+ digitalWrite(CS, LOW); // open SPI
+ transfer(opcode1); // opcode1
+ transfer(_numRegistersMinusOne); // opcode2
+ for(int i = 0; i <= _numRegistersMinusOne; i++){
+ regData[_address + i] = transfer(0x00); // add register byte to mirror array
+ }
+ digitalWrite(CS, HIGH); // close SPI
+ if(verbose){ // verbose output
+ for(int i = 0; i<= _numRegistersMinusOne; i++){
+ printRegisterName(_address + i);
+ printHex(_address + i);
+ Serial.print(", ");
+ printHex(regData[_address + i]);
+ Serial.print(", ");
+ for(int j = 0; j<8; j++){
+ Serial.print(bitRead(regData[_address + i], 7-j));
+ if(j!=7) Serial.print(", ");
+ }
+ Serial.println();
+ }
+ }
+
+}
+
+void ADS1299::WREG(byte _address, byte _value) { // Write ONE register at _address
+ byte opcode1 = _address + 0x40; // WREG expects 010rrrrr where rrrrr = _address
+ digitalWrite(CS, LOW); // open SPI
+ transfer(opcode1); // Send WREG command & address
+ transfer(0x00); // Send number of registers to read -1
+ transfer(_value); // Write the value to the register
+ digitalWrite(CS, HIGH); // close SPI
+ regData[_address] = _value; // update the mirror array
+ if(verbose){ // verbose output
+ Serial.print(F("Register "));
+ printHex(_address);
+ Serial.println(F(" modified."));
+ }
+}
+
+void ADS1299::WREGS(byte _address, byte _numRegistersMinusOne) {
+ byte opcode1 = _address + 0x40; // WREG expects 010rrrrr where rrrrr = _address
+ digitalWrite(CS, LOW); // open SPI
+ transfer(opcode1); // Send WREG command & address
+ transfer(_numRegistersMinusOne); // Send number of registers to read -1
+ for (int i=_address; i <=(_address + _numRegistersMinusOne); i++){
+ transfer(regData[i]); // Write to the registers
+ }
+ digitalWrite(CS,HIGH); // close SPI
+ if(verbose){
+ Serial.print(F("Registers "));
+ printHex(_address); Serial.print(F(" to "));
+ printHex(_address + _numRegistersMinusOne);
+ Serial.println(F(" modified"));
+ }
+}
+
+
+void ADS1299::updateChannelData(){
+ byte inByte;
+ digitalWrite(CS, LOW); // open SPI
+ stat = transfer(0x00); // read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+ stat = transfer(0x00); // read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+ stat = transfer(0x00); // read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+ for(int i = 0; i<8; i++){
+ for(int j=0; j<3; j++){ // read 24 bits of channel data in 8 3 byte chunks
+ inByte = transfer(0x00);
+ channelData[i] = (channelData[i]<<8) | inByte;
+ }
+ }
+ digitalWrite(CS, HIGH); // close SPI
+
+ for(int i=0; i<8; i++){ // convert 3 byte 2's compliment to 4 byte 2's compliment
+ if(bitRead(channelData[i],23) == 1){
+ channelData[i] |= 0xFF000000;
+ }else{
+ channelData[i] &= 0x00FFFFFF;
+ }
+ }
+// if(verbose){
+// Serial.print(stat); Serial.print(", ");
+// for(int i=0; i<8; i++){
+// Serial.print(channelData[i]);
+// if(i<7){Serial.print(", ");}
+// }
+// Serial.println();
+// }
+}
+
+
+
+
+void ADS1299::RDATA() { // use in Stop Read Continuous mode when DRDY goes low
+ byte inByte; // to read in one sample of the channels
+ digitalWrite(CS, LOW); // open SPI
+ transfer(_RDATA); // send the RDATA command
+ stat = transfer(0x00); // read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+ for(int i = 0; i<8; i++){
+ for(int j=0; j<3; j++){ // read in the status register and new channel data
+ inByte = transfer(0x00);
+ channelData[i] = (channelData[i]<<8) | inByte;
+ }
+ }
+ digitalWrite(CS, HIGH); // close SPI
+
+ for(int i=0; i<8; i++){
+ if(bitRead(channelData[i],23) == 1){ // convert 3 byte 2's compliment to 4 byte 2's compliment
+ channelData[i] |= 0xFF000000;
+ }else{
+ channelData[i] &= 0x00FFFFFF;
+ }
+ }
+
+}
+
+
+// String-Byte converters for RREG and WREG
+void ADS1299::printRegisterName(byte _address) {
+ if(_address == ID){
+ Serial.print(F("ID, ")); //the "F" macro loads the string directly from Flash memory, thereby saving RAM
+ }
+ else if(_address == CONFIG1){
+ Serial.print(F("CONFIG1, "));
+ }
+ else if(_address == CONFIG2){
+ Serial.print(F("CONFIG2, "));
+ }
+ else if(_address == CONFIG3){
+ Serial.print(F("CONFIG3, "));
+ }
+ else if(_address == LOFF){
+ Serial.print(F("LOFF, "));
+ }
+ else if(_address == CH1SET){
+ Serial.print(F("CH1SET, "));
+ }
+ else if(_address == CH2SET){
+ Serial.print(F("CH2SET, "));
+ }
+ else if(_address == CH3SET){
+ Serial.print(F("CH3SET, "));
+ }
+ else if(_address == CH4SET){
+ Serial.print(F("CH4SET, "));
+ }
+ else if(_address == CH5SET){
+ Serial.print(F("CH5SET, "));
+ }
+ else if(_address == CH6SET){
+ Serial.print(F("CH6SET, "));
+ }
+ else if(_address == CH7SET){
+ Serial.print(F("CH7SET, "));
+ }
+ else if(_address == CH8SET){
+ Serial.print(F("CH8SET, "));
+ }
+ else if(_address == BIAS_SENSP){
+ Serial.print(F("BIAS_SENSP, "));
+ }
+ else if(_address == BIAS_SENSN){
+ Serial.print(F("BIAS_SENSN, "));
+ }
+ else if(_address == LOFF_SENSP){
+ Serial.print(F("LOFF_SENSP, "));
+ }
+ else if(_address == LOFF_SENSN){
+ Serial.print(F("LOFF_SENSN, "));
+ }
+ else if(_address == LOFF_FLIP){
+ Serial.print(F("LOFF_FLIP, "));
+ }
+ else if(_address == LOFF_STATP){
+ Serial.print(F("LOFF_STATP, "));
+ }
+ else if(_address == LOFF_STATN){
+ Serial.print(F("LOFF_STATN, "));
+ }
+ else if(_address == GPIO){
+ Serial.print(F("GPIO, "));
+ }
+ else if(_address == MISC1){
+ Serial.print(F("MISC1, "));
+ }
+ else if(_address == MISC2){
+ Serial.print(F("MISC2, "));
+ }
+ else if(_address == CONFIG4){
+ Serial.print(F("CONFIG4, "));
+ }
+}
+
+//SPI communication methods
+byte ADS1299::transfer(byte _data) {
+ cli();
+ SPDR = _data;
+ while (!(SPSR & _BV(SPIF)))
+ ;
+ sei();
+ return SPDR;
+}
+
+// Used for printing HEX in verbose feedback mode
+void ADS1299::printHex(byte _data){
+ Serial.print("0x");
+ if(_data < 0x10) Serial.print("0");
+ Serial.print(_data, HEX);
+}
+
+//-------------------------------------------------------------------//
+//-------------------------------------------------------------------//
+//-------------------------------------------------------------------//
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ADS1299.h Mon Mar 23 19:22:04 2015 +0000 @@ -0,0 +1,74 @@ +// +// ADS1299.h +// Created by Joel Murphy, Summer 2K13 +// Ported from Conor Russomanno's Arduino library +// +// + +#ifndef _ADS1299_h +#define _ADS1299_h +#include "mbed.h" + +//SPI Command Definition Byte Assignments (Datasheet, p35) +#define _WAKEUP 0x02 // Wake-up from standby mode +#define _STANDBY 0x04 // Enter Standby mode +#define _RESET 0x06 // Reset the device +#define _START 0x08 // Start and restart (synchronize) conversions +#define _STOP 0x0A // Stop conversion +#define _RDATAC 0x10 // Enable Read Data Continuous mode (default mode at power-up) +#define _SDATAC 0x11 // Stop Read Data Continuous mode +#define _RDATA 0x12 // Read data by command; supports multiple read back +//#define _RREG 0x20 // (00100000) is the first opcode that the address must be added to for RREG communication +//#define _WREG 0x40 // 01000000 in binary (Datasheet, p35) + +//Register Addresses +#define ID 0x00 +#define CONFIG1 0x01 +#define CONFIG2 0x02 +#define CONFIG3 0x03 +#define LOFF 0x04 +#define CH1SET 0x05 +#define CH2SET 0x06 +#define CH3SET 0x07 +#define CH4SET 0x08 +#define CH5SET 0x09 +#define CH6SET 0x0A +#define CH7SET 0x0B +#define CH8SET 0x0C +#define BIAS_SENSP 0x0D +#define BIAS_SENSN 0x0E +#define LOFF_SENSP 0x0F +#define LOFF_SENSN 0x10 +#define LOFF_FLIP 0x11 +#define LOFF_STATP 0x12 +#define LOFF_STATN 0x13 +#define GPIO 0x14 +#define MISC1 0x15 +#define MISC2 0x16 +#define CONFIG4 0x17 + + void WAKEUP(); + void STANDBY(); + void RESET(); + void START(); + void STOP(); + //Data Read Commands + void RDATAC(); + void SDATAC(); + void RDATA(); + //Register Read/Write Commands + char getDeviceID(); + char RREG(char _address); + void RREGS(char _address, char _numRegistersMinusOne); + void printRegisterName(char _address); + void WREG(char _address, char _value); + void WREGS(char _address, char _numRegistersMinusOne); + void printHex(char _data); + void updateChannelData(); + + int stat; // used to hold the status register + char regData [24]; // array used when reading register data + long channelData [8]; // array used when reading channel data + bool verbose; // turn on/off Serial feedback + +#endif
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299Manager.h Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,83 @@
+
+//
+// ADS1299Manager.h
+// Part of the Arduino Library for the ADS1299 Shield
+// Created by Chip Audette, Fall 2013
+//
+
+
+#ifndef ____ADS1299Manager__
+#define ____ADS1299Manager__
+
+#include <ADS1299.h>
+
+//Pick which version of OpenBCI you have
+#define OPENBCI_V1 (1) //Sept 2013
+#define OPENBCI_V2 (2) //Oct 24, 2013
+#define OPENBCI_NCHAN (8) // number of EEG channels
+
+/* Arduino Uno - Pin Assignments
+ SCK = 13
+ MISO [DOUT] = 12
+ MOSI [DIN] = 11
+ CS = 10;
+ RESET = 9;
+ DRDY = 8;
+*/
+#define PIN_DRDY (8)
+#define PIN_RST (9)
+#define PIN_CS (10)
+#define SCK_MHZ (4)
+
+//gainCode choices
+#define ADS_GAIN01 (0b00000000)
+#define ADS_GAIN02 (0b00010000)
+#define ADS_GAIN04 (0b00100000)
+#define ADS_GAIN06 (0b00110000)
+#define ADS_GAIN08 (0b01000000)
+#define ADS_GAIN12 (0b01010000)
+#define ADS_GAIN24 (0b01100000)
+
+//inputCode choices
+#define ADSINPUT_NORMAL (0b00000000)
+#define ADSINPUT_SHORTED (0b00000001)
+#define ADSINPUT_TESTSIG (0b00000101)
+
+//test signal choices...ADS1299 datasheet page 41
+#define ADSTESTSIG_AMP_1X (0b00000000)
+#define ADSTESTSIG_AMP_2X (0b00000100)
+#define ADSTESTSIG_PULSE_SLOW (0b00000000)
+#define ADSTESTSIG_PULSE_FAST (0b00000001)
+#define ADSTESTSIG_DCSIG (0b00000011)
+#define ADSTESTSIG_NOCHANGE (0b11111111)
+
+//binary communication codes for each packet
+#define PCKT_START 0xA0
+#define PCKT_END 0xC0
+
+class ADS1299Manager : public ADS1299 {
+ public:
+ void initialize(void); //initialize the ADS1299 controller. Call once. Assumes OpenBCI_V2
+ void initialize(int version); //initialize the ADS1299 controller. Call once. Set which version of OpenBCI you're using.
+ void setVersionOpenBCI(int version); //Set which version of OpenBCI you're using.
+ void reset(void); //reset all the ADS1299's settings. Call however you'd like
+ void activateChannel(int N, byte gainCode,byte inputCode); //setup the channel 1-8
+ void deactivateChannel(int N); //disable given channel 1-8
+ void configureInternalTestSignal(byte amplitudeCode, byte freqCode);
+ void start(void);
+ void stop(void);
+ int isDataAvailable(void);
+ void printChannelDataAsText(int N, long int sampleNumber);
+ void writeChannelDataAsBinary(int N, long int sampleNumber);
+ void writeChannelDataAsOpenEEG_P2(long int sampleNumber);
+ void writeChannelDataAsOpenEEG_P2(long int sampleNumber, boolean useSyntheticData);
+ void printAllRegisters(void);
+ void setSRB1(boolean desired_state);
+
+ private:
+ boolean use_neg_inputs;
+ boolean use_SRB2[OPENBCI_NCHAN];
+ boolean use_SRB1(void);
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad.cpp Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,165 @@
+//
+// Biquad.cpp
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+
+#include <math.h>
+#include "Biquad.h"
+
+Biquad::Biquad() {
+ type = bq_type_lowpass;
+ a0 = 1.0;
+ a1 = a2 = b1 = b2 = 0.0;
+ Fc = 0.50;
+ Q = 0.707;
+ peakGain = 0.0;
+ z1 = z2 = 0.0;
+}
+
+Biquad::Biquad(int type, double Fc, double Q, double peakGainDB) {
+ setBiquad(type, Fc, Q, peakGainDB);
+ z1 = z2 = 0.0;
+}
+
+Biquad::~Biquad() {
+}
+
+void Biquad::setType(int type) {
+ this->type = type;
+ calcBiquad();
+}
+
+void Biquad::setQ(double Q) {
+ this->Q = Q;
+ calcBiquad();
+}
+
+void Biquad::setFc(double Fc) {
+ this->Fc = Fc;
+ calcBiquad();
+}
+
+void Biquad::setPeakGain(double peakGainDB) {
+ this->peakGain = peakGainDB;
+ calcBiquad();
+}
+
+void Biquad::setBiquad(int type, double Fc, double Q, double peakGainDB) {
+ this->type = type;
+ this->Q = Q;
+ this->Fc = Fc;
+ setPeakGain(peakGainDB);
+}
+
+void Biquad::calcBiquad(void) {
+ double norm;
+ double V = pow(10, fabs(peakGain) / 20.0);
+ double K = tan(M_PI * Fc);
+ switch (this->type) {
+ case bq_type_lowpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = K * K * norm;
+ a1 = 2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_highpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = 1 * norm;
+ a1 = -2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_bandpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = K / Q * norm;
+ a1 = 0;
+ a2 = -a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_notch:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = (1 + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = a0;
+ b1 = a1;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_peak:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + 1/Q * K + K * K);
+ a0 = (1 + V/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - V/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - 1/Q * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + V/Q * K + K * K);
+ a0 = (1 + 1/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - 1/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - V/Q * K + K * K) * norm;
+ }
+ break;
+ case bq_type_lowshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
+ a1 = 2 * (V * K * K - 1) * norm;
+ a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (V * K * K - 1) * norm;
+ b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ }
+ break;
+ case bq_type_highshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (V + sqrt(2*V) * K + K * K) * norm;
+ a1 = 2 * (K * K - V) * norm;
+ a2 = (V - sqrt(2*V) * K + K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (V + sqrt(2*V) * K + K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (K * K - V) * norm;
+ b2 = (V - sqrt(2*V) * K + K * K) * norm;
+ }
+ break;
+ }
+
+ return;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad.h Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,60 @@
+//
+// Biquad.h
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/25/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+
+#ifndef Biquad_h
+#define Biquad_h
+
+enum {
+ bq_type_lowpass = 0,
+ bq_type_highpass,
+ bq_type_bandpass,
+ bq_type_notch,
+ bq_type_peak,
+ bq_type_lowshelf,
+ bq_type_highshelf
+};
+
+class Biquad {
+public:
+ Biquad();
+ Biquad(int type, double Fc, double Q, double peakGainDB);
+ ~Biquad();
+ void setType(int type);
+ void setQ(double Q);
+ void setFc(double Fc);
+ void setPeakGain(double peakGainDB);
+ void setBiquad(int type, double Fc, double Q, double peakGain);
+ float process(float in);
+
+protected:
+ void calcBiquad(void);
+
+ int type;
+ double a0, a1, a2, b1, b2;
+ double Fc, Q, peakGain;
+ double z1, z2;
+};
+
+inline float Biquad::process(float in) {
+ double out = in * a0 + z1;
+ z1 = in * a1 + z2 - b1 * out;
+ z2 = in * a2 - b2 * out;
+ return out;
+}
+
+#endif // Biquad_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad_multiChan.cpp Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,180 @@
+//
+// Biquad.cpp
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+// Extended by Chip Audette to handle multiple channels of data
+// that are being filtered by the same coefficients
+//
+
+#include <math.h>
+#include "Biquad_multiChan.h"
+
+
+/*
+Biquad_multiChan::Biquad_multiChan() {
+ type = bq_type_lowpass;
+ a0 = 1.0;
+ a1 = a2 = b1 = b2 = 0.0;
+ Fc = 0.50;
+ Q = 0.707;
+ peakGain = 0.0;
+ z1 = z2 = 0.0;
+}
+*/
+
+Biquad_multiChan::Biquad_multiChan(int N,int type, double Fc, double Q, double peakGainDB) {
+ setBiquad(type, Fc, Q, peakGainDB);
+
+ Nchan = N;
+ z1 = new double[Nchan];
+ z2 = new double[Nchan];
+ for (int Ichan=0;Ichan<Nchan;Ichan++) {
+ z1[Ichan]=0.0;
+ z2[Ichan]=0.0;
+ }
+}
+
+Biquad_multiChan::~Biquad_multiChan() {
+ delete z2;
+ delete z1;
+}
+
+void Biquad_multiChan::setType(int type) {
+ this->type = type;
+ calcBiquad();
+}
+
+void Biquad_multiChan::setQ(double Q) {
+ this->Q = Q;
+ calcBiquad();
+}
+
+void Biquad_multiChan::setFc(double Fc) {
+ this->Fc = Fc;
+ calcBiquad();
+}
+
+void Biquad_multiChan::setPeakGain(double peakGainDB) {
+ this->peakGain = peakGainDB;
+ calcBiquad();
+}
+
+void Biquad_multiChan::setBiquad(int type, double Fc, double Q, double peakGainDB) {
+ this->type = type;
+ this->Q = Q;
+ this->Fc = Fc;
+ setPeakGain(peakGainDB);
+}
+
+void Biquad_multiChan::calcBiquad(void) {
+ double norm;
+ double V = pow(10, fabs(peakGain) / 20.0);
+ double K = tan(M_PI * Fc);
+ switch (this->type) {
+ case bq_type_lowpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = K * K * norm;
+ a1 = 2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_highpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = 1 * norm;
+ a1 = -2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_bandpass:
+ norm = 1.0 / (1.0 + K / Q + K * K);
+ a0 = K / Q * norm;
+ a1 = 0.0;
+ a2 = -a0;
+ b1 = 2.0 * (K * K - 1.0) * norm;
+ b2 = (1.0 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_notch:
+ norm = 1.0 / (1.0 + K / Q + K * K);
+ a0 = (1 + K * K) * norm;
+ a1 = 2.0 * (K * K - 1) * norm;
+ a2 = a0;
+ b1 = a1;
+ b2 = (1.0 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_peak:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + 1/Q * K + K * K);
+ a0 = (1 + V/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - V/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - 1/Q * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + V/Q * K + K * K);
+ a0 = (1 + 1/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - 1/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - V/Q * K + K * K) * norm;
+ }
+ break;
+ case bq_type_lowshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
+ a1 = 2 * (V * K * K - 1) * norm;
+ a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (V * K * K - 1) * norm;
+ b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ }
+ break;
+ case bq_type_highshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (V + sqrt(2*V) * K + K * K) * norm;
+ a1 = 2 * (K * K - V) * norm;
+ a2 = (V - sqrt(2*V) * K + K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (V + sqrt(2*V) * K + K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (K * K - V) * norm;
+ b2 = (V - sqrt(2*V) * K + K * K) * norm;
+ }
+ break;
+ }
+
+ return;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad_multiChan.h Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,64 @@
+//
+// Biquad_multiChan.h
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/25/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+// Extended by Chip Audette (Nov 2013) to handle multiple channels of data
+// that are being filtered by the same coefficients
+//
+
+#ifndef Biquad_multiChan_h
+#define Biquad_multiChan_h
+
+enum {
+ bq_type_lowpass = 0,
+ bq_type_highpass,
+ bq_type_bandpass,
+ bq_type_notch,
+ bq_type_peak,
+ bq_type_lowshelf,
+ bq_type_highshelf
+};
+
+class Biquad_multiChan {
+public:
+ //Biquad_multiChan();
+ Biquad_multiChan(int Nchan, int type, double Fc, double Q, double peakGainDB);
+ ~Biquad_multiChan();
+ void setType(int type);
+ void setQ(double Q);
+ void setFc(double Fc);
+ void setPeakGain(double peakGainDB);
+ void setBiquad(int type, double Fc, double Q, double peakGain);
+ float process(float in,int Ichan);
+
+protected:
+ void calcBiquad(void);
+
+ int Nchan;
+ int type;
+ double a0, a1, a2, b1, b2;
+ double Fc, Q, peakGain;
+ double *z1, *z2;
+};
+
+inline float Biquad_multiChan::process(float in,int Ichan) {
+ double out = in * a0 + z1[Ichan];
+ z1[Ichan] = in * a1 + z2[Ichan] - b1 * out;
+ z2[Ichan] = in * a2 - b2 * out;
+ return out;
+}
+
+#endif // Biquad_multiChan_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,351 @@
+
+#include "mbed.h"
+#include "ADS1299.h"
+
+Serial pc(USBTX, USBRX); // set up the COM
+SPI spi (PTD2,PTD3,PTD1); // mosi, miso, sck
+
+
+//define how I'd like my channels setup
+#define MAX_N_CHANNELS (8) //must be less than or equal to length of channelData in ADS1299 object!!
+int nActiveChannels = 8; //how many active channels would I like?
+byte gainCode = ADS_GAIN24; //how much gain do I want
+byte inputType = ADSINPUT_NORMAL; //here's the normal way to setup the channels
+//byte inputType = ADSINPUT_SHORTED; //here's another way to setup the channels
+//byte inputType = ADSINPUT_TESTSIG; //here's a third way to setup the channels
+
+//other variables
+long sampleCounter = 0; // used to time the tesing loop
+boolean is_running = false; // this flag is set in serialEvent on reciept of prompt
+#define PIN_STARTBINARY (7) //pull this pin to ground to start binary transfer
+//define PIN_STARTBINARY_OPENEEG (6)
+boolean startBecauseOfPin = false;
+boolean startBecauseOfSerial = false;
+
+#define OUTPUT_NOTHING (0)
+#define OUTPUT_TEXT (1)
+#define OUTPUT_BINARY (2)
+#define OUTPUT_BINARY_4CHAN (4)
+#define OUTPUT_BINARY_OPENEEG (6)
+#define OUTPUT_BINARY_OPENEEG_SYNTHETIC (7)
+int outputType;
+
+//Design filters (This BIQUAD class requires ~6K of program space! Ouch.)
+//For frequency response of these filters: http://www.earlevel.com/main/2010/12/20/biquad-calculator/
+#include <Biquad_multiChan.h> //modified from this source code: http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+#define SAMPLE_RATE_HZ (250.0) //default setting for OpenBCI
+#define FILTER_Q (0.5) //critically damped is 0.707 (Butterworth)
+#define FILTER_PEAK_GAIN_DB (0.0) //we don't want any gain in the passband
+#define HP_CUTOFF_HZ (0.5) //set the desired cutoff for the highpass filter
+Biquad_multiChan stopDC_filter(MAX_N_CHANNELS,bq_type_highpass,HP_CUTOFF_HZ / SAMPLE_RATE_HZ, FILTER_Q, FILTER_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+//Biquad_multiChan stopDC_filter(MAX_N_CHANNELS,bq_type_bandpass,10.0 / SAMPLE_RATE_HZ, 6.0, FILTER_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+#define NOTCH_FREQ_HZ (60.0)
+#define NOTCH_Q (4.0) //pretty shap notch
+#define NOTCH_PEAK_GAIN_DB (0.0) //doesn't matter for this filter type
+Biquad_multiChan notch_filter1(MAX_N_CHANNELS,bq_type_notch,NOTCH_FREQ_HZ / SAMPLE_RATE_HZ, NOTCH_Q, NOTCH_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+Biquad_multiChan notch_filter2(MAX_N_CHANNELS,bq_type_notch,NOTCH_FREQ_HZ / SAMPLE_RATE_HZ, NOTCH_Q, NOTCH_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+boolean useFilters = false;
+
+
+
+DigitalOut CS (PTD0); // arduino pin 10 - chip select
+DigitalOut RST(PTD5); // arduino pin 9 - reset pin
+DigitalIn DRDY(PTA13); // arduino pin 8 - data ready pin
+DigitalOut myled(LED3); // debugging LED CONFLICT WITH SPI & LED1
+
+
+
+int main() {
+
+ ADS1299Manager ADSManager;
+ int OpenBCI_version = OPENBCI_V2; //assume V2
+ ADSManager.initialize(OpenBCI_version); //must do this VERY early in the setup...preferably first
+
+ spi.format(8,1); // spi: 8 bit, mode 1
+ spi.frequency(1000000); // spi sck frequency
+
+ pc.baud(115200);
+ pc.printf("\nADS1299 Stream Raw Data\n");
+ wait(2.0);
+
+ pc.printf("ADS1299-Arduino UNO - Stream Raw Data"); //read the string from Flash to save RAM
+ pc.printf("Configured as OpenBCI_Version code = ");pc.printf(OpenBCI_version);
+ Serial.flush();
+
+ // setup the channels as desired on the ADS1299..set gain, input type, referece (SRB1), and patient bias signal
+ for (int chan=1; chan <= nActiveChannels; chan++) {
+ ADSManager.activateChannel(chan, gainCode, inputType);
+ }
+
+ //print state of all registers
+ ADSManager.printAllRegisters();Serial.flush();
+
+ // setup hardware to allow a jumper or button to start the digitaltransfer
+ pinMode(PIN_STARTBINARY,INPUT); digitalWrite(PIN_STARTBINARY,HIGH); //activate pullup
+ //pinMode(PIN_STARTBINARY_OPENEEG,INPUT); digitalWrite(PIN_STARTBINARY_OPENEEG,HIGH); //activate pullup
+
+ // tell the controlling program that we're ready to start!
+ pc.printf("Press '?' to query and print ADS1299 register settings again"); //read it straight from flash
+ pc.printf("Press 1-8 to disable EEG Channels, q-i to enable (all enabled by default)");
+ pc.printf("Press 'F' to enable filters. 'f' to disable filters (disabled by default)");
+ pc.printf("Press 'x' (text) or 'b' (binary) to begin streaming data...");
+
+boolean firstReport = true;
+unsigned long totalMicrosBusy = 0; //use this to count time
+
+ while(1) {
+
+ if (is_running) {
+
+ //is data ready?
+ while(!(ADSManager.isDataAvailable())){ // watch the DRDY pin
+ delayMicroseconds(100);
+ }
+ unsigned long start_micros = micros();
+
+ //get the data
+ ADSManager.updateChannelData(); // update the channelData array
+ sampleCounter++; // increment my sample counter
+
+ //Apply filers to the data
+ if (useFilters) applyFilters();
+
+ //print the data
+ //if ((sampleCounter % 1) == 0) {
+ switch (outputType) {
+ case OUTPUT_NOTHING:
+ //don't output anything...the Arduino is still collecting data from the OpenBCI board...just nothing is being done with it
+ break;
+ case OUTPUT_BINARY:
+ ADSManager.writeChannelDataAsBinary(8,sampleCounter); //print all channels, whether active or not
+ break;
+ case OUTPUT_BINARY_4CHAN:
+ ADSManager.writeChannelDataAsBinary(4,sampleCounter); //print all channels, whether active or not
+ break;
+ case OUTPUT_BINARY_OPENEEG:
+ ADSManager.writeChannelDataAsOpenEEG_P2(sampleCounter); //print all channels, whether active or not
+ break;
+ case OUTPUT_BINARY_OPENEEG_SYNTHETIC:
+ ADSManager.writeChannelDataAsOpenEEG_P2(sampleCounter,true); //print all channels, whether active or not
+ break;
+ default:
+ ADSManager.printChannelDataAsText(8,sampleCounter); //print all channels, whether active or not
+ }// end of if(is_running)
+ }// end of while(1)
+
+ if(pc.readable()) {
+ serialEvent();
+ }
+
+}// end of main()
+
+
+#define ACTIVATE_SHORTED (2)
+#define ACTIVATE (1)
+#define DEACTIVATE (0)
+void serialEvent(){ //
+ char inChar = pc.getc();
+ switch (inChar)
+ {
+ case '1':
+ changeChannelState_maintainRunningState(1,DEACTIVATE); break;
+ case '2':
+ changeChannelState_maintainRunningState(2,DEACTIVATE); break;
+ case '3':
+ changeChannelState_maintainRunningState(3,DEACTIVATE); break;
+ case '4':
+ changeChannelState_maintainRunningState(4,DEACTIVATE); break;
+ case '5':
+ changeChannelState_maintainRunningState(5,DEACTIVATE); break;
+ case '6':
+ changeChannelState_maintainRunningState(6,DEACTIVATE); break;
+ case '7':
+ changeChannelState_maintainRunningState(7,DEACTIVATE); break;
+ case '8':
+ changeChannelState_maintainRunningState(8,DEACTIVATE); break;
+ case 'q':
+ changeChannelState_maintainRunningState(1,ACTIVATE); break;
+ case 'w':
+ changeChannelState_maintainRunningState(2,ACTIVATE); break;
+ case 'e':
+ changeChannelState_maintainRunningState(3,ACTIVATE); break;
+ case 'r':
+ changeChannelState_maintainRunningState(4,ACTIVATE); break;
+ case 't':
+ changeChannelState_maintainRunningState(5,ACTIVATE); break;
+ case 'y':
+ changeChannelState_maintainRunningState(6,ACTIVATE); break;
+ case 'u':
+ changeChannelState_maintainRunningState(7,ACTIVATE); break;
+ case 'i':
+ changeChannelState_maintainRunningState(8,ACTIVATE); break;
+ case '0':
+ activateAllChannelsToTestCondition(ADSINPUT_SHORTED,ADSTESTSIG_NOCHANGE,ADSTESTSIG_NOCHANGE); break;
+ case '-':
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_SLOW); break;
+ case '+':
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); break;
+ case '=':
+ //repeat the line above...just for human convenience
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); break;
+ case 'p':
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_DCSIG); break;
+ case '[':
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_PULSE_SLOW); break;
+ case ']':
+ activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_PULSE_FAST); break;
+ case 'n':
+ toggleRunState(OUTPUT_NOTHING);
+ startBecauseOfSerial = is_running;
+ if (is_running) Serial.println(F("FRDM: Starting, but not outputing to PC..."));
+ break;
+ case 'b':
+ toggleRunState(OUTPUT_BINARY);
+ startBecauseOfSerial = is_running;
+ if (is_running) Serial.println(F("FRDM: Starting binary..."));
+ break;
+ case 'v':
+ toggleRunState(OUTPUT_BINARY_4CHAN);
+ startBecauseOfSerial = is_running;
+ if (is_running) Serial.println(F("FRDM: Starting binary 4-chan..."));
+ break;
+ case 's':
+ stopRunning();
+ startBecauseOfSerial = is_running;
+ break;
+ case 'x':
+ toggleRunState(OUTPUT_TEXT);
+ startBecauseOfSerial = is_running;
+ if (is_running) Serial.println(F("FRDM: Starting text..."));
+ break;
+ case 'f':
+ useFilters = false;
+ Serial.println(F("FRDM: disabling filters"));
+ break;
+ case 'F':
+ useFilters = true;
+ Serial.println(F("FRDM: enabaling filters"));
+ break;
+ case '?':
+ //print state of all registers
+ ADSManager.printAllRegisters();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+boolean toggleRunState(int OUT_TYPE)
+{
+ if (is_running) {
+ return stopRunning();
+ } else {
+ return startRunning(OUT_TYPE);
+ }
+}
+
+boolean stopRunning(void) {
+ ADSManager.stop(); // stop the data acquisition
+ is_running = false;
+ return is_running;
+}
+
+boolean startRunning(int OUT_TYPE) {
+ outputType = OUT_TYPE;
+ ADSManager.start(); //start the data acquisition
+ is_running = true;
+ return is_running;
+}
+
+int changeChannelState_maintainRunningState(int chan, int start)
+{
+ boolean is_running_when_called = is_running;
+ int cur_outputType = outputType;
+
+ //must stop running to change channel settings
+ stopRunning();
+ if (start == true) {
+ Serial.print("Activating channel ");
+ Serial.println(chan);
+ ADSManager.activateChannel(chan,gainCode,inputType);
+ } else {
+ Serial.print("Deactivating channel ");
+ Serial.println(chan);
+ ADSManager.deactivateChannel(chan);
+ }
+
+ //restart, if it was running before
+ if (is_running_when_called == true) {
+ startRunning(cur_outputType);
+ }
+}
+
+int activateAllChannelsToTestCondition(int testInputCode, byte amplitudeCode, byte freqCode)
+{
+ boolean is_running_when_called = is_running;
+ int cur_outputType = outputType;
+
+ //set the test signal to the desired state
+ ADSManager.configureInternalTestSignal(amplitudeCode,freqCode);
+
+ //must stop running to change channel settings
+ stopRunning();
+
+ //loop over all channels to change their state
+ for (int Ichan=1; Ichan <= 8; Ichan++) {
+ ADSManager.activateChannel(Ichan,gainCode,testInputCode); //Ichan must be [1 8]...it does not start counting from zero
+ }
+
+ //restart, if it was running before
+ if (is_running_when_called == true) {
+ startRunning(cur_outputType);
+ }
+}
+
+long int runningAve[MAX_N_CHANNELS];
+int applyFilters(void) {
+ //scale factor for these coefficients was 32768 = 2^15
+ const static long int a0 = 32360L; //16 bit shift?
+ const static long int a1 = -2L*a0;
+ const static long int a2 = a0;
+ const static long int b1 = -64718L; //this is a shift of 17 bits!
+ const static long int b2 = 31955L;
+ static long int z1[MAX_N_CHANNELS], z2[MAX_N_CHANNELS];
+ long int val_int, val_in_down9, val_out, val_out_down9;
+ float val;
+ for (int Ichan=0; Ichan < MAX_N_CHANNELS; Ichan++) {
+ switch (1) {
+ case 1:
+ //use BiQuad
+ val = (float) ADSManager.channelData[Ichan]; //get the stored value for this sample
+ val = stopDC_filter.process(val,Ichan); //apply DC-blocking filter
+ break;
+ case 2:
+ //do fixed point, 1st order running ave
+ val_int = ADSManager.channelData[Ichan]; //get the stored value for this sample
+ //runningAve[Ichan]=( ((512-1)*(runningAve[Ichan]>>2)) + (val_int>>2) )>>7; // fs/0.5Hz = ~512 points..9 bits
+ //runningAve[Ichan]=( ((256-1)*(runningAve[Ichan]>>2)) + (val_int>>2) )>>6; // fs/1.0Hz = ~256 points...8 bits
+ runningAve[Ichan]=( ((128-1)*(runningAve[Ichan]>>1)) + (val_int>>1) )>>6; // fs/2.0Hz = ~128 points...7 bits
+ val = (float)(val_int - runningAve[Ichan]); //remove the DC
+ break;
+// case 3:
+// val_in_down9 = ADSManager.channelData[Ichan] >> 9; //get the stored value for this sample...bring 24-bit value down to 16-bit
+// val_out = (val_in_down9 * a0 + (z1[Ichan]>>9)) >> (16-9); //8bits were already removed...results in 24-bit value
+// val_out_down9 = val_out >> 9; //remove eight bits to go from 24-bit down to 16 bit
+// z1[Ichan] = (val_in_down9 * a1 + (z2[Ichan] >> 9) - b1 * val_out_down9 ) >> (16-9); //8-bits were pre-removed..end in 24 bit number
+// z2[Ichan] = (val_in_down9 * a2 - b2 * val_out_down9) >> (16-9); //8-bits were pre-removed...end in 24-bit number
+// val = (float)val_out;
+// break;
+ }
+ val = notch_filter1.process(val,Ichan); //apply 60Hz notch filter
+ val = notch_filter2.process(val,Ichan); //apply it again
+ ADSManager.channelData[Ichan] = (long) val; //save the value back into the main data-holding object
+ }
+ return 0;
+}
+
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Mar 23 19:22:04 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/a9913a65894f \ No newline at end of file