Dan Allen
/
MAX86150_ECG_PPG
MAX86150 code
Fork of MAX86150_ECG_PPG by
main.cpp@1:f62247cbeac6, 2017-01-04 (annotated)
- Committer:
- laserdad
- Date:
- Wed Jan 04 17:00:53 2017 +0000
- Revision:
- 1:f62247cbeac6
- Parent:
- 0:74fff521858e
- Child:
- 2:c8b7ef52c65c
code formated
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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 14 | * of an I2C transaction, so we have to keep I2C transactions short. |
laserdad | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 31 | * then the FIFO data will be garbage. You have to reread the FIFO. |
laserdad | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 34 | * |
laserdad | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 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 | 1:f62247cbeac6 | 142 | void writeRegister(uint8_t addr, uint8_t reg, uint8_t val) |
laserdad | 1:f62247cbeac6 | 143 | { |
laserdad | 1:f62247cbeac6 | 144 | /*writes 1 byte to a single register*/ |
laserdad | 0:74fff521858e | 145 | char writeData[2]; |
laserdad | 0:74fff521858e | 146 | writeData[0] = reg ; |
laserdad | 0:74fff521858e | 147 | writeData[1] = val; |
laserdad | 0:74fff521858e | 148 | i2c.write(addr,writeData, 2); |
laserdad | 0:74fff521858e | 149 | } |
laserdad | 0:74fff521858e | 150 | |
laserdad | 1:f62247cbeac6 | 151 | void writeBlock(uint8_t addr, uint8_t startReg, uint8_t *data, uint8_t numBytes) |
laserdad | 1:f62247cbeac6 | 152 | { |
laserdad | 1:f62247cbeac6 | 153 | /*writes data from an array beginning at the startReg*/ |
laserdad | 0:74fff521858e | 154 | char writeData[numBytes+1]; |
laserdad | 0:74fff521858e | 155 | writeData[0]=startReg; |
laserdad | 1:f62247cbeac6 | 156 | for(int n=1; n<numBytes; n++) { |
laserdad | 0:74fff521858e | 157 | writeData[n]=data[n-1]; |
laserdad | 0:74fff521858e | 158 | } |
laserdad | 0:74fff521858e | 159 | i2c.write(addr,writeData,numBytes+1); |
laserdad | 0:74fff521858e | 160 | } |
laserdad | 0:74fff521858e | 161 | |
laserdad | 1:f62247cbeac6 | 162 | void readRegisters(uint8_t addr, uint8_t startReg, char *regData, int numBytes) |
laserdad | 1:f62247cbeac6 | 163 | { |
laserdad | 0:74fff521858e | 164 | char writeData = startReg; |
laserdad | 0:74fff521858e | 165 | i2c.write(addr,&writeData,1,true); //true is for repeated start |
laserdad | 0:74fff521858e | 166 | i2c.read(addr,regData,numBytes); |
laserdad | 0:74fff521858e | 167 | } |
laserdad | 0:74fff521858e | 168 | |
laserdad | 0:74fff521858e | 169 | //clears interrupts |
laserdad | 1:f62247cbeac6 | 170 | void clearInterrupts(char *data) |
laserdad | 1:f62247cbeac6 | 171 | { |
laserdad | 0:74fff521858e | 172 | readRegisters(MAX86150_Addr,InterruptStatusReg1,data,1); |
laserdad | 0:74fff521858e | 173 | } |
laserdad | 0:74fff521858e | 174 | |
laserdad | 1:f62247cbeac6 | 175 | void regDump(uint8_t Addr, uint8_t startByte, uint8_t endByte) |
laserdad | 1:f62247cbeac6 | 176 | { |
laserdad | 1:f62247cbeac6 | 177 | /*print the values of up to 20 registers*/ |
laserdad | 0:74fff521858e | 178 | char regData[20]; |
laserdad | 0:74fff521858e | 179 | int numBytes; |
laserdad | 0:74fff521858e | 180 | if (endByte>=startByte) { |
laserdad | 0:74fff521858e | 181 | numBytes = (endByte-startByte+1) < 20 ? (endByte-startByte+1) : 20; |
laserdad | 1:f62247cbeac6 | 182 | } else { |
laserdad | 0:74fff521858e | 183 | numBytes=1; |
laserdad | 1:f62247cbeac6 | 184 | } |
laserdad | 1:f62247cbeac6 | 185 | |
laserdad | 0:74fff521858e | 186 | regData[0] = startByte; |
laserdad | 0:74fff521858e | 187 | i2c.write(Addr,regData,1,true); |
laserdad | 0:74fff521858e | 188 | i2c.read(Addr, regData, numBytes); |
laserdad | 1:f62247cbeac6 | 189 | for(int n=0; n<numBytes; n++) { |
laserdad | 1:f62247cbeac6 | 190 | pc.printf("%X, %X \r\n", startByte+n, regData[n]); |
laserdad | 0:74fff521858e | 191 | } |
laserdad | 0:74fff521858e | 192 | } |
laserdad | 0:74fff521858e | 193 | |
laserdad | 1:f62247cbeac6 | 194 | bool bitRead(long data, uint8_t bitNum) |
laserdad | 1:f62247cbeac6 | 195 | { |
laserdad | 1:f62247cbeac6 | 196 | long mask = 1<<bitNum; |
laserdad | 1:f62247cbeac6 | 197 | long masked_bit = data & mask; |
laserdad | 1:f62247cbeac6 | 198 | return masked_bit >> bitNum; |
laserdad | 0:74fff521858e | 199 | } |
laserdad | 0:74fff521858e | 200 | |
laserdad | 1:f62247cbeac6 | 201 | void intEvent(void) |
laserdad | 1:f62247cbeac6 | 202 | { |
laserdad | 1:f62247cbeac6 | 203 | intFlagged = 1; |
laserdad | 0:74fff521858e | 204 | } |
laserdad | 0:74fff521858e | 205 | |
laserdad | 1:f62247cbeac6 | 206 | void initMAX86150(void) |
laserdad | 1:f62247cbeac6 | 207 | { |
laserdad | 0:74fff521858e | 208 | pc.printf("Initializing MAX86150\r\n"); |
laserdad | 0:74fff521858e | 209 | |
laserdad | 1:f62247cbeac6 | 210 | //print configurations |
laserdad | 0:74fff521858e | 211 | pc.printf( (ppgOn ? "PPG on" : "PPG off") ); |
laserdad | 0:74fff521858e | 212 | pc.printf( (ecgOn ? ", ECG On" : ", ECG off") ); |
laserdad | 0:74fff521858e | 213 | pc.printf( (etiOn ? ", ETI On\r\n" : ", ETI off\r\n") ); |
laserdad | 0:74fff521858e | 214 | pc.printf( (ppgRdyIntEn ? "PPG Int On" : "PPG Int off") ); |
laserdad | 0:74fff521858e | 215 | pc.printf( (ecgRdyIntEn ? ", ECG Int On" : ", ECG Int off") ); |
laserdad | 0:74fff521858e | 216 | pc.printf( (almostFullIntEn ? ", Afull Int On\r\n" : ", Afull Int off\r\n") ); |
laserdad | 0:74fff521858e | 217 | //write register configurations |
laserdad | 0:74fff521858e | 218 | writeRegister(MAX86150_Addr,SystemControlReg,0x01); //chip reset |
laserdad | 0:74fff521858e | 219 | wait_ms(2); //wait for chip to come back on line |
laserdad | 0:74fff521858e | 220 | //if PPG or ETI are not enabled, then FIFO is setup for ECG |
laserdad | 0:74fff521858e | 221 | writeRegister(MAX86150_Addr,FIFOConfigReg,0x1F); // [4] FIFO_ROLLS_ON_FULL, clears with status read or FIFO_data read |
laserdad | 0:74fff521858e | 222 | uint16_t FIFOCode; |
laserdad | 0:74fff521858e | 223 | FIFOCode = etiOn ? 0x000A : 0x0000 ; //ETI is last in FIFO |
laserdad | 0:74fff521858e | 224 | FIFOCode = ecgOn ? (FIFOCode<<4 | 0x0009) : FIFOCode; //insert ECG front of ETI in FIFO |
laserdad | 0:74fff521858e | 225 | FIFOCode = ppgOn ? (FIFOCode<<8 | 0x0021) : FIFOCode; //insert Red(2) and IR (1) in front of ECG in FIFO |
laserdad | 0:74fff521858e | 226 | writeRegister(MAX86150_Addr,FIFODataControlReg1, (char)(FIFOCode & 0x00FF) ); |
laserdad | 0:74fff521858e | 227 | writeRegister(MAX86150_Addr,FIFODataControlReg2, (char)(FIFOCode >>8) ); |
laserdad | 1:f62247cbeac6 | 228 | writeRegister(MAX86150_Addr, ppgConfigReg0,0b11010111); //D3 for 100Hz, PPG_ADC_RGE: 32768nA, PPG_SR: 100SpS, PPG_LED_PW: 400uS |
laserdad | 0:74fff521858e | 229 | writeRegister(MAX86150_Addr,LED1PulseAmpReg, ppgOn ? rCurrent : 0x00 ); |
laserdad | 0:74fff521858e | 230 | writeRegister(MAX86150_Addr,LED2PulseAmpReg, ppgOn ? irCurrent : 0x00); |
laserdad | 0:74fff521858e | 231 | writeRegister(MAX86150_Addr,LEDRangeReg, irHiPWR * 2 + redHiPWR ); // PPG_ADC_RGE: 32768nA |
laserdad | 0:74fff521858e | 232 | //ecg configuration |
laserdad | 0:74fff521858e | 233 | if (ecgOn) { |
laserdad | 0:74fff521858e | 234 | //**************************************** |
laserdad | 1:f62247cbeac6 | 235 | //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 | 236 | 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 | 237 | writeRegister(MAX86150_Addr,EcgConfigReg2,0x11); //hidden register at ECG config 2, per JY's settings |
laserdad | 0:74fff521858e | 238 | writeRegister(MAX86150_Addr,EcgConfigReg3,0x3D); //ECG config 3 |
laserdad | 0:74fff521858e | 239 | writeRegister(MAX86150_Addr,EcgConfigReg4,0x02); //ECG config 4 per JY's settings |
laserdad | 1:f62247cbeac6 | 240 | //enter test mode |
laserdad | 0:74fff521858e | 241 | writeRegister(MAX86150_Addr,0xFF,0x54); //write 0x54 to register 0xFF |
laserdad | 0:74fff521858e | 242 | writeRegister(MAX86150_Addr,0xFF,0x4D); //write 0x4D to register 0xFF |
laserdad | 0:74fff521858e | 243 | writeRegister(MAX86150_Addr,0xCE,0x01); |
laserdad | 0:74fff521858e | 244 | writeRegister(MAX86150_Addr,0xCF,0x18); //adjust hidden registers at CE and CF per JY's settings in EV kit |
laserdad | 0:74fff521858e | 245 | writeRegister(MAX86150_Addr,0xD0,0x01); //adjust hidden registers D0 (probably ETI) |
laserdad | 1:f62247cbeac6 | 246 | writeRegister(MAX86150_Addr,0xFF,0x00); //exit test mode |
laserdad | 0:74fff521858e | 247 | } |
laserdad | 0:74fff521858e | 248 | //setup interrupts last |
laserdad | 0:74fff521858e | 249 | writeRegister(MAX86150_Addr,InterruptEnableReg1,( almostFullIntEn ? 0x80 : (ppgRdyIntEn ? 0x40 : 0x00) ) ); |
laserdad | 0:74fff521858e | 250 | writeRegister(MAX86150_Addr,InterruptEnableReg2, (ecgRdyIntEn ? 0x04 : 0x00) ); |
laserdad | 0:74fff521858e | 251 | writeRegister(MAX86150_Addr,SystemControlReg,0x04);//start FIFO |
laserdad | 1:f62247cbeac6 | 252 | |
laserdad | 0:74fff521858e | 253 | pc.printf("done configuring MAX86150\r\n"); |
laserdad | 0:74fff521858e | 254 | } //end initMAX86150 |
laserdad | 0:74fff521858e | 255 | |
laserdad | 0:74fff521858e | 256 | |
laserdad | 1:f62247cbeac6 | 257 | bool readFIFO(int numSamples, char *fifodata) |
laserdad | 1:f62247cbeac6 | 258 | { |
laserdad | 0:74fff521858e | 259 | char stat[1]; |
laserdad | 0:74fff521858e | 260 | bool dataValid = 0; |
laserdad | 0:74fff521858e | 261 | uint8_t tries = 0; |
laserdad | 0:74fff521858e | 262 | char newReadPointer; |
laserdad | 0:74fff521858e | 263 | clearInterrupts(stat); |
laserdad | 0:74fff521858e | 264 | //get FIFO position |
laserdad | 0:74fff521858e | 265 | 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 | 1:f62247cbeac6 | 266 | char readPointer = i2cReadBuffer[0]; |
laserdad | 1:f62247cbeac6 | 267 | while(!dataValid) { |
laserdad | 0:74fff521858e | 268 | tries++; |
laserdad | 0:74fff521858e | 269 | //try reading FIFO |
laserdad | 0:74fff521858e | 270 | readRegisters(MAX86150_Addr, FIFODataReg, fifodata, bytes2Read); //get data |
laserdad | 0:74fff521858e | 271 | //see if it worked if you are not servicing interrupts faster than the sample rate |
laserdad | 0:74fff521858e | 272 | //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 | 273 | dataValid=1; |
laserdad | 1:f62247cbeac6 | 274 | //readRegisters(MAX86150_Addr, FIFOReadPointerReg, i2cReadBuffer, 1); //check that the read pointer has moved (otherwise FIFO was being written to) |
laserdad | 0:74fff521858e | 275 | // newReadPointer = i2cReadBuffer[0]; |
laserdad | 0:74fff521858e | 276 | // 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 | 277 | // dataValid=1; |
laserdad | 0:74fff521858e | 278 | // return 1; |
laserdad | 0:74fff521858e | 279 | // } |
laserdad | 0:74fff521858e | 280 | // 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 | 281 | // break; |
laserdad | 0:74fff521858e | 282 | // } |
laserdad | 0:74fff521858e | 283 | // else { |
laserdad | 0:74fff521858e | 284 | // wait_us(100); //try again a moment later |
laserdad | 1:f62247cbeac6 | 285 | // } |
laserdad | 0:74fff521858e | 286 | } |
laserdad | 0:74fff521858e | 287 | return dataValid; |
laserdad | 0:74fff521858e | 288 | } //end readFIFO |
laserdad | 0:74fff521858e | 289 | |
laserdad | 0:74fff521858e | 290 | //for serial monitoring |
laserdad | 1:f62247cbeac6 | 291 | void printData(uint16_t numSamples,char *fifodata) |
laserdad | 1:f62247cbeac6 | 292 | { |
laserdad | 1:f62247cbeac6 | 293 | //cat and mask the bits from the FIFO |
laserdad | 1:f62247cbeac6 | 294 | for (int n = 0; n < numSamples; n++) { //for every sample |
laserdad | 1:f62247cbeac6 | 295 | char p = bytesPerSample; |
laserdad | 1:f62247cbeac6 | 296 | for (int m=0; m<numMeasEnabled; m++) { //for every enabled measurement |
laserdad | 1:f62247cbeac6 | 297 | 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 | 1:f62247cbeac6 | 298 | if (bitRead(data[m][n], 17) && ( (ecgChannel==m) || (etiChannel==m) ) ) {//handle ECG and ETI differently than PPG data. data[m][n] |= 0xFFFC0000; |
laserdad | 1:f62247cbeac6 | 299 | data[m][n] |= 0xFFFC0000; |
laserdad | 1:f62247cbeac6 | 300 | } |
laserdad | 1:f62247cbeac6 | 301 | } |
laserdad | 0:74fff521858e | 302 | } |
laserdad | 1:f62247cbeac6 | 303 | for (int n = 0; n < numSamples; n++) { //for every sample |
laserdad | 1:f62247cbeac6 | 304 | ind++; |
laserdad | 1:f62247cbeac6 | 305 | ind = ind % printEvery; |
laserdad | 1:f62247cbeac6 | 306 | |
laserdad | 1:f62247cbeac6 | 307 | //calc avg and baseline |
laserdad | 1:f62247cbeac6 | 308 | for(int m=0; m<numMeasEnabled; m++) { //for every enabled measurement |
laserdad | 1:f62247cbeac6 | 309 | dataAvg[m] += ( data[m][n] - dataAvg[m] ) >> bits2Avg[m] ; //get the running average |
laserdad | 1:f62247cbeac6 | 310 | dataBaseline[m] += ( data[m][n] - dataBaseline[m] ) >> bits2AvgBaseline[m]; //get the long baseline |
laserdad | 1:f62247cbeac6 | 311 | } |
laserdad | 0:74fff521858e | 312 | |
laserdad | 1:f62247cbeac6 | 313 | //print data |
laserdad | 1:f62247cbeac6 | 314 | if (ind == 1) { //printing only every specified number of samples to reduce serial traffic to manageable level |
laserdad | 1:f62247cbeac6 | 315 | for (int m=0; m<numMeasEnabled; m++) { //for every enabled measurement |
laserdad | 1:f62247cbeac6 | 316 | if(bits2AvgBaseline[m]>0) { |
laserdad | 1:f62247cbeac6 | 317 | pc.printf("%i, ",dataAvg[m]-dataBaseline[m]); //print with baseline subtraction |
laserdad | 1:f62247cbeac6 | 318 | } else { |
laserdad | 1:f62247cbeac6 | 319 | pc.printf("%i, ", dataAvg[m]); //print without baseline subtraction |
laserdad | 1:f62247cbeac6 | 320 | } |
laserdad | 1:f62247cbeac6 | 321 | } |
laserdad | 1:f62247cbeac6 | 322 | pc.printf("\r\n"); |
laserdad | 1:f62247cbeac6 | 323 | } //end print loop |
laserdad | 1:f62247cbeac6 | 324 | } //end sample loop |
laserdad | 0:74fff521858e | 325 | }//end printData() |
laserdad | 0:74fff521858e | 326 | |
laserdad | 0:74fff521858e | 327 | |
laserdad | 0:74fff521858e | 328 | |
laserdad | 0:74fff521858e | 329 | |
laserdad | 0:74fff521858e | 330 | //for debugging |
laserdad | 1:f62247cbeac6 | 331 | void printRawFIFO(int numSamples,char *fifodata) |
laserdad | 1:f62247cbeac6 | 332 | { |
laserdad | 1:f62247cbeac6 | 333 | // Serial.print("FIFO bytes "); |
laserdad | 1:f62247cbeac6 | 334 | for (int n = 0; n < numSamples; n++) {//for every sample. |
laserdad | 1:f62247cbeac6 | 335 | for (int m=0; m<numMeasEnabled; m++) { //for every kind of measurement |
laserdad | 1:f62247cbeac6 | 336 | pc.printf("%d: ",m); |
laserdad | 1:f62247cbeac6 | 337 | pc.printf("%x, %x, %x, ",fifodata[bytesPerSample * n +3*m], fifodata[bytesPerSample * n + 1 +3*m], fifodata[bytesPerSample * n + 2 + 3*m] ); |
laserdad | 1:f62247cbeac6 | 338 | } //end measurement loop |
laserdad | 1:f62247cbeac6 | 339 | pc.printf("\r\n"); |
laserdad | 1:f62247cbeac6 | 340 | } //end sample loop |
laserdad | 0:74fff521858e | 341 | } //end function |
laserdad | 0:74fff521858e | 342 | |
laserdad | 0:74fff521858e | 343 | |
laserdad | 1:f62247cbeac6 | 344 | void setupASIC(void) |
laserdad | 1:f62247cbeac6 | 345 | { |
laserdad | 0:74fff521858e | 346 | pc.printf("Running Setup\r\n"); |
laserdad | 0:74fff521858e | 347 | runSetup = 0; //only run once |
laserdad | 0:74fff521858e | 348 | i2c.frequency(recommendedi2cFreq); //set I2C frequency to 400kHz |
laserdad | 0:74fff521858e | 349 | // intPin.mode(PullUp); //pullups on the sensor board |
laserdad | 0:74fff521858e | 350 | //configure MAX86150 register settings |
laserdad | 0:74fff521858e | 351 | initMAX86150(); |
laserdad | 1:f62247cbeac6 | 352 | pc.printf("register dump\r\n"); |
laserdad | 0:74fff521858e | 353 | |
laserdad | 0:74fff521858e | 354 | // //print register configuration |
laserdad | 0:74fff521858e | 355 | // regDump(MAX86150_Addr,0x00, 0x06); |
laserdad | 0:74fff521858e | 356 | // regDump(MAX86150_Addr,0x08, 0x15); |
laserdad | 0:74fff521858e | 357 | // regDump(MAX86150_Addr,0x3C, 0x3F); |
laserdad | 0:74fff521858e | 358 | // //go to test mode |
laserdad | 0:74fff521858e | 359 | // writeRegister(MAX86150_Addr,0xFF,0x54); |
laserdad | 0:74fff521858e | 360 | // writeRegister(MAX86150_Addr,0xFF,0x4D); |
laserdad | 0:74fff521858e | 361 | // regDump(MAX86150_Addr,0xCE, 0xCF); |
laserdad | 0:74fff521858e | 362 | // regDump(MAX86150_Addr,0xD0, 0xD0); |
laserdad | 0:74fff521858e | 363 | // writeRegister(MAX86150_Addr,0xFF,0x00); |
laserdad | 0:74fff521858e | 364 | clearInterrupts(i2cReadBuffer); |
laserdad | 1:f62247cbeac6 | 365 | i2c.frequency(maxi2cFreq); |
laserdad | 0:74fff521858e | 366 | //configure averaging |
laserdad | 0:74fff521858e | 367 | if(ppgOn) { |
laserdad | 0:74fff521858e | 368 | bits2Avg[rChannel]=ppgBits2Avg; |
laserdad | 0:74fff521858e | 369 | bits2Avg[irChannel]=ppgBits2Avg; |
laserdad | 0:74fff521858e | 370 | bits2AvgBaseline[rChannel]=ppgBaselineBits; |
laserdad | 0:74fff521858e | 371 | bits2AvgBaseline[irChannel]=ppgBaselineBits; |
laserdad | 0:74fff521858e | 372 | } |
laserdad | 1:f62247cbeac6 | 373 | if(ecgOn) { |
laserdad | 0:74fff521858e | 374 | bits2Avg[ecgChannel]=ecgBits2Avg; |
laserdad | 0:74fff521858e | 375 | bits2AvgBaseline[ecgChannel]=ecgBaselineBits; |
laserdad | 0:74fff521858e | 376 | } |
laserdad | 1:f62247cbeac6 | 377 | if(etiOn) { |
laserdad | 0:74fff521858e | 378 | bits2Avg[etiChannel]=etiBits2Avg; |
laserdad | 0:74fff521858e | 379 | bits2AvgBaseline[etiChannel]=etiBaselineBits; |
laserdad | 0:74fff521858e | 380 | } |
laserdad | 0:74fff521858e | 381 | pc.printf("Done w/setup\r\n"); |
laserdad | 0:74fff521858e | 382 | } |
laserdad | 0:74fff521858e | 383 | |
laserdad | 0:74fff521858e | 384 | |
laserdad | 0:74fff521858e | 385 | |
laserdad | 1:f62247cbeac6 | 386 | int main() |
laserdad | 1:f62247cbeac6 | 387 | { |
laserdad | 0:74fff521858e | 388 | char FIFOData[bytesPerSample]; |
laserdad | 0:74fff521858e | 389 | if(runSetup) { |
laserdad | 0:74fff521858e | 390 | setupASIC(); |
laserdad | 1:f62247cbeac6 | 391 | } |
laserdad | 1:f62247cbeac6 | 392 | |
laserdad | 0:74fff521858e | 393 | intPin.fall(&intEvent); |
laserdad | 0:74fff521858e | 394 | while(1) { |
laserdad | 1:f62247cbeac6 | 395 | char stat[1]; |
laserdad | 1:f62247cbeac6 | 396 | static int n = 0; |
laserdad | 1:f62247cbeac6 | 397 | if (intFlagged) { |
laserdad | 0:74fff521858e | 398 | // pc.printf("intFlagged\r\n"); |
laserdad | 0:74fff521858e | 399 | if (almostFullIntEn) { |
laserdad | 0:74fff521858e | 400 | n = 0; |
laserdad | 0:74fff521858e | 401 | intFlagged = 0; |
laserdad | 0:74fff521858e | 402 | readFIFO(samples2Read,FIFOData); |
laserdad | 0:74fff521858e | 403 | printData(samples2Read,FIFOData); |
laserdad | 1:f62247cbeac6 | 404 | // printRawFIFO(samples2Read,FIFOData); //for debugging |
laserdad | 1:f62247cbeac6 | 405 | } //end if AFULL |
laserdad | 1:f62247cbeac6 | 406 | else { //this is to handle end of conversion interrupts (not configured by default) |
laserdad | 1:f62247cbeac6 | 407 | if (n < samples2Read) { |
laserdad | 1:f62247cbeac6 | 408 | n++; |
laserdad | 1:f62247cbeac6 | 409 | intFlagged = 0; |
laserdad | 1:f62247cbeac6 | 410 | clearInterrupts(stat); |
laserdad | 1:f62247cbeac6 | 411 | // Serial.println(n); |
laserdad | 1:f62247cbeac6 | 412 | } else { |
laserdad | 1:f62247cbeac6 | 413 | n = 0; |
laserdad | 1:f62247cbeac6 | 414 | intFlagged = 0; |
laserdad | 1:f62247cbeac6 | 415 | readFIFO(samples2Read,FIFOData); |
laserdad | 1:f62247cbeac6 | 416 | printData(samples2Read,FIFOData); |
laserdad | 1:f62247cbeac6 | 417 | pc.printf("\r\n"); |
laserdad | 1:f62247cbeac6 | 418 | // printRawFIFO(samples2Read); |
laserdad | 1:f62247cbeac6 | 419 | } |
laserdad | 0:74fff521858e | 420 | } //end if/else AFULL |
laserdad | 1:f62247cbeac6 | 421 | } //end if intFlagged |
laserdad | 1:f62247cbeac6 | 422 | } //end while |
laserdad | 0:74fff521858e | 423 | |
laserdad | 0:74fff521858e | 424 | }//end main |
laserdad | 0:74fff521858e | 425 | |
laserdad | 0:74fff521858e | 426 | |
laserdad | 0:74fff521858e | 427 | |
laserdad | 0:74fff521858e | 428 | |
laserdad | 0:74fff521858e | 429 | /* FYI seee this table for effective 3dB bandwidth for IIR filter given sample rate and number of bits shifted |
laserdad | 0:74fff521858e | 430 | * fsample bits f3dB |
laserdad | 0:74fff521858e | 431 | * 1600 1 89.6 |
laserdad | 0:74fff521858e | 432 | * 1600 2 72 |
laserdad | 0:74fff521858e | 433 | * 1600 3 24 |
laserdad | 0:74fff521858e | 434 | * 1600 4 8 |
laserdad | 0:74fff521858e | 435 | * 800 1 44.8 |
laserdad | 0:74fff521858e | 436 | * 800 2 36 |
laserdad | 0:74fff521858e | 437 | * 800 3 12 |
laserdad | 0:74fff521858e | 438 | * 800 4 4 |
laserdad | 0:74fff521858e | 439 | * 400 1 22.4 |
laserdad | 0:74fff521858e | 440 | * 400 2 18 |
laserdad | 0:74fff521858e | 441 | * 400 3 6 |
laserdad | 0:74fff521858e | 442 | * 400 4 2 |
laserdad | 0:74fff521858e | 443 | * 200 1 11.2 |
laserdad | 0:74fff521858e | 444 | * 200 2 9 |
laserdad | 0:74fff521858e | 445 | * 200 3 3 |
laserdad | 0:74fff521858e | 446 | * 200 4 1 |
laserdad | 0:74fff521858e | 447 | */ |