Dan Allen
/
MAX86150_ECG_PPG
MAX86150 code
Fork of MAX86150_ECG_PPG by
main.cpp@0:74fff521858e, 2017-01-04 (annotated)
- Committer:
- laserdad
- Date:
- Wed Jan 04 17:00:04 2017 +0000
- Revision:
- 0:74fff521858e
- Child:
- 1:f62247cbeac6
Working version (all features--I think). I moved interrupt pin to d12 (which works). I sped up I2C to 1MHz (not recommended by mfg, but seems to be fine) for data taking, but left it at 400kHz for setting registers.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
laserdad | 0:74fff521858e | 1 | |
laserdad | 0:74fff521858e | 2 | //********************************************** Arduino code |
laserdad | 0:74fff521858e | 3 | /* This program configures and reads data from the MAX86150 ECG/PPG (BioZ to come) ASIC from Maxim Integrated. |
laserdad | 0:74fff521858e | 4 | * Originally writter for Arduino. The MAX86150r4 or MAX86150r8 boards work well (others are noisy). |
laserdad | 0:74fff521858e | 5 | * Keep ECG electrodes as short as possible. Due to the nature of this chip low frequency RC filters cannot be used. |
laserdad | 0:74fff521858e | 6 | * Note the ECG input oscillates when not loaded with human body impedance. |
laserdad | 0:74fff521858e | 7 | * ECG inputs have internal ESD protection. |
laserdad | 0:74fff521858e | 8 | * Configuration is done by the initMAX86150() subroutine. Register settings are per recommendations from JY Kim at Maxim. |
laserdad | 0:74fff521858e | 9 | * In the global declarations are several options for enabling 4 kinds of measurements: PPG (IR and R), ECG, and ETI. |
laserdad | 0:74fff521858e | 10 | * The flex FIFO will be configured to order the selected measurements exactly in that order: first PPG, then ECG, then ETI. |
laserdad | 0:74fff521858e | 11 | * If PPG is not enabled (ppgOn=0), then the first data point will be ECG. If ecg is not enabled then the only data input will be ETI. |
laserdad | 0:74fff521858e | 12 | * This program uses by default the A-full interrupt which alerts when the FIFO is almost full. It does not attempt to read the entire FIFO, |
laserdad | 0:74fff521858e | 13 | * but rather reads a fixed number of samples. This operation is important because the ASIC can't update the FIFO while we are in the middle |
laserdad | 0:74fff521858e | 14 | * of an I2C transaction, so we have to keep I2C transactions short. |
laserdad | 0:74fff521858e | 15 | * Note the program limits the number of samples which can be read according to a specified I2C buffer size which will depend on the HW and |
laserdad | 0:74fff521858e | 16 | * library you use. For Arduino the default is 32 bytes. |
laserdad | 0:74fff521858e | 17 | * ECG data and PPG data are different. PPG data is unipolar 18 bits and has garbage in the MSBs that has to be masked. |
laserdad | 0:74fff521858e | 18 | * ECG data is bipolar (2's complement), 17 bits and has to have the MSBs masked with FFF.. if the signal is negative. |
laserdad | 0:74fff521858e | 19 | * ECG data is displayed with a short IIR noise filter and longer IIR baseline removal filter. The filter length is specified in bits |
laserdad | 0:74fff521858e | 20 | * so the math can be done with integers using register shifting, rather than floats. |
laserdad | 0:74fff521858e | 21 | * This chip practically requires at least 400kHZ i2c speed for ECG readout. (I have a query in to Maxim to see if it unofficially supports 1MHz.) |
laserdad | 0:74fff521858e | 22 | * ETI is electrical tissue impedance, a few kHz sampling of impedance to check for presence of user's fingers. This is not a datasheet feature. |
laserdad | 0:74fff521858e | 23 | * ETI settings were delivered by Maxim. |
laserdad | 0:74fff521858e | 24 | * ECG comes too fast to read every EOC interrupt. Either read every few interrupts or use AFULL int. |
laserdad | 0:74fff521858e | 25 | * PPG data is measured at a much lower rate than ECG, but data is buffered through in the FIFO, showing the most recent sample. |
laserdad | 0:74fff521858e | 26 | * To reduce data rate it would be possible to only send PPG data when it changes. Otherwise ECG can be filtered down to the PPG data rate and |
laserdad | 0:74fff521858e | 27 | * filtered data sent at a the reduced bandwidth (what Maxim does on their BLE EV kit). |
laserdad | 0:74fff521858e | 28 | * |
laserdad | 0:74fff521858e | 29 | * IMPORTANT: Maxim recommends to read the FIFO pointer after getting data to make sure it changed. I have not implemented that yet. |
laserdad | 0:74fff521858e | 30 | * Basically, if you try to read the FIFO during the few clock cycles while it is being updated (a rare but eventual event), |
laserdad | 0:74fff521858e | 31 | * then the FIFO data will be garbage. You have to reread the FIFO. |
laserdad | 0:74fff521858e | 32 | * |
laserdad | 0:74fff521858e | 33 | * BTW I am not convinced it is possible to mix PPG and ETI in the same row of the flex FIFO. Eg. FD2= ECG, FD1 = PPG |
laserdad | 0:74fff521858e | 34 | * |
laserdad | 0:74fff521858e | 35 | * Note: IR and R LED power should be closed loop controlled to an optimal region above 90% of full scale for best resolution. |
laserdad | 0:74fff521858e | 36 | * (Not yet implemented). |
laserdad | 0:74fff521858e | 37 | * |
laserdad | 0:74fff521858e | 38 | * Note: PPG system uses sophisticated background subtraction to cancel ambient light flicker and large ambient light signals (sunlight). |
laserdad | 0:74fff521858e | 39 | * This is important for finger measurements, but less important for measurements indoors under the thigh, for example, where a generic |
laserdad | 0:74fff521858e | 40 | * light source and LED can be used in DC operation. |
laserdad | 0:74fff521858e | 41 | * |
laserdad | 0:74fff521858e | 42 | * Note the PPG ADC uses feedback cancellation from the current source to reduce noise. No LED current will result in noisy ADC readings. |
laserdad | 0:74fff521858e | 43 | * In other words you can't measure ADC noise without LED current. There are test registers not available to us which can disconnect the photodiode and |
laserdad | 0:74fff521858e | 44 | * measure with noise with test currents on the chip. |
laserdad | 0:74fff521858e | 45 | * |
laserdad | 0:74fff521858e | 46 | * Note ASIC has configurable on-chip PPG averaging if-desired (prior to being written to FIFO). |
laserdad | 0:74fff521858e | 47 | */ |
laserdad | 0:74fff521858e | 48 | #include "mbed.h" |
laserdad | 0:74fff521858e | 49 | #include "math.h" |
laserdad | 0:74fff521858e | 50 | //Register definitions |
laserdad | 0:74fff521858e | 51 | #define MAX86150_Addr 0xBC //updated per I2Cscanner, 8 bit version of 7 bit code 0x5E |
laserdad | 0:74fff521858e | 52 | #define InterruptStatusReg1 0x00 //Interrupt status byte 0 (read both bytes 0x00 and 0x01 when checking int status) |
laserdad | 0:74fff521858e | 53 | #define InterruptStatusReg2 0x01 |
laserdad | 0:74fff521858e | 54 | #define InterruptEnableReg1 0x02 //Interrupt enable byte 0 |
laserdad | 0:74fff521858e | 55 | #define InterruptEnableReg2 0x03 |
laserdad | 0:74fff521858e | 56 | #define FIFOWritePointerReg 0x04 |
laserdad | 0:74fff521858e | 57 | #define OverflowCounterReg 0x05 |
laserdad | 0:74fff521858e | 58 | #define FIFOReadPointerReg 0x06 |
laserdad | 0:74fff521858e | 59 | #define FIFODataReg 0x07 |
laserdad | 0:74fff521858e | 60 | #define FIFOConfigReg 0x08 |
laserdad | 0:74fff521858e | 61 | #define FIFODataControlReg1 0x09 |
laserdad | 0:74fff521858e | 62 | #define FIFODataControlReg2 0x0A |
laserdad | 0:74fff521858e | 63 | #define SystemControlReg 0x0D |
laserdad | 0:74fff521858e | 64 | #define ppgConfigReg0 0x0E |
laserdad | 0:74fff521858e | 65 | #define ppgConfigReg1 0x0F |
laserdad | 0:74fff521858e | 66 | #define ProxIntThreshReg 0x10 |
laserdad | 0:74fff521858e | 67 | #define LED1PulseAmpReg 0x11 |
laserdad | 0:74fff521858e | 68 | #define LED2PulseAmpReg 0x12 |
laserdad | 0:74fff521858e | 69 | #define LEDRangeReg 0x14 |
laserdad | 0:74fff521858e | 70 | #define LEDPilotPAReg 0x15 |
laserdad | 0:74fff521858e | 71 | #define EcgConfigReg1 0x3C |
laserdad | 0:74fff521858e | 72 | #define EcgConfigReg2 0x3D |
laserdad | 0:74fff521858e | 73 | #define EcgConfigReg3 0x3E |
laserdad | 0:74fff521858e | 74 | #define EcgConfigReg4 0x3F |
laserdad | 0:74fff521858e | 75 | #define PartIDReg 0xFF |
laserdad | 0:74fff521858e | 76 | #define maxi2cFreq 1000000 |
laserdad | 0:74fff521858e | 77 | #define recommendedi2cFreq 400000 |
laserdad | 0:74fff521858e | 78 | #define maxECGrate 0 |
laserdad | 0:74fff521858e | 79 | #define normECGrate 1 |
laserdad | 0:74fff521858e | 80 | const int16_t i2cBufferSize=32; |
laserdad | 0:74fff521858e | 81 | |
laserdad | 0:74fff521858e | 82 | //global variables |
laserdad | 0:74fff521858e | 83 | |
laserdad | 0:74fff521858e | 84 | //USER SELECTABLE**************************** |
laserdad | 0:74fff521858e | 85 | const uint8_t ppgOn = 1; //turn on PPG (IR and Red both) |
laserdad | 0:74fff521858e | 86 | const uint8_t ecgOn = 1; //turn on ECG measurement |
laserdad | 0:74fff521858e | 87 | const uint8_t etiOn = 1; //turn on ETI (lead check) electrical tissue impedance. Checks if your fingers are on or not. |
laserdad | 0:74fff521858e | 88 | const uint8_t ecgRate = maxECGrate; //use normECGrate 800Hz or maxECGrate 1600Hz |
laserdad | 0:74fff521858e | 89 | const int16_t printEvery = 5 *(2-ecgRate); //print data only every X samples to reduce serial data BW (print fewer for faster sampling) |
laserdad | 0:74fff521858e | 90 | const int16_t ppgBits2Avg = 0; //log(2) of IIR filter divisor, data = data + (new_data-data)>>bits2Avg for PPG IR and R, use 0 for no averaging |
laserdad | 0:74fff521858e | 91 | const int16_t ecgBits2Avg = 3 + (1-ecgRate);; //(Recommend 3) log(2) of IIR filter divisor, data = data + (new_data-data)>>bits2Avg for ECG, use 0 for no averaging, or 4 with fast rate |
laserdad | 0:74fff521858e | 92 | const int16_t etiBits2Avg = 0; //log(2) of IIR filter divisor, data = data + (new_data-data)>>bits2Avg for ETI, use 0 for no averaging |
laserdad | 0:74fff521858e | 93 | const int16_t ppgBaselineBits = 9; //log(2) of baseline IIR filter divisor for PPG IR and PPG R, use 0 for no baseline removal |
laserdad | 0:74fff521858e | 94 | const int16_t ecgBaselineBits = 9; //(Recommend 9-10)log(2) of baseline IIR filter divisor for ecg, use 0 for no baseline removal |
laserdad | 0:74fff521858e | 95 | const int16_t etiBaselineBits = 0; //log(2) of baseline IIR filter divisor for eti, use 0 for no baseline removal |
laserdad | 0:74fff521858e | 96 | const uint8_t rCurrent = 0x50; //PPG LED current (when enabled), max=0xFF (255) |
laserdad | 0:74fff521858e | 97 | const uint8_t irCurrent = 0x50; //PPG LED current (when enabled), max=0xFF (255) in increments of 0.2mA or 0.4mA if hi pwr is enabled. |
laserdad | 0:74fff521858e | 98 | const uint8_t redHiPWR = 0; //0 = use normal pwr level (recommended for PPG). Note hi pwr is useful for proximity detection. |
laserdad | 0:74fff521858e | 99 | const uint8_t irHiPWR = 0; //0 = use normal pwr level (recommended for PPG). Note hi pwr is useful for proximity detection. |
laserdad | 0:74fff521858e | 100 | |
laserdad | 0:74fff521858e | 101 | bool runSetup =1; //tells program to reprogram ASIC (used on startup) |
laserdad | 0:74fff521858e | 102 | //*************************************** |
laserdad | 0:74fff521858e | 103 | |
laserdad | 0:74fff521858e | 104 | //USER SELECTABLE (though not recommended) |
laserdad | 0:74fff521858e | 105 | const uint8_t ppgRdyIntEn = 0; //not using ppgRdyInt (too slow) |
laserdad | 0:74fff521858e | 106 | const uint8_t ecgRdyIntEn = 0; //not using ECG_RDY interrrupt. It will interrupt frequently if this is enabled (too fast) |
laserdad | 0:74fff521858e | 107 | //************************************** |
laserdad | 0:74fff521858e | 108 | |
laserdad | 0:74fff521858e | 109 | //NOT USER CONFIGURABLE****************** |
laserdad | 0:74fff521858e | 110 | |
laserdad | 0:74fff521858e | 111 | #define interrupt_pin D12 //INTB pin --see InterruptIn declaration |
laserdad | 0:74fff521858e | 112 | const int16_t irChannel = ppgOn-1; //-1 or 0 |
laserdad | 0:74fff521858e | 113 | const int16_t rChannel = ppgOn*(2)-1; //-1 or 1 |
laserdad | 0:74fff521858e | 114 | const int16_t ecgChannel = ecgOn*(1+2*ppgOn)-1; //-1, 0, or 2 |
laserdad | 0:74fff521858e | 115 | const int16_t etiChannel = etiOn*(1+ecgOn+2*ppgOn)-1; //-1, 0,1 or 3 |
laserdad | 0:74fff521858e | 116 | const uint8_t numMeasEnabled = 2*ppgOn + ecgOn + etiOn; //Index # of how many measurements will be in each block of the FIFO |
laserdad | 0:74fff521858e | 117 | const uint8_t bytesPerSample = 3*numMeasEnabled; //each measurement is 3bytes |
laserdad | 0:74fff521858e | 118 | const int16_t samples2Read = i2cBufferSize / bytesPerSample ; //assuming 32 byte I2C buffer default |
laserdad | 0:74fff521858e | 119 | const uint8_t almostFullIntEn = 1; //priority to AFULL vs. PPG in code |
laserdad | 0:74fff521858e | 120 | const char bytes2Read = bytesPerSample*samples2Read; |
laserdad | 0:74fff521858e | 121 | char i2cWriteBuffer[10]; |
laserdad | 0:74fff521858e | 122 | char i2cReadBuffer[i2cBufferSize]; //32 bytes in i2c buffer size in Arduino by default. |
laserdad | 0:74fff521858e | 123 | unsigned long tim = 0; //for counting milliseconds |
laserdad | 0:74fff521858e | 124 | volatile bool intFlagged = 0; //ISR variable |
laserdad | 0:74fff521858e | 125 | long data[4][5]; //data from the first measurement type |
laserdad | 0:74fff521858e | 126 | long dataAvg[4]; //currently using data1 for ECG. This variable stores the IIR filtered avg. |
laserdad | 0:74fff521858e | 127 | long dataBaseline[4]; //this variable stores the few second baseline for substraction (IIR filter) |
laserdad | 0:74fff521858e | 128 | int ind = 0; //index to keep track of whether to print data or not (not printing every sample to serial monitor b/c it is too slow) |
laserdad | 0:74fff521858e | 129 | int bits2Avg[4]; //array stores the selected averaging time constants set below for filtering (loaded in setup) |
laserdad | 0:74fff521858e | 130 | int bits2AvgBaseline[4]; //stores below filter time constants (loaded in setup) |
laserdad | 0:74fff521858e | 131 | char FIFOData[bytes2Read]; |
laserdad | 0:74fff521858e | 132 | |
laserdad | 0:74fff521858e | 133 | |
laserdad | 0:74fff521858e | 134 | |
laserdad | 0:74fff521858e | 135 | //setup I2C, serial connection and timer |
laserdad | 0:74fff521858e | 136 | InterruptIn intPin(interrupt_pin); //config p5 as interrupt |
laserdad | 0:74fff521858e | 137 | I2C i2c(I2C_SDA,I2C_SCL); |
laserdad | 0:74fff521858e | 138 | Serial pc(USBTX,USBRX,NULL,230400); //open serial port (optionally add device name and baud rate after specifying TX and RX pins) |
laserdad | 0:74fff521858e | 139 | |
laserdad | 0:74fff521858e | 140 | |
laserdad | 0:74fff521858e | 141 | //declare subroutines |
laserdad | 0:74fff521858e | 142 | void writeRegister(uint8_t addr, uint8_t reg, uint8_t val) { |
laserdad | 0:74fff521858e | 143 | /*writes 1 byte to a single register*/ |
laserdad | 0:74fff521858e | 144 | char writeData[2]; |
laserdad | 0:74fff521858e | 145 | writeData[0] = reg ; |
laserdad | 0:74fff521858e | 146 | writeData[1] = val; |
laserdad | 0:74fff521858e | 147 | i2c.write(addr,writeData, 2); |
laserdad | 0:74fff521858e | 148 | } |
laserdad | 0:74fff521858e | 149 | |
laserdad | 0:74fff521858e | 150 | void writeBlock(uint8_t addr, uint8_t startReg, uint8_t *data, uint8_t numBytes) { |
laserdad | 0:74fff521858e | 151 | /*writes data from an array beginning at the startReg*/ |
laserdad | 0:74fff521858e | 152 | char writeData[numBytes+1]; |
laserdad | 0:74fff521858e | 153 | writeData[0]=startReg; |
laserdad | 0:74fff521858e | 154 | for(int n=1;n<numBytes;n++) { |
laserdad | 0:74fff521858e | 155 | writeData[n]=data[n-1]; |
laserdad | 0:74fff521858e | 156 | } |
laserdad | 0:74fff521858e | 157 | i2c.write(addr,writeData,numBytes+1); |
laserdad | 0:74fff521858e | 158 | } |
laserdad | 0:74fff521858e | 159 | |
laserdad | 0:74fff521858e | 160 | void readRegisters(uint8_t addr, uint8_t startReg, char *regData, int numBytes) { |
laserdad | 0:74fff521858e | 161 | char writeData = startReg; |
laserdad | 0:74fff521858e | 162 | i2c.write(addr,&writeData,1,true); //true is for repeated start |
laserdad | 0:74fff521858e | 163 | i2c.read(addr,regData,numBytes); |
laserdad | 0:74fff521858e | 164 | } |
laserdad | 0:74fff521858e | 165 | |
laserdad | 0:74fff521858e | 166 | //clears interrupts |
laserdad | 0:74fff521858e | 167 | void clearInterrupts(char *data) { |
laserdad | 0:74fff521858e | 168 | readRegisters(MAX86150_Addr,InterruptStatusReg1,data,1); |
laserdad | 0:74fff521858e | 169 | } |
laserdad | 0:74fff521858e | 170 | |
laserdad | 0:74fff521858e | 171 | void regDump(uint8_t Addr, uint8_t startByte, uint8_t endByte) { |
laserdad | 0:74fff521858e | 172 | /*print the values of up to 20 registers*/ |
laserdad | 0:74fff521858e | 173 | char regData[20]; |
laserdad | 0:74fff521858e | 174 | int numBytes; |
laserdad | 0:74fff521858e | 175 | if (endByte>=startByte) { |
laserdad | 0:74fff521858e | 176 | numBytes = (endByte-startByte+1) < 20 ? (endByte-startByte+1) : 20; |
laserdad | 0:74fff521858e | 177 | } |
laserdad | 0:74fff521858e | 178 | else { |
laserdad | 0:74fff521858e | 179 | numBytes=1; |
laserdad | 0:74fff521858e | 180 | } |
laserdad | 0:74fff521858e | 181 | |
laserdad | 0:74fff521858e | 182 | regData[0] = startByte; |
laserdad | 0:74fff521858e | 183 | i2c.write(Addr,regData,1,true); |
laserdad | 0:74fff521858e | 184 | i2c.read(Addr, regData, numBytes); |
laserdad | 0:74fff521858e | 185 | for(int n=0;n<numBytes;n++) { |
laserdad | 0:74fff521858e | 186 | pc.printf("%X, %X \r\n", startByte+n, regData[n]); |
laserdad | 0:74fff521858e | 187 | } |
laserdad | 0:74fff521858e | 188 | } |
laserdad | 0:74fff521858e | 189 | |
laserdad | 0:74fff521858e | 190 | bool bitRead(long data, uint8_t bitNum) { |
laserdad | 0:74fff521858e | 191 | long mask = 1<<bitNum; |
laserdad | 0:74fff521858e | 192 | long masked_bit = data & mask; |
laserdad | 0:74fff521858e | 193 | return masked_bit >> bitNum; |
laserdad | 0:74fff521858e | 194 | } |
laserdad | 0:74fff521858e | 195 | |
laserdad | 0:74fff521858e | 196 | void intEvent(void) { |
laserdad | 0:74fff521858e | 197 | intFlagged = 1; |
laserdad | 0:74fff521858e | 198 | } |
laserdad | 0:74fff521858e | 199 | |
laserdad | 0:74fff521858e | 200 | void initMAX86150(void) { |
laserdad | 0:74fff521858e | 201 | pc.printf("Initializing MAX86150\r\n"); |
laserdad | 0:74fff521858e | 202 | |
laserdad | 0:74fff521858e | 203 | //print configurations |
laserdad | 0:74fff521858e | 204 | pc.printf( (ppgOn ? "PPG on" : "PPG off") ); |
laserdad | 0:74fff521858e | 205 | pc.printf( (ecgOn ? ", ECG On" : ", ECG off") ); |
laserdad | 0:74fff521858e | 206 | pc.printf( (etiOn ? ", ETI On\r\n" : ", ETI off\r\n") ); |
laserdad | 0:74fff521858e | 207 | pc.printf( (ppgRdyIntEn ? "PPG Int On" : "PPG Int off") ); |
laserdad | 0:74fff521858e | 208 | pc.printf( (ecgRdyIntEn ? ", ECG Int On" : ", ECG Int off") ); |
laserdad | 0:74fff521858e | 209 | pc.printf( (almostFullIntEn ? ", Afull Int On\r\n" : ", Afull Int off\r\n") ); |
laserdad | 0:74fff521858e | 210 | //write register configurations |
laserdad | 0:74fff521858e | 211 | writeRegister(MAX86150_Addr,SystemControlReg,0x01); //chip reset |
laserdad | 0:74fff521858e | 212 | wait_ms(2); //wait for chip to come back on line |
laserdad | 0:74fff521858e | 213 | //if PPG or ETI are not enabled, then FIFO is setup for ECG |
laserdad | 0:74fff521858e | 214 | writeRegister(MAX86150_Addr,FIFOConfigReg,0x1F); // [4] FIFO_ROLLS_ON_FULL, clears with status read or FIFO_data read |
laserdad | 0:74fff521858e | 215 | uint16_t FIFOCode; |
laserdad | 0:74fff521858e | 216 | FIFOCode = etiOn ? 0x000A : 0x0000 ; //ETI is last in FIFO |
laserdad | 0:74fff521858e | 217 | FIFOCode = ecgOn ? (FIFOCode<<4 | 0x0009) : FIFOCode; //insert ECG front of ETI in FIFO |
laserdad | 0:74fff521858e | 218 | FIFOCode = ppgOn ? (FIFOCode<<8 | 0x0021) : FIFOCode; //insert Red(2) and IR (1) in front of ECG in FIFO |
laserdad | 0:74fff521858e | 219 | writeRegister(MAX86150_Addr,FIFODataControlReg1, (char)(FIFOCode & 0x00FF) ); |
laserdad | 0:74fff521858e | 220 | writeRegister(MAX86150_Addr,FIFODataControlReg2, (char)(FIFOCode >>8) ); |
laserdad | 0:74fff521858e | 221 | writeRegister(MAX86150_Addr, ppgConfigReg0,0b11010111); //D3 for 100Hz, PPG_ADC_RGE: 32768nA, PPG_SR: 100SpS, PPG_LED_PW: 400uS |
laserdad | 0:74fff521858e | 222 | writeRegister(MAX86150_Addr,LED1PulseAmpReg, ppgOn ? rCurrent : 0x00 ); |
laserdad | 0:74fff521858e | 223 | writeRegister(MAX86150_Addr,LED2PulseAmpReg, ppgOn ? irCurrent : 0x00); |
laserdad | 0:74fff521858e | 224 | writeRegister(MAX86150_Addr,LEDRangeReg, irHiPWR * 2 + redHiPWR ); // PPG_ADC_RGE: 32768nA |
laserdad | 0:74fff521858e | 225 | //ecg configuration |
laserdad | 0:74fff521858e | 226 | if (ecgOn) { |
laserdad | 0:74fff521858e | 227 | //**************************************** |
laserdad | 0:74fff521858e | 228 | //ECG data rate is user configurable in theory, but you have to adjust your filter time constants and serial printEvery variables accordingly |
laserdad | 0:74fff521858e | 229 | writeRegister(MAX86150_Addr,EcgConfigReg1,ecgRate); //ECG sample rate 0x00 =1600, 0x01=800Hz etc down to 200Hz. add 4 to double frequency (double ADC clock) |
laserdad | 0:74fff521858e | 230 | writeRegister(MAX86150_Addr,EcgConfigReg2,0x11); //hidden register at ECG config 2, per JY's settings |
laserdad | 0:74fff521858e | 231 | writeRegister(MAX86150_Addr,EcgConfigReg3,0x3D); //ECG config 3 |
laserdad | 0:74fff521858e | 232 | writeRegister(MAX86150_Addr,EcgConfigReg4,0x02); //ECG config 4 per JY's settings |
laserdad | 0:74fff521858e | 233 | //enter test mode |
laserdad | 0:74fff521858e | 234 | writeRegister(MAX86150_Addr,0xFF,0x54); //write 0x54 to register 0xFF |
laserdad | 0:74fff521858e | 235 | writeRegister(MAX86150_Addr,0xFF,0x4D); //write 0x4D to register 0xFF |
laserdad | 0:74fff521858e | 236 | writeRegister(MAX86150_Addr,0xCE,0x01); |
laserdad | 0:74fff521858e | 237 | writeRegister(MAX86150_Addr,0xCF,0x18); //adjust hidden registers at CE and CF per JY's settings in EV kit |
laserdad | 0:74fff521858e | 238 | writeRegister(MAX86150_Addr,0xD0,0x01); //adjust hidden registers D0 (probably ETI) |
laserdad | 0:74fff521858e | 239 | writeRegister(MAX86150_Addr,0xFF,0x00); //exit test mode |
laserdad | 0:74fff521858e | 240 | } |
laserdad | 0:74fff521858e | 241 | //setup interrupts last |
laserdad | 0:74fff521858e | 242 | writeRegister(MAX86150_Addr,InterruptEnableReg1,( almostFullIntEn ? 0x80 : (ppgRdyIntEn ? 0x40 : 0x00) ) ); |
laserdad | 0:74fff521858e | 243 | writeRegister(MAX86150_Addr,InterruptEnableReg2, (ecgRdyIntEn ? 0x04 : 0x00) ); |
laserdad | 0:74fff521858e | 244 | writeRegister(MAX86150_Addr,SystemControlReg,0x04);//start FIFO |
laserdad | 0:74fff521858e | 245 | |
laserdad | 0:74fff521858e | 246 | pc.printf("done configuring MAX86150\r\n"); |
laserdad | 0:74fff521858e | 247 | } //end initMAX86150 |
laserdad | 0:74fff521858e | 248 | |
laserdad | 0:74fff521858e | 249 | |
laserdad | 0:74fff521858e | 250 | bool readFIFO(int numSamples, char *fifodata) { |
laserdad | 0:74fff521858e | 251 | char stat[1]; |
laserdad | 0:74fff521858e | 252 | bool dataValid = 0; |
laserdad | 0:74fff521858e | 253 | uint8_t tries = 0; |
laserdad | 0:74fff521858e | 254 | char newReadPointer; |
laserdad | 0:74fff521858e | 255 | clearInterrupts(stat); |
laserdad | 0:74fff521858e | 256 | //get FIFO position |
laserdad | 0:74fff521858e | 257 | readRegisters(MAX86150_Addr, FIFOReadPointerReg, i2cReadBuffer, 1); //you can do more sophisticated stuff looking for missed samples, but I'm keeping this lean and simple |
laserdad | 0:74fff521858e | 258 | char readPointer = i2cReadBuffer[0]; |
laserdad | 0:74fff521858e | 259 | while(!dataValid) { |
laserdad | 0:74fff521858e | 260 | tries++; |
laserdad | 0:74fff521858e | 261 | //try reading FIFO |
laserdad | 0:74fff521858e | 262 | readRegisters(MAX86150_Addr, FIFODataReg, fifodata, bytes2Read); //get data |
laserdad | 0:74fff521858e | 263 | //see if it worked if you are not servicing interrupts faster than the sample rate |
laserdad | 0:74fff521858e | 264 | //if you are servicing interrupts much faster than the sample rate, then you can get rid of the extra pointer register check. |
laserdad | 0:74fff521858e | 265 | dataValid=1; |
laserdad | 0:74fff521858e | 266 | //readRegisters(MAX86150_Addr, FIFOReadPointerReg, i2cReadBuffer, 1); //check that the read pointer has moved (otherwise FIFO was being written to) |
laserdad | 0:74fff521858e | 267 | // newReadPointer = i2cReadBuffer[0]; |
laserdad | 0:74fff521858e | 268 | // if( (newReadPointer - readPointer == numSamples) || (newReadPointer+32 -readPointer ==numSamples ) ){ //check that we got the right number of samples (including FIFO pointer wrap around possiblity) |
laserdad | 0:74fff521858e | 269 | // dataValid=1; |
laserdad | 0:74fff521858e | 270 | // return 1; |
laserdad | 0:74fff521858e | 271 | // } |
laserdad | 0:74fff521858e | 272 | // else if (tries > 1) { //if it failed twice, you've got a different problem perhaps, exiting with error code (0) so you can void the data |
laserdad | 0:74fff521858e | 273 | // break; |
laserdad | 0:74fff521858e | 274 | // } |
laserdad | 0:74fff521858e | 275 | // else { |
laserdad | 0:74fff521858e | 276 | // wait_us(100); //try again a moment later |
laserdad | 0:74fff521858e | 277 | // } |
laserdad | 0:74fff521858e | 278 | } |
laserdad | 0:74fff521858e | 279 | return dataValid; |
laserdad | 0:74fff521858e | 280 | } //end readFIFO |
laserdad | 0:74fff521858e | 281 | |
laserdad | 0:74fff521858e | 282 | //for serial monitoring |
laserdad | 0:74fff521858e | 283 | void printData(uint16_t numSamples,char *fifodata) { |
laserdad | 0:74fff521858e | 284 | //cat and mask the bits from the FIFO |
laserdad | 0:74fff521858e | 285 | for (int n = 0; n < numSamples; n++) { //for every sample |
laserdad | 0:74fff521858e | 286 | char p = bytesPerSample; |
laserdad | 0:74fff521858e | 287 | for (int m=0;m<numMeasEnabled;m++) { //for every enabled measurement |
laserdad | 0:74fff521858e | 288 | data[m][n] = ( (long)(fifodata[p*n +3*m] & 0x7) << 16) | ( (long)fifodata[p*n + 1+3*m] << 8) | ( (long)fifodata[p*n + 2+3*m]); |
laserdad | 0:74fff521858e | 289 | if (bitRead(data[m][n], 17) && ( (ecgChannel==m) || (etiChannel==m) ) ) {//handle ECG and ETI differently than PPG data. data[m][n] |= 0xFFFC0000; |
laserdad | 0:74fff521858e | 290 | data[m][n] |= 0xFFFC0000; |
laserdad | 0:74fff521858e | 291 | } |
laserdad | 0:74fff521858e | 292 | } |
laserdad | 0:74fff521858e | 293 | } |
laserdad | 0:74fff521858e | 294 | for (int n = 0; n < numSamples; n++) { //for every sample |
laserdad | 0:74fff521858e | 295 | ind++; |
laserdad | 0:74fff521858e | 296 | ind = ind % printEvery; |
laserdad | 0:74fff521858e | 297 | |
laserdad | 0:74fff521858e | 298 | //calc avg and baseline |
laserdad | 0:74fff521858e | 299 | for(int m=0;m<numMeasEnabled;m++) { //for every enabled measurement |
laserdad | 0:74fff521858e | 300 | dataAvg[m] += ( data[m][n] - dataAvg[m] ) >> bits2Avg[m] ; //get the running average |
laserdad | 0:74fff521858e | 301 | dataBaseline[m] += ( data[m][n] - dataBaseline[m] ) >> bits2AvgBaseline[m]; //get the long baseline |
laserdad | 0:74fff521858e | 302 | } |
laserdad | 0:74fff521858e | 303 | |
laserdad | 0:74fff521858e | 304 | //print data |
laserdad | 0:74fff521858e | 305 | if (ind == 1) { //printing only every specified number of samples to reduce serial traffic to manageable level |
laserdad | 0:74fff521858e | 306 | for (int m=0;m<numMeasEnabled;m++) {//for every enabled measurement |
laserdad | 0:74fff521858e | 307 | if(bits2AvgBaseline[m]>0) { |
laserdad | 0:74fff521858e | 308 | pc.printf("%i, ",dataAvg[m]-dataBaseline[m]); //print with baseline subtraction |
laserdad | 0:74fff521858e | 309 | } |
laserdad | 0:74fff521858e | 310 | else { |
laserdad | 0:74fff521858e | 311 | pc.printf("%i, ", dataAvg[m]); //print without baseline subtraction |
laserdad | 0:74fff521858e | 312 | } |
laserdad | 0:74fff521858e | 313 | } |
laserdad | 0:74fff521858e | 314 | pc.printf("\r\n"); |
laserdad | 0:74fff521858e | 315 | } //end print loop |
laserdad | 0:74fff521858e | 316 | } //end sample loop |
laserdad | 0:74fff521858e | 317 | }//end printData() |
laserdad | 0:74fff521858e | 318 | |
laserdad | 0:74fff521858e | 319 | |
laserdad | 0:74fff521858e | 320 | |
laserdad | 0:74fff521858e | 321 | |
laserdad | 0:74fff521858e | 322 | //for debugging |
laserdad | 0:74fff521858e | 323 | void printRawFIFO(int numSamples,char *fifodata) { |
laserdad | 0:74fff521858e | 324 | // Serial.print("FIFO bytes "); |
laserdad | 0:74fff521858e | 325 | for (int n = 0; n < numSamples; n++) {//for every sample. |
laserdad | 0:74fff521858e | 326 | for (int m=0;m<numMeasEnabled;m++) {//for every kind of measurement |
laserdad | 0:74fff521858e | 327 | pc.printf("%d: ",m); |
laserdad | 0:74fff521858e | 328 | pc.printf("%x, %x, %x, ",fifodata[bytesPerSample * n +3*m], fifodata[bytesPerSample * n + 1 +3*m], fifodata[bytesPerSample * n + 2 + 3*m] ); |
laserdad | 0:74fff521858e | 329 | } //end measurement loop |
laserdad | 0:74fff521858e | 330 | pc.printf("\r\n"); |
laserdad | 0:74fff521858e | 331 | } //end sample loop |
laserdad | 0:74fff521858e | 332 | } //end function |
laserdad | 0:74fff521858e | 333 | |
laserdad | 0:74fff521858e | 334 | |
laserdad | 0:74fff521858e | 335 | void setupASIC(void) { |
laserdad | 0:74fff521858e | 336 | pc.printf("Running Setup\r\n"); |
laserdad | 0:74fff521858e | 337 | runSetup = 0; //only run once |
laserdad | 0:74fff521858e | 338 | i2c.frequency(recommendedi2cFreq); //set I2C frequency to 400kHz |
laserdad | 0:74fff521858e | 339 | // intPin.mode(PullUp); //pullups on the sensor board |
laserdad | 0:74fff521858e | 340 | //configure MAX86150 register settings |
laserdad | 0:74fff521858e | 341 | initMAX86150(); |
laserdad | 0:74fff521858e | 342 | pc.printf("register dump\r\n"); |
laserdad | 0:74fff521858e | 343 | |
laserdad | 0:74fff521858e | 344 | // //print register configuration |
laserdad | 0:74fff521858e | 345 | // regDump(MAX86150_Addr,0x00, 0x06); |
laserdad | 0:74fff521858e | 346 | // regDump(MAX86150_Addr,0x08, 0x15); |
laserdad | 0:74fff521858e | 347 | // regDump(MAX86150_Addr,0x3C, 0x3F); |
laserdad | 0:74fff521858e | 348 | // //go to test mode |
laserdad | 0:74fff521858e | 349 | // writeRegister(MAX86150_Addr,0xFF,0x54); |
laserdad | 0:74fff521858e | 350 | // writeRegister(MAX86150_Addr,0xFF,0x4D); |
laserdad | 0:74fff521858e | 351 | // regDump(MAX86150_Addr,0xCE, 0xCF); |
laserdad | 0:74fff521858e | 352 | // regDump(MAX86150_Addr,0xD0, 0xD0); |
laserdad | 0:74fff521858e | 353 | // writeRegister(MAX86150_Addr,0xFF,0x00); |
laserdad | 0:74fff521858e | 354 | clearInterrupts(i2cReadBuffer); |
laserdad | 0:74fff521858e | 355 | i2c.frequency(maxi2cFreq); |
laserdad | 0:74fff521858e | 356 | //configure averaging |
laserdad | 0:74fff521858e | 357 | if(ppgOn) { |
laserdad | 0:74fff521858e | 358 | bits2Avg[rChannel]=ppgBits2Avg; |
laserdad | 0:74fff521858e | 359 | bits2Avg[irChannel]=ppgBits2Avg; |
laserdad | 0:74fff521858e | 360 | bits2AvgBaseline[rChannel]=ppgBaselineBits; |
laserdad | 0:74fff521858e | 361 | bits2AvgBaseline[irChannel]=ppgBaselineBits; |
laserdad | 0:74fff521858e | 362 | } |
laserdad | 0:74fff521858e | 363 | if(ecgOn){ |
laserdad | 0:74fff521858e | 364 | bits2Avg[ecgChannel]=ecgBits2Avg; |
laserdad | 0:74fff521858e | 365 | bits2AvgBaseline[ecgChannel]=ecgBaselineBits; |
laserdad | 0:74fff521858e | 366 | } |
laserdad | 0:74fff521858e | 367 | if(etiOn){ |
laserdad | 0:74fff521858e | 368 | bits2Avg[etiChannel]=etiBits2Avg; |
laserdad | 0:74fff521858e | 369 | bits2AvgBaseline[etiChannel]=etiBaselineBits; |
laserdad | 0:74fff521858e | 370 | } |
laserdad | 0:74fff521858e | 371 | pc.printf("Done w/setup\r\n"); |
laserdad | 0:74fff521858e | 372 | } |
laserdad | 0:74fff521858e | 373 | |
laserdad | 0:74fff521858e | 374 | |
laserdad | 0:74fff521858e | 375 | |
laserdad | 0:74fff521858e | 376 | int main() { |
laserdad | 0:74fff521858e | 377 | char FIFOData[bytesPerSample]; |
laserdad | 0:74fff521858e | 378 | if(runSetup) { |
laserdad | 0:74fff521858e | 379 | setupASIC(); |
laserdad | 0:74fff521858e | 380 | } |
laserdad | 0:74fff521858e | 381 | |
laserdad | 0:74fff521858e | 382 | intPin.fall(&intEvent); |
laserdad | 0:74fff521858e | 383 | while(1) { |
laserdad | 0:74fff521858e | 384 | char stat[1]; |
laserdad | 0:74fff521858e | 385 | static int n = 0; |
laserdad | 0:74fff521858e | 386 | if (intFlagged) { |
laserdad | 0:74fff521858e | 387 | // pc.printf("intFlagged\r\n"); |
laserdad | 0:74fff521858e | 388 | if (almostFullIntEn) { |
laserdad | 0:74fff521858e | 389 | n = 0; |
laserdad | 0:74fff521858e | 390 | intFlagged = 0; |
laserdad | 0:74fff521858e | 391 | readFIFO(samples2Read,FIFOData); |
laserdad | 0:74fff521858e | 392 | printData(samples2Read,FIFOData); |
laserdad | 0:74fff521858e | 393 | // printRawFIFO(samples2Read,FIFOData); //for debugging |
laserdad | 0:74fff521858e | 394 | } //end if AFULL |
laserdad | 0:74fff521858e | 395 | else { //this is to handle end of conversion interrupts (not configured by default) |
laserdad | 0:74fff521858e | 396 | if (n < samples2Read) { |
laserdad | 0:74fff521858e | 397 | n++; |
laserdad | 0:74fff521858e | 398 | intFlagged = 0; |
laserdad | 0:74fff521858e | 399 | clearInterrupts(stat); |
laserdad | 0:74fff521858e | 400 | // Serial.println(n); |
laserdad | 0:74fff521858e | 401 | } |
laserdad | 0:74fff521858e | 402 | else { |
laserdad | 0:74fff521858e | 403 | n = 0; |
laserdad | 0:74fff521858e | 404 | intFlagged = 0; |
laserdad | 0:74fff521858e | 405 | readFIFO(samples2Read,FIFOData); |
laserdad | 0:74fff521858e | 406 | printData(samples2Read,FIFOData); |
laserdad | 0:74fff521858e | 407 | pc.printf("\r\n"); |
laserdad | 0:74fff521858e | 408 | // printRawFIFO(samples2Read); |
laserdad | 0:74fff521858e | 409 | } |
laserdad | 0:74fff521858e | 410 | } //end if/else AFULL |
laserdad | 0:74fff521858e | 411 | } //end if intFlagged |
laserdad | 0:74fff521858e | 412 | } //end while |
laserdad | 0:74fff521858e | 413 | |
laserdad | 0:74fff521858e | 414 | }//end main |
laserdad | 0:74fff521858e | 415 | |
laserdad | 0:74fff521858e | 416 | |
laserdad | 0:74fff521858e | 417 | |
laserdad | 0:74fff521858e | 418 | |
laserdad | 0:74fff521858e | 419 | /* FYI seee this table for effective 3dB bandwidth for IIR filter given sample rate and number of bits shifted |
laserdad | 0:74fff521858e | 420 | * fsample bits f3dB |
laserdad | 0:74fff521858e | 421 | * 1600 1 89.6 |
laserdad | 0:74fff521858e | 422 | * 1600 2 72 |
laserdad | 0:74fff521858e | 423 | * 1600 3 24 |
laserdad | 0:74fff521858e | 424 | * 1600 4 8 |
laserdad | 0:74fff521858e | 425 | * 800 1 44.8 |
laserdad | 0:74fff521858e | 426 | * 800 2 36 |
laserdad | 0:74fff521858e | 427 | * 800 3 12 |
laserdad | 0:74fff521858e | 428 | * 800 4 4 |
laserdad | 0:74fff521858e | 429 | * 400 1 22.4 |
laserdad | 0:74fff521858e | 430 | * 400 2 18 |
laserdad | 0:74fff521858e | 431 | * 400 3 6 |
laserdad | 0:74fff521858e | 432 | * 400 4 2 |
laserdad | 0:74fff521858e | 433 | * 200 1 11.2 |
laserdad | 0:74fff521858e | 434 | * 200 2 9 |
laserdad | 0:74fff521858e | 435 | * 200 3 3 |
laserdad | 0:74fff521858e | 436 | * 200 4 1 |
laserdad | 0:74fff521858e | 437 | */ |
laserdad | 0:74fff521858e | 438 |