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.
Dependencies: MAX30101 MAX32620FTHR
main.cpp
- Committer:
- johnGreeneMaxim
- Date:
- 2018-03-08
- Revision:
- 13:ef4a84158e4c
- Parent:
- 12:ac85295f8713
- Child:
- 14:b47461092164
File content as of revision 13:ef4a84158e4c:
/******************************************************************************* * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. ******************************************************************************* */ #include "mbed.h" #include "max32630fthr.h" #include "MAX30101.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> //variable for the algorithm uint16_t sampleRate =100; uint16_t compSpO2=1; int16_t ir_ac_comp =0; int16_t red_ac_comp=0; int16_t green_ac_comp=0; int16_t ir_ac_mag=0; int16_t red_ac_mag=0; int16_t green_ac_mag=0; uint16_t HRbpm2=0; uint16_t SpO2B=0; uint16_t DRdy=0; //Heart rate and SpO2 algorithm void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ); //helper functions for the heart rate and SpO2 function uint16_t avg_dc_est(int32_t *p, uint16_t x); void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) ; int32_t mul16(int16_t x, int16_t y); //set logic level to 3.3V MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3); //IC configuration functions bool op_sensor_config(MAX30101 &op_sensor); void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en); /* Op Sensor FIFO nearly full callback */ volatile bool op_sensorIntFlag = 0; void op_sensor_callback() { op_sensorIntFlag = 1; } //declare large variables outside of main uint32_t redData[500];//set array to max fifo size uint32_t irData[500];//set array to max fifo size uint32_t greenData[500];//set array to max fifo size int main() { Serial pc(USBTX, USBRX); // Use USB debug probe for serial link pc.baud(115200); // Baud rate = 115200 DigitalOut rLed(LED1, LED_OFF); // Debug LED InterruptIn op_sensor_int(P3_2); // Config P3_2 as int. in for op_sensor_int.fall(&op_sensor_callback); // FIFO ready interrupt I2C i2cBus(I2C1_SDA, I2C1_SCL); // I2C bus, P3_4 = SDA, P3_5 = SCL DigitalOut VLED_EN(P3_3,0); //Enable for VLEDs pmic_config(i2cBus, VLED_EN); MAX30101 op_sensor(i2cBus); // Create new MAX30101 on i2cBus int rc = op_sensor_config(op_sensor); // Config sensor, return 0 on success MAX30101::InterruptBitField_u ints; // Read interrupt status to clear rc = op_sensor.getInterruptStatus(ints); // power on interrupt uint8_t fifoData[MAX30101::MAX_FIFO_BYTES]; uint16_t idx, readBytes; int32_t opSample; uint32_t sample; uint16_t HRTemp; uint16_t spo2Temp; int r=0; //counter for redData position int ir=0; //counter for irData position int g =0; //counter for greenData position int c=0; //counter to print values pc.printf("Starting Program...Please wait a few seconds while data is being collected.\r\n"); while(1) { if( rc == 0 ) { // Check if op_sensor interrupt asserted if(op_sensorIntFlag) { //pc.printf("Entered op_sensorIntFlag check\r\n"); op_sensorIntFlag = 0; // Lower interrupt flag rc = op_sensor.getInterruptStatus(ints); // Read interrupt status //to clear interrupt // Confirms proper read prior to executing if((rc == 0)) { // Read FIFO rc = op_sensor.readFIFO(MAX30101::ThreeLedChannels, fifoData, readBytes); if(rc == 0) { // Convert read bytes into samples for (idx = 0; idx < readBytes; idx+=9) { if (r >= 500 || ir >= 500 || g >= 500) { pc.printf("Overflow!"); } redData[r++] = ((fifoData[idx] << 16) | (fifoData[idx + 1] << 8) | (fifoData[idx + 2])) & 0x03FFFF; irData[ir++] = ((fifoData[idx + 3] << 16) | (fifoData[idx + 4] << 8) | (fifoData[idx + 5])) & 0x03FFFF; greenData[g++] = ((fifoData[idx + 6] << 16) | (fifoData[idx + 7] << 8) | (fifoData[idx + 8])) & 0x03FFFF; } if(r>=500 && ir>=500 && g>500)//checks to make sure there are 500 //samples in data buffers { //runs the heart rate and SpO2 algorithm for(c=0, HRTemp = 0; c<r; c++) { HRSpO2Func(irData[c], redData[c],greenData[c], c,sampleRate, compSpO2, &ir_ac_comp,&red_ac_comp, &green_ac_comp, &ir_ac_mag,&red_ac_mag, &green_ac_mag, &HRbpm2,&SpO2B,&DRdy); if(DRdy) { HRTemp = HRbpm2; spo2Temp = SpO2B; } } //If the above algorithm returns a valid heart rate on the last sample, it is printed if(DRdy==1) { pc.printf("Heart Rate = %i\r\n",HRbpm2); pc.printf("SPO2 = %i\r\n",SpO2B); } else if (HRTemp!=0)//if a valid heart was calculated at all, it is printed { pc.printf("Heart Rate = %i\r\n",HRTemp); pc.printf("SPO2 = %i\r\n",spo2Temp); } else { pc.printf("Calculation failed...waiting for more samples...\r\n"); pc.printf("Please keep your finger on the MAX30101 sensor with minimal movement.\r\n"); } //dump the first hundred samples after caluclaiton for(c=100; c<500; c++) { redData[c-100]=redData[c]; irData[c-100]=irData[c]; greenData[c-100] = greenData[c]; } //reset counters r=400; ir=400; g=400; } } } } // If rc != 0, a communication error has occurred } else { pc.printf("Something went wrong, " "check the I2C bus or power connections... \r\n"); //bLed = LED_OFF; //gLed = LED_OFF; while(1) { rLed = !rLed; wait(0.5); } } } } bool op_sensor_config(MAX30101 &op_sensor) { //Reset Device MAX30101::ModeConfiguration_u modeConfig; modeConfig.all = 0; modeConfig.bits.reset = 1; modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets SPO2 Mode int32_t rc = op_sensor.setModeConfiguration(modeConfig); //enable MAX30101 interrupts MAX30101::InterruptBitField_u ints; if(rc == 0) { ints.all = 0; ints.bits.a_full = 1; // Enable FIFO almost full interrupt ints.bits.ppg_rdy =1; //Enables an interrupt when a new sample is ready rc = op_sensor.enableInterrupts(ints); } //configure FIFO MAX30101::FIFO_Configuration_u fifoConfig; if(rc == 0) { fifoConfig.all = 0; fifoConfig.bits.fifo_a_full = 10; // Max level of 17 samples fifoConfig.bits.sample_average = MAX30101::AveragedSamples_0;// Average 0 samples rc = op_sensor.setFIFOConfiguration(fifoConfig); } MAX30101::SpO2Configuration_u spo2Config; if(rc == 0) { spo2Config.all = 0; // clears register spo2Config.bits.spo2_adc_range = 1; //sets resolution to 4096 nAfs spo2Config.bits.spo2_sr = MAX30101::SR_100_Hz; // SpO2 SR = 100Hz spo2Config.bits.led_pw = MAX30101::PW_3; // 18-bit ADC resolution ~400us rc = op_sensor.setSpO2Configuration(spo2Config); } //Set time slots for LEDS MAX30101::ModeControlReg_u multiLED; if(rc==0) { //sets timing for control register 1 multiLED.bits.lo_slot=1; multiLED.bits.hi_slot=2; rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg1, multiLED); if(rc==0) { multiLED.bits.lo_slot=3; multiLED.bits.hi_slot=0; rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg2, multiLED); } } //Set LED drive currents if(rc == 0) { // Heart Rate only, 1 LED channel, Pulse amp. = ~7mA rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24); //To include SPO2, 2 LED channel, Pulse amp. ~7mA if(rc==0) { rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24); } if(rc==0) { rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED3_PA, 0x24); } } //Set operating mode modeConfig.all = 0; if(rc == 0) { modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets multiLED mode rc = op_sensor.setModeConfiguration(modeConfig); } return rc; } void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en) { const uint8_t PMIC_ADRS = 0x54; const uint8_t BBB_EXTRA_ADRS = 0x1C; const uint8_t BOOST_VOLTAGE = 0x05; char data_buff[] = {BBB_EXTRA_ADRS, 0x40}; //BBBExtra register address //and data to enable passive //pull down. i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register data_buff[0] = BOOST_VOLTAGE; data_buff[1] = 0x08; //Boost voltage configuration //register followed by data //to set voltage to 4.5V 1f pmic_en = 0; //disables VLED 08 i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register pmic_en = 1; //enables VLED } // // Heart Rate/SpO2 Monitor function takes sample input 'dinIR' and dinRed. // Other inputs: // ns -> Sample Counter, increments with each sample input. // SampRate -> Input data real-time sample rate. // dinLShft -> Number of left shifts for data to be 16 bit wide. // compSpO2 -> If '1' compute SpO2 value,else compute HR only. // // Outputs: // ir_ac_comp -> AC component of the IR signal. // red_ac_comp -> AC component of the Red signal. // ir_ac_mag -> Peak to Peak magnitude of the IR signal. // red_ac_mag -> Peak to Peak magnitude of the Red signal. // HRbpm -> Heart Rate in beats per minute. // SpO2 -> SpO2 value as %saturation. // DRdy -> '1' when new data is available. // void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ) { static int32_t ir_avg_reg=0; static int32_t red_avg_reg=0; static int32_t green_avg_reg=0; static int16_t ir_ac_sig_cur=0; static int16_t ir_ac_sig_pre; static int16_t ir_ac_sig_min=0; static int16_t ir_ac_sig_max=0; static int16_t ir_avg_est; static int16_t ir_pedge=0, ir_nedge=0; static int16_t ir_pzxic, ir_pzxip; static int16_t ir_nzxic; static int16_t red_ac_sig_cur=0; static int16_t red_ac_sig_min=0; static int16_t red_ac_sig_max=0; static int16_t red_avg_est; static int16_t green_avg_est; static int16_t green_ac_sig_cur=0; //static int16_t green_ac_sig_cur=0; static int16_t green_ac_sig_pre; static int16_t green_ac_sig_max ; static int16_t green_ac_sig_min; static int16_t green_mac_FIFO[5]; int16_t meanGreenMagFIFO; int16_t minAmpForHeartBeat ; uint32_t IRData,RedData, greenData , rnum,rden,rdens; uint16_t zeros_in_HrQue =0 , posCount=0; static uint32_t prevPeakLoc = 0 ; static int16_t IrFIFO[100]; static int16_t HrQue[10], lastKnownGoodHr[10]; static int16_t SPO2Que[5]; int16_t SPO2score[5]; static uint16_t HrQindex=0 ,lengthOfposCountExceeding =0 ; static uint16_t initHrQueCounter=0 , fingerOff =0; static int16_t HrQueSmoothing[3]; static int16_t SPO2QueSmoothing[3]; int16_t k, j; uint32_t peakLoc ; int16_t bufferIdx1, bufferIdx2; int16_t maxFIFO ,IdxMaxFIFO ; int16_t HRperiod2, HRComp2 ,deltaHR; int16_t cSpO2, SpO2; int16_t HrCount =0, HrSum =0 ,meanGreenMagFIFOcounter =0; int16_t SPO2D ,meanHrQ; int16_t dx[99] ,cumsumX[99]; static int16_t SPO2QueCounter =0 ;//, lastDisplayedHrValue; int16_t validSPO2Count =0; int16_t validSPO2Sum =0; int16_t SPO2scoreAverage= 0; int16_t SPO2scoreSum =0 ; // int16_t deltaMeanLastKnownGoodHr=0,meanLastKnownGoodHr =0 ; // int16_t counterMeanLastKnownGoodHr =0 ; // clear some vars if fresh new start if ((ns ==0) || (fingerOff >300)) { ir_avg_reg=0; red_avg_reg=0; green_avg_reg=0; ir_ac_sig_cur=0; ir_ac_sig_pre=0; ir_ac_sig_min=0; ir_ac_sig_max=0; ir_avg_est=0; green_avg_est =0; red_avg_est =0 ; ir_pedge=0; ir_nedge=0; ir_pzxic=0; ir_pzxip =0; ir_nzxic=0 ; //ir_nzxip =0; red_ac_sig_cur=0; red_ac_sig_min=0; red_ac_sig_max=0; prevPeakLoc = 0 ; bufferIdx1=0 ; bufferIdx2=0; HrQindex =0; initHrQueCounter=0; lengthOfposCountExceeding =0 ; fingerOff =0; HRComp2 =0; for (k=0 ; k<100 ; k++) { IrFIFO[k]= 0; } for (k=0 ; k<10 ; k++) { HrQue[k]= 0; lastKnownGoodHr[k]=0; } for (k=0 ; k<3 ; k++) { HrQueSmoothing[k]= 70; SPO2QueSmoothing[k]=97; } for (k=0 ; k<5 ; k++) { SPO2Que[k] =97; SPO2score[k] =0; green_mac_FIFO[k] =0; } SPO2QueCounter =0; *SpO2B =97; *HRbpm2 = 0; *DRdy =0 ; } // Save current state green_ac_sig_pre = green_ac_sig_cur; // // Process next data sample minAmpForHeartBeat =0; IRData = dinIR; RedData = dinRed; greenData = dinGreen ; ir_avg_est = avg_dc_est(&ir_avg_reg,IRData); red_avg_est = avg_dc_est(&red_avg_reg,RedData); green_avg_est = avg_dc_est(&green_avg_reg,greenData); lp_dfir_flt((uint16_t)(IRData - ir_avg_est),(uint16_t)(RedData - red_avg_est), (uint16_t)(greenData - green_avg_est), &ir_ac_sig_cur,&red_ac_sig_cur,&green_ac_sig_cur); *ir_ac_comp = ir_ac_sig_cur; *red_ac_comp = red_ac_sig_cur; *green_ac_comp = green_ac_sig_cur; //save to FIFO for (k=1 ; k<100 ; k++) { IrFIFO[100 -k]= IrFIFO[99-k]; } IrFIFO[0] = green_ac_sig_cur ; // invert for (k=0 ; k<97 ; k++) dx[k]= IrFIFO[k+2]-IrFIFO[k] ; dx[97]= dx[96]; dx[98]=dx[96]; for (k=0 ; k<99 ; k++) { if (dx[k] > 0 ) dx[k]=1; else dx[k] = 0; } cumsumX[0] =0; for (k=1; k<99 ; k++) { if (dx[k]>0 ) cumsumX[k]= cumsumX[k-1] + dx[k] ; else cumsumX[k]= 0; } // determine noise // ignore less than 3 conseuctive non-zeros's // detect # of sign change posCount=0; for (k=1; k<99 ; k++) { if (cumsumX[k]>0 ) { posCount ++ ; } else if (cumsumX[k]==0 ) { if (posCount<4 && k>=4) { for ( j= k-1; j> k-posCount-1; j--) cumsumX[j]=0 ; } posCount =0; } } // ignore less than 3 conseuctive zeros's posCount=0; for (k=1; k<99 ; k++) { if (cumsumX[k]==0 ) { posCount ++ ; } else if (cumsumX[k] > 0 ) { if (posCount<4 && k>=4) { for ( j= k-1; j> k-posCount-1; j--) cumsumX[j]=100 ; } posCount =0; } } //// detect # of sign change posCount=0; // sign change counter for (k=0; k<98 ; k++) { if (cumsumX[k]==0 && cumsumX[k+1] > 0 ) { posCount ++; } } if (posCount>=4) { lengthOfposCountExceeding ++ ; // printf("PosCount =%i \n", posCount ); } else lengthOfposCountExceeding = 0 ; // Detect IR channel positive zero crossing (rising edge) if ((green_ac_sig_pre < 0) && (green_ac_sig_cur >= 0) && fingerOff==0 ) { *ir_ac_mag = ir_ac_sig_max - ir_ac_sig_min; *red_ac_mag = red_ac_sig_max - red_ac_sig_min; *green_ac_mag = green_ac_sig_max - green_ac_sig_min; if ( *green_ac_mag>0 ) { for (k=0; k<4 ; k++) green_mac_FIFO[k]=green_mac_FIFO[k+1]; green_mac_FIFO[4] = *green_ac_mag ; if ( green_mac_FIFO[4] > 1000) green_mac_FIFO[4] =1000; } meanGreenMagFIFO=0; meanGreenMagFIFOcounter=0; for (k=0; k<5 ; k++) { if( green_mac_FIFO[k] >0) { meanGreenMagFIFO= meanGreenMagFIFO +green_mac_FIFO[k] ; meanGreenMagFIFOcounter++; } } if (meanGreenMagFIFOcounter>=2 ) { meanGreenMagFIFO =meanGreenMagFIFO/ meanGreenMagFIFOcounter ; minAmpForHeartBeat= meanGreenMagFIFO /4 ; //25% of mean of past heart beat } else minAmpForHeartBeat = 75; if (minAmpForHeartBeat <75) minAmpForHeartBeat =75; if (minAmpForHeartBeat >400) minAmpForHeartBeat =400; ir_pedge = 1; ir_nedge = 0; ir_ac_sig_max = 0; ir_pzxip = ir_pzxic; ir_pzxic = ns; bufferIdx1= ir_pzxic- ir_nzxic; bufferIdx2 = ir_pzxic -ir_pzxip; if ((*green_ac_mag)> minAmpForHeartBeat && (*green_ac_mag)< 20000 && bufferIdx1>=0 && bufferIdx1 <100 && bufferIdx2>=0 && bufferIdx2 <100 && bufferIdx1< bufferIdx2 ) { // was <5000 maxFIFO = -32766; IdxMaxFIFO = 0; for ( j=bufferIdx1; j<= bufferIdx2; j++) { // find max peak if (IrFIFO[j] > maxFIFO ) { maxFIFO =IrFIFO[j]; IdxMaxFIFO =j; } } peakLoc= ir_pzxic -IdxMaxFIFO+1 ; if (prevPeakLoc !=0) { HRperiod2 =( uint16_t) (peakLoc - prevPeakLoc); if (HRperiod2>33 && HRperiod2 < 134) { HRComp2= (6000/HRperiod2); fingerOff =0 ; } else HRComp2=0 ; } else HRComp2 = 0 ; if ( initHrQueCounter<10 && HRComp2 >0 ) { HrQue[HrQindex] =HRComp2; HrQindex++; initHrQueCounter ++; if (HrQindex== 10) HrQindex =0; } if (initHrQueCounter > 7 && lengthOfposCountExceeding<=3) { if ( HRComp2 > 0) { HrCount =0; HrSum =0; zeros_in_HrQue=0; for (k=1 ; k<initHrQueCounter ; k++) { if (HrQue[k] >0) { HrSum +=HrQue[k]; HrCount ++; } else zeros_in_HrQue ++; } meanHrQ = HrSum/HrCount ; deltaHR= lastKnownGoodHr[0]/10; if ( HRComp2 > lastKnownGoodHr[0] -deltaHR && HRComp2 <lastKnownGoodHr[0] +deltaHR ) { for (k=1 ; k<10 ; k++) { HrQue[10 -k]= HrQue[9-k]; } HrQue[0] =HRComp2; } // HR smmothing using FIFO queue - if (zeros_in_HrQue<=2) { for (k=1 ; k<3 ; k++) { HrQueSmoothing[3 -k]= HrQueSmoothing[2-k]; } HrQueSmoothing[0] = meanHrQ ; HRComp2 = ( (HrQueSmoothing[0]<<2) + (HrQueSmoothing[1]<<1) + (HrQueSmoothing[2] <<1) ) >>3; *HRbpm2 =HRComp2 ; for (k=1 ; k<10 ; k++) { lastKnownGoodHr[10 -k]= lastKnownGoodHr[9-k]; } lastKnownGoodHr[0] =HRComp2; } } } ///// if (initHrQueCounter > 7 && lengthOfposCountExceeding<5) else if (initHrQueCounter < 7) { // before que is filled up, display whatever it got. *HRbpm2 = HRComp2; } else { // *HRbpm2 = 0 ; HrCount =0; HrSum =0; for (k=0 ; k<10 ; k++) { if (lastKnownGoodHr[k] >0) { HrSum =HrSum + lastKnownGoodHr[k]; HrCount++; } } if (HrCount>0) *HRbpm2 = HrSum/HrCount; else *HRbpm2 = 0; } prevPeakLoc = peakLoc ; // save peakLoc into Static var if (compSpO2) { rnum = (ir_avg_reg >> 20)*(*red_ac_mag); rden = (red_avg_reg >> 20)*(*ir_ac_mag); rdens = (rden>>15); if (rdens>0) cSpO2 = 110- (((25*rnum)/(rdens))>>15); if (cSpO2 >=100) SpO2 = 100; else if (cSpO2 <= 70) SpO2 = 70; else SpO2 = cSpO2; SPO2Que[SPO2QueCounter ] = SpO2; for (k=0 ; k<5 ; k++) { SPO2score[k] =0; for (j=0 ; j< 5 ; j++) if( abs( SPO2Que[k] - SPO2Que[j] )>5) SPO2score[k] ++; } SPO2scoreSum= 0; for (k=0 ; k<5 ; k++) SPO2scoreSum += SPO2score[k] ; SPO2scoreAverage= SPO2scoreSum / 5; for (k=1 ; k<5 ; k++) SPO2score[k] = SPO2score[k] -SPO2scoreAverage; validSPO2Count =0; validSPO2Sum =0; for (k=1 ; k<5 ; k++) { if (SPO2score[k]<=0 ) { // add for HR to report validSPO2Sum +=SPO2Que[k]; validSPO2Count ++; } } if ( validSPO2Count>0) SPO2D = (validSPO2Sum /validSPO2Count)-1; if ( SPO2D >100) SPO2D = 100; SPO2QueCounter ++; if (SPO2QueCounter ==5) SPO2QueCounter = 0; for (k=1 ; k<3 ; k++) { SPO2QueSmoothing[3 -k]= SPO2QueSmoothing[2-k]; } SPO2QueSmoothing[0] = SPO2D; *SpO2B = ( (SPO2QueSmoothing[0]<<2) + (SPO2QueSmoothing[1]<<1) + (SPO2QueSmoothing[2] <<1) ) >>3; if (*SpO2B> 100) *SpO2B = 100 ; } else { SpO2 = 0; *SpO2B = 0; } *DRdy = 1; } } // Detect IR channel negative zero crossing (falling edge) if ((green_ac_sig_pre > 0) && (green_ac_sig_cur <= 0)) { ir_pedge = 0; ir_nedge = 1; ir_ac_sig_min = 0; ir_nzxic = ns; } // Find Maximum IR & Red values in positive cycle if (ir_pedge && (green_ac_sig_cur > green_ac_sig_pre)) { ir_ac_sig_max = ir_ac_sig_cur; red_ac_sig_max = red_ac_sig_cur; green_ac_sig_max = green_ac_sig_cur; } // Find minimum IR & Red values in negative cycle if (ir_nedge && (green_ac_sig_cur < green_ac_sig_pre)) { ir_ac_sig_min = ir_ac_sig_cur; red_ac_sig_min = red_ac_sig_cur; green_ac_sig_min = green_ac_sig_cur; } if (IRData <50000 ) // finger-off { fingerOff++; *DRdy = 0; } else fingerOff = 0 ; /*if (IRData <50000 && fingerOff>10 ) fingerOff = 0; */ if ( *SpO2B == 0 || *HRbpm2 ==0) *DRdy = 0; /*if (ns > 2000 ) { if (abs(lastDisplayedHrValue - *HRbpm2)>5) *HRbpm2 =lastDisplayedHrValue ; else lastDisplayedHrValue = *HRbpm2; }*/ // *DRdy = minAmpForHeartBeat; } // Average DC Estimator uint16_t avg_dc_est(int32_t *p, uint16_t x) { *p += ((((int32_t) x << 15) - *p)>>4); return (*p >> 15); } // Symmetric Dual Low Pass FIR Filter void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) { static const uint16_t FIRCoeffs[12] = {688,1283,2316,3709,5439,7431, 9561,11666,13563,15074,16047,16384 }; static int16_t cbuf0[32],cbuf1[32] , cbuf2[32]; static int16_t offset = 0; int32_t y0,y1, y2; int16_t i; cbuf0[offset] = din0; cbuf1[offset] = din1; cbuf2[offset] = din2; y0 = mul16(FIRCoeffs[11], cbuf0[(offset - 11)&0x1F]); y1 = mul16(FIRCoeffs[11], cbuf1[(offset - 11)&0x1F]); y2 = mul16(FIRCoeffs[11], cbuf2[(offset - 11)&0x1F]); for (i=0; i<11; i++) { y0 += mul16(FIRCoeffs[i], cbuf0[(offset-i)&0x1F] + cbuf0[(offset-22+i)&0x1F]); y1 += mul16(FIRCoeffs[i], cbuf1[(offset-i)&0x1F] + cbuf1[(offset-22+i)&0x1F]); y2 += mul16(FIRCoeffs[i], cbuf2[(offset-i)&0x1F] + cbuf2[(offset-22+i)&0x1F]); } offset = (offset + 1)&0x1F; *dout0 = (y0>>15); *dout1 = (y1>>15); *dout2 = (y2>>15); } // Integer multiplier int32_t mul16(int16_t x, int16_t y) { return (int32_t)(x* y); }