the MAX30102 driver for LPC1768 board

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
anning865
Date:
Fri Oct 26 10:30:22 2018 +0000
Commit message:
The MAX30102 driver for LPC1768

Changed in this revision

MAX30102/MAX30102.cpp Show annotated file Show diff for this revision Revisions of this file
MAX30102/MAX30102.h Show annotated file Show diff for this revision Revisions of this file
algorithm/algorithm.cpp Show annotated file Show diff for this revision Revisions of this file
algorithm/algorithm.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 82a90765baa2 MAX30102/MAX30102.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX30102/MAX30102.cpp	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,216 @@
+/** \file max30102.cpp ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: max30102.cpp
+* Description: This module is an embedded controller driver for the MAX30102
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 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 "MAX30102.h"
+
+I2C i2c(p9,p10);//p9-sda,p10-scl
+
+bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
+/**
+* \brief        Write a value to a MAX30102 register
+* \par          Details
+*               This function writes a value to a MAX30102 register
+*
+* \param[in]    uch_addr    - register address
+* \param[in]    uch_data    - register data
+*
+* \retval       true on success
+*/
+{
+  char ach_i2c_data[2];
+  ach_i2c_data[0]=uch_addr;
+  ach_i2c_data[1]=uch_data;
+  
+  if(i2c.write(I2C_WRITE_ADDR, ach_i2c_data, 2, false)==0)
+    return true;
+  else
+    return false;
+}
+
+bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
+/**
+* \brief        Read a MAX30102 register
+* \par          Details
+*               This function reads a MAX30102 register
+*
+* \param[in]    uch_addr    - register address
+* \param[out]   puch_data    - pointer that stores the register data
+*
+* \retval       true on success
+*/
+{
+  char ch_i2c_data;
+  ch_i2c_data=uch_addr;
+  if(i2c.write(I2C_WRITE_ADDR, &ch_i2c_data, 1, true)!=0)
+    return false;
+  if(i2c.read(I2C_READ_ADDR, &ch_i2c_data, 1, false)==0)
+  {
+    *puch_data=(uint8_t) ch_i2c_data;
+    return true;
+  }
+  else
+    return false;
+}
+
+bool maxim_max30102_init()
+/**
+* \brief        Initialize the MAX30102
+* \par          Details
+*               This function initializes the MAX30102
+*
+* \param        None
+*
+* \retval       true on success
+*/
+{
+  if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1,0xc0)) // INTR setting
+    return false;
+  if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2,0x00))
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR,0x00))  //FIFO_WR_PTR[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_OVF_COUNTER,0x00))  //OVF_COUNTER[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR,0x00))  //FIFO_RD_PTR[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_CONFIG,0x0f))  //sample avg = 1, fifo rollover=false, fifo almost full = 17
+    return false;
+  if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x03))   //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
+    return false;
+  if(!maxim_max30102_write_reg(REG_SPO2_CONFIG,0x27))  // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
+    return false;
+  
+  if(!maxim_max30102_write_reg(REG_LED1_PA,0x24))   //Choose value for ~ 7mA for LED1
+    return false;
+  if(!maxim_max30102_write_reg(REG_LED2_PA,0x24))   // Choose value for ~ 7mA for LED2
+    return false;
+  if(!maxim_max30102_write_reg(REG_PILOT_PA,0x7f))   // Choose value for ~ 25mA for Pilot LED
+    return false;
+  return true;  
+}
+
+bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
+/**
+* \brief        Read a set of samples from the MAX30102 FIFO register
+* \par          Details
+*               This function reads a set of samples from the MAX30102 FIFO register
+*
+* \param[out]   *pun_red_led   - pointer that stores the red LED reading data
+* \param[out]   *pun_ir_led    - pointer that stores the IR LED reading data
+*
+* \retval       true on success
+*/
+{
+  uint32_t un_temp;
+  unsigned char uch_temp;
+  *pun_red_led=0;
+  *pun_ir_led=0;
+  char ach_i2c_data[6];
+  
+  //read and clear status register
+  maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
+  maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);
+  
+  ach_i2c_data[0]=REG_FIFO_DATA;
+  if(i2c.write(I2C_WRITE_ADDR, ach_i2c_data, 1, true)!=0)
+    return false;
+  if(i2c.read(I2C_READ_ADDR, ach_i2c_data, 6, false)!=0)
+  {
+    return false;
+  }
+  un_temp=(unsigned char) ach_i2c_data[0];
+  un_temp<<=16;
+  *pun_red_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[1];
+  un_temp<<=8;
+  *pun_red_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[2];
+  *pun_red_led+=un_temp;
+  
+  un_temp=(unsigned char) ach_i2c_data[3];
+  un_temp<<=16;
+  *pun_ir_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[4];
+  un_temp<<=8;
+  *pun_ir_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[5];
+  *pun_ir_led+=un_temp;
+  *pun_red_led&=0x03FFFF;  //Mask MSB [23:18]
+  *pun_ir_led&=0x03FFFF;  //Mask MSB [23:18]
+  
+  
+  return true;
+}
+
+bool maxim_max30102_reset()
+/**
+* \brief        Reset the MAX30102
+* \par          Details
+*               This function resets the MAX30102
+*
+* \param        None
+*
+* \retval       true on success
+*/
+{
+    if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x40))
+        return false;
+    else
+        return true;    
+}
diff -r 000000000000 -r 82a90765baa2 MAX30102/MAX30102.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX30102/MAX30102.h	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,97 @@
+/** \file max30102.h ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: max30102.h
+* Description: This module is an embedded controller driver header file for MAX30102
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 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.
+*******************************************************************************
+*/
+#ifndef MAX30102_H_
+#define MAX30102_H_
+
+#include "mbed.h"
+
+#define I2C_WRITE_ADDR 0xAE
+#define I2C_READ_ADDR 0xAF
+
+//register addresses
+#define REG_INTR_STATUS_1 0x00
+#define REG_INTR_STATUS_2 0x01
+#define REG_INTR_ENABLE_1 0x02
+#define REG_INTR_ENABLE_2 0x03
+#define REG_FIFO_WR_PTR 0x04
+#define REG_OVF_COUNTER 0x05
+#define REG_FIFO_RD_PTR 0x06
+#define REG_FIFO_DATA 0x07
+#define REG_FIFO_CONFIG 0x08
+#define REG_MODE_CONFIG 0x09
+#define REG_SPO2_CONFIG 0x0A
+#define REG_LED1_PA 0x0C
+#define REG_LED2_PA 0x0D
+#define REG_PILOT_PA 0x10
+#define REG_MULTI_LED_CTRL1 0x11
+#define REG_MULTI_LED_CTRL2 0x12
+#define REG_TEMP_INTR 0x1F
+#define REG_TEMP_FRAC 0x20
+#define REG_TEMP_CONFIG 0x21
+#define REG_PROX_INT_THRESH 0x30
+#define REG_REV_ID 0xFE
+#define REG_PART_ID 0xFF
+
+bool maxim_max30102_init();
+bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led);
+bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data);
+bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data);
+bool maxim_max30102_reset(void);
+
+#endif /* MAX30102_H_ */
\ No newline at end of file
diff -r 000000000000 -r 82a90765baa2 algorithm/algorithm.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/algorithm/algorithm.cpp	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,363 @@
+/** \file algorithm.cpp ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: algorithm.cpp
+* Description: This module calculates the heart rate/SpO2 level
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 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 "algorithm.h"
+#include "mbed.h"
+
+void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer,  int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, 
+                              int32_t *pn_heart_rate, int8_t  *pch_hr_valid)
+/**
+* \brief        Calculate the heart rate and SpO2 level
+* \par          Details
+*               By detecting  peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the ratio for the SPO2 is computed.
+*               Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
+*               Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each ratio.
+*
+* \param[in]    *pun_ir_buffer           - IR sensor data buffer
+* \param[in]    n_ir_buffer_length      - IR sensor data buffer length
+* \param[in]    *pun_red_buffer          - Red sensor data buffer
+* \param[out]    *pn_spo2                - Calculated SpO2 value
+* \param[out]    *pch_spo2_valid         - 1 if the calculated SpO2 value is valid
+* \param[out]    *pn_heart_rate          - Calculated heart rate value
+* \param[out]    *pch_hr_valid           - 1 if the calculated heart rate value is valid
+*
+* \retval       None
+*/
+{
+    uint32_t un_ir_mean ,un_only_once ;
+    int32_t k ,n_i_ratio_count;
+    int32_t i, s, m, n_exact_ir_valley_locs_count ,n_middle_idx;
+    int32_t n_th1, n_npks,n_c_min;      
+    int32_t an_ir_valley_locs[15] ;
+    int32_t an_exact_ir_valley_locs[15] ;
+    int32_t an_dx_peak_locs[15] ;
+    int32_t n_peak_interval_sum;
+    
+    int32_t n_y_ac, n_x_ac;
+    int32_t n_spo2_calc; 
+    int32_t n_y_dc_max, n_x_dc_max; 
+    int32_t n_y_dc_max_idx, n_x_dc_max_idx; 
+    int32_t an_ratio[5],n_ratio_average; 
+    int32_t n_nume,  n_denom ;
+    // remove DC of ir signal    
+    un_ir_mean =0; 
+    for (k=0 ; k<n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
+    un_ir_mean =un_ir_mean/n_ir_buffer_length ;
+    for (k=0 ; k<n_ir_buffer_length ; k++ )  an_x[k] =  pun_ir_buffer[k] - un_ir_mean ; 
+    
+    // 4 pt Moving Average
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
+        n_denom= ( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3]);
+        an_x[k]=  n_denom/(int32_t)4; 
+    }
+
+    // get difference of smoothed IR signal
+    
+    for( k=0; k<BUFFER_SIZE-MA4_SIZE-1;  k++)
+        an_dx[k]= (an_x[k+1]- an_x[k]);
+
+    // 2-pt Moving Average to an_dx
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE-2; k++){
+        an_dx[k] =  ( an_dx[k]+an_dx[k+1])/2 ;
+    }
+    
+    // hamming window
+    // flip wave form so that we can detect valley with peak detector
+    for ( i=0 ; i<BUFFER_SIZE-HAMMING_SIZE-MA4_SIZE-2 ;i++){
+        s= 0;
+        for( k=i; k<i+ HAMMING_SIZE ;k++){
+            s -= an_dx[k] *auw_hamm[k-i] ; 
+                     }
+        an_dx[i]= s/ (int32_t)1146; // divide by sum of auw_hamm 
+    }
+
+ 
+    n_th1=0; // threshold calculation
+    for ( k=0 ; k<BUFFER_SIZE-HAMMING_SIZE ;k++){
+        n_th1 += ((an_dx[k]>0)? an_dx[k] : ((int32_t)0-an_dx[k])) ;
+    }
+    n_th1= n_th1/ ( BUFFER_SIZE-HAMMING_SIZE);
+    // peak location is acutally index for sharpest location of raw signal since we flipped the signal         
+    maxim_find_peaks( an_dx_peak_locs, &n_npks, an_dx, BUFFER_SIZE-HAMMING_SIZE, n_th1, 8, 5 );//peak_height, peak_distance, max_num_peaks 
+
+    n_peak_interval_sum =0;
+    if (n_npks>=2){
+        for (k=1; k<n_npks; k++)
+            n_peak_interval_sum += (an_dx_peak_locs[k]-an_dx_peak_locs[k -1]);
+        n_peak_interval_sum=n_peak_interval_sum/(n_npks-1);
+        *pn_heart_rate=(int32_t)(6000/n_peak_interval_sum);// beats per minutes
+        *pch_hr_valid  = 1;
+    }
+    else  {
+        *pn_heart_rate = -999;
+        *pch_hr_valid  = 0;
+    }
+            
+    for ( k=0 ; k<n_npks ;k++)
+        an_ir_valley_locs[k]=an_dx_peak_locs[k]+HAMMING_SIZE/2; 
+
+
+    // raw value : RED(=y) and IR(=X)
+    // we need to assess DC and AC value of ir and red PPG. 
+    for (k=0 ; k<n_ir_buffer_length ; k++ )  {
+        an_x[k] =  pun_ir_buffer[k] ; 
+        an_y[k] =  pun_red_buffer[k] ; 
+    }
+
+    // find precise min near an_ir_valley_locs
+    n_exact_ir_valley_locs_count =0; 
+    for(k=0 ; k<n_npks ;k++){
+        un_only_once =1;
+        m=an_ir_valley_locs[k];
+        n_c_min= 16777216;//2^24;
+        if (m+5 <  BUFFER_SIZE-HAMMING_SIZE  && m-5 >0){
+            for(i= m-5;i<m+5; i++)
+                if (an_x[i]<n_c_min){
+                    if (un_only_once >0){
+                       un_only_once =0;
+                   } 
+                   n_c_min= an_x[i] ;
+                   an_exact_ir_valley_locs[k]=i;
+                }
+            if (un_only_once ==0)
+                n_exact_ir_valley_locs_count ++ ;
+        }
+    }
+    if (n_exact_ir_valley_locs_count <2 ){
+       *pn_spo2 =  -999 ; // do not use SPO2 since signal ratio is out of range
+       *pch_spo2_valid  = 0; 
+       return;
+    }
+    // 4 pt MA
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
+        an_x[k]=( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3])/(int32_t)4;
+        an_y[k]=( an_y[k]+an_y[k+1]+ an_y[k+2]+ an_y[k+3])/(int32_t)4;
+    }
+
+    //using an_exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration ratio
+    //finding AC/DC maximum of raw ir * red between two valley locations
+    n_ratio_average =0; 
+    n_i_ratio_count =0; 
+    
+    for(k=0; k< 5; k++) an_ratio[k]=0;
+    for (k=0; k< n_exact_ir_valley_locs_count; k++){
+        if (an_exact_ir_valley_locs[k] > BUFFER_SIZE ){             
+            *pn_spo2 =  -999 ; // do not use SPO2 since valley loc is out of range
+            *pch_spo2_valid  = 0; 
+            return;
+        }
+    }
+    // find max between two valley locations 
+    // and use ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2 
+
+    for (k=0; k< n_exact_ir_valley_locs_count-1; k++){
+        n_y_dc_max= -16777216 ; 
+        n_x_dc_max= - 16777216; 
+        if (an_exact_ir_valley_locs[k+1]-an_exact_ir_valley_locs[k] >10){
+            for (i=an_exact_ir_valley_locs[k]; i< an_exact_ir_valley_locs[k+1]; i++){
+                if (an_x[i]> n_x_dc_max) {n_x_dc_max =an_x[i];n_x_dc_max_idx =i; }
+                if (an_y[i]> n_y_dc_max) {n_y_dc_max =an_y[i];n_y_dc_max_idx=i;}
+            }
+            n_y_ac= (an_y[an_exact_ir_valley_locs[k+1]] - an_y[an_exact_ir_valley_locs[k] ] )*(n_y_dc_max_idx -an_exact_ir_valley_locs[k]); //red
+            n_y_ac=  an_y[an_exact_ir_valley_locs[k]] + n_y_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k])  ; 
+        
+        
+            n_y_ac=  an_y[n_y_dc_max_idx] - n_y_ac;    // subracting linear DC compoenents from raw 
+            n_x_ac= (an_x[an_exact_ir_valley_locs[k+1]] - an_x[an_exact_ir_valley_locs[k] ] )*(n_x_dc_max_idx -an_exact_ir_valley_locs[k]); // ir
+            n_x_ac=  an_x[an_exact_ir_valley_locs[k]] + n_x_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k]); 
+            n_x_ac=  an_x[n_y_dc_max_idx] - n_x_ac;      // subracting linear DC compoenents from raw 
+            n_nume=( n_y_ac *n_x_dc_max)>>7 ; //prepare X100 to preserve floating value
+            n_denom= ( n_x_ac *n_y_dc_max)>>7;
+            if (n_denom>0  && n_i_ratio_count <5 &&  n_nume != 0)
+            {   
+                an_ratio[n_i_ratio_count]= (n_nume*100)/n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
+                n_i_ratio_count++;
+            }
+        }
+    }
+
+    maxim_sort_ascend(an_ratio, n_i_ratio_count);
+    n_middle_idx= n_i_ratio_count/2;
+
+    if (n_middle_idx >1)
+        n_ratio_average =( an_ratio[n_middle_idx-1] +an_ratio[n_middle_idx])/2; // use median
+    else
+        n_ratio_average = an_ratio[n_middle_idx ];
+
+    if( n_ratio_average>2 && n_ratio_average <184){
+        n_spo2_calc= uch_spo2_table[n_ratio_average] ;
+        *pn_spo2 = n_spo2_calc ;
+        *pch_spo2_valid  = 1;//  float_SPO2 =  -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ;  // for comparison with table
+    }
+    else{
+        *pn_spo2 =  -999 ; // do not use SPO2 since signal ratio is out of range
+        *pch_spo2_valid  = 0; 
+    }
+}
+
+
+void maxim_find_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num)
+/**
+* \brief        Find peaks
+* \par          Details
+*               Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
+*
+* \retval       None
+*/
+{
+    maxim_peaks_above_min_height( pn_locs, pn_npks, pn_x, n_size, n_min_height );
+    maxim_remove_close_peaks( pn_locs, pn_npks, pn_x, n_min_distance );
+    *pn_npks = min( *pn_npks, n_max_num );
+}
+
+void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *pn_npks, int32_t  *pn_x, int32_t n_size, int32_t n_min_height)
+/**
+* \brief        Find peaks above n_min_height
+* \par          Details
+*               Find all peaks above MIN_HEIGHT
+*
+* \retval       None
+*/
+{
+    int32_t i = 1, n_width;
+    *pn_npks = 0;
+    
+    while (i < n_size-1){
+        if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){            // find left edge of potential peaks
+            n_width = 1;
+            while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width])    // find flat peaks
+                n_width++;
+            if (pn_x[i] > pn_x[i+n_width] && (*pn_npks) < 15 ){                            // find right edge of peaks
+                pn_locs[(*pn_npks)++] = i;        
+                // for flat peaks, peak location is left edge
+                i += n_width+1;
+            }
+            else
+                i += n_width;
+        }
+        else
+            i++;
+    }
+}
+
+
+void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
+/**
+* \brief        Remove peaks
+* \par          Details
+*               Remove peaks separated by less than MIN_DISTANCE
+*
+* \retval       None
+*/
+{
+    
+    int32_t i, j, n_old_npks, n_dist;
+    
+    /* Order peaks from large to small */
+    maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
+
+    for ( i = -1; i < *pn_npks; i++ ){
+        n_old_npks = *pn_npks;
+        *pn_npks = i+1;
+        for ( j = i+1; j < n_old_npks; j++ ){
+            n_dist =  pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
+            if ( n_dist > n_min_distance || n_dist < -n_min_distance )
+                pn_locs[(*pn_npks)++] = pn_locs[j];
+        }
+    }
+
+    // Resort indices longo ascending order
+    maxim_sort_ascend( pn_locs, *pn_npks );
+}
+
+void maxim_sort_ascend(int32_t *pn_x,int32_t n_size) 
+/**
+* \brief        Sort array
+* \par          Details
+*               Sort array in ascending order (insertion sort algorithm)
+*
+* \retval       None
+*/
+{
+    int32_t i, j, n_temp;
+    for (i = 1; i < n_size; i++) {
+        n_temp = pn_x[i];
+        for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
+            pn_x[j] = pn_x[j-1];
+        pn_x[j] = n_temp;
+    }
+}
+
+void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
+/**
+* \brief        Sort indices
+* \par          Details
+*               Sort indices according to descending order (insertion sort algorithm)
+*
+* \retval       None
+*/ 
+{
+    int32_t i, j, n_temp;
+    for (i = 1; i < n_size; i++) {
+        n_temp = pn_indx[i];
+        for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
+            pn_indx[j] = pn_indx[j-1];
+        pn_indx[j] = n_temp;
+    }
+}
+
diff -r 000000000000 -r 82a90765baa2 algorithm/algorithm.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/algorithm/algorithm.h	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,100 @@
+/** \file algorithm.h ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: algorithm.h
+* Description: This module is the heart rate/SpO2 calculation algorithm header file
+*
+* Revision History:
+*\n 1-18-2016 Rev 01.00 SK Initial release.
+*\n
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+*\n char              ch_pmod_value
+*\n char (array)      s_pmod_s_string[16]
+*\n float             f_pmod_value
+*\n int32_t           n_pmod_value
+*\n int32_t (array)   an_pmod_value[16]
+*\n int16_t           w_pmod_value
+*\n int16_t (array)   aw_pmod_value[16]
+*\n uint16_t          uw_pmod_value
+*\n uint16_t (array)  auw_pmod_value[16]
+*\n uint8_t           uch_pmod_value
+*\n uint8_t (array)   auch_pmod_buffer[16]
+*\n uint32_t          un_pmod_value
+*\n int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2015 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.
+*******************************************************************************
+*/
+#ifndef ALGORITHM_H_
+#define ALGORITHM_H_
+
+#include "mbed.h"
+
+#define true 1
+#define false 0
+#define FS 100
+#define BUFFER_SIZE  (FS* 5) 
+#define HR_FIFO_SIZE 7
+#define MA4_SIZE  4 // DO NOT CHANGE
+#define HAMMING_SIZE  5// DO NOT CHANGE
+#define min(x,y) ((x) < (y) ? (x) : (y))
+
+const uint16_t auw_hamm[31]={ 41,    276,    512,    276,     41 }; //Hamm=  long16(512* hamming(5)');
+//uch_spo2_table is computed as  -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
+const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99, 
+                            99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 
+                            100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 
+                            97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91, 
+                            90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 
+                            80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67, 
+                            66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 
+                            49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29, 
+                            28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5, 
+                            3, 2, 1 } ;
+static  int32_t an_dx[ BUFFER_SIZE-MA4_SIZE]; // delta
+static  int32_t an_x[ BUFFER_SIZE]; //ir
+static  int32_t an_y[ BUFFER_SIZE]; //red
+
+
+void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer ,  int32_t n_ir_buffer_length, uint32_t *pun_red_buffer ,   int32_t *pn_spo2, int8_t *pch_spo2_valid ,  int32_t *pn_heart_rate , int8_t  *pch_hr_valid);
+void maxim_find_peaks( int32_t *pn_locs, int32_t *pn_npks,  int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num );
+void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *pn_npks,  int32_t *pn_x, int32_t n_size, int32_t n_min_height );
+void maxim_remove_close_peaks( int32_t *pn_locs, int32_t *pn_npks,   int32_t  *pn_x, int32_t n_min_distance );
+void maxim_sort_ascend( int32_t *pn_x, int32_t n_size );
+void maxim_sort_indices_descend(  int32_t  *pn_x, int32_t *pn_indx, int32_t n_size);
+
+#endif /* ALGORITHM_H_ */
\ No newline at end of file
diff -r 000000000000 -r 82a90765baa2 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,212 @@
+/** \file main.cpp ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: main.cpp
+* Description: This module contains the Main application for the MAXREFDES117 example program.
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 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.
+*******************************************************************************
+*/
+/*!\mainpage Main Page
+*
+* \section intro_sec Introduction
+*
+* This is the code documentation for the MAXREFDES117# subsystem reference design.
+* 
+*  The Files page contains the File List page and the Globals page.
+* 
+*  The Globals page contains the Functions, Variables, and Macros sub-pages.
+*
+* \image html MAXREFDES117_Block_Diagram.png "MAXREFDES117# System Block Diagram"
+* 
+* \image html MAXREFDES117_firmware_Flowchart.png "MAXREFDES117# Firmware Flowchart"
+*
+*/
+#include "mbed.h"
+#include "algorithm.h"
+#include "MAX30102.h"
+
+#define MAX_BRIGHTNESS 255
+#define false 0
+#define true 1
+
+uint32_t aun_ir_buffer[500]; //IR LED sensor data
+int32_t n_ir_buffer_length;    //data length
+uint32_t aun_red_buffer[500];    //Red LED sensor data
+int32_t n_sp02; //SPO2 value
+int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
+int32_t n_heart_rate;   //heart rate value
+int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid
+uint8_t uch_dummy;
+
+Serial pc(USBTX, USBRX);    //initializes the serial port
+DigitalIn INT(p8);  //pin P8 connects to the interrupt output pin of the MAX30102
+PwmOut led(LED1);  //initializes the pwm output that connects to the on board LED
+
+// the setup routine runs once when you press reset:
+int main() { 
+    uint32_t un_min, un_max, un_prev_data;  //variables to calculate the on-board LED brightness that reflects the heartbeats
+    int i;
+    int32_t n_brightness;
+    float f_temp;
+    
+    maxim_max30102_reset(); //resets the MAX30102
+    // initialize serial communication at 115200 bits per second:
+    pc.baud(115200);
+    pc.format(8,SerialBase::None,1);
+    wait(1);
+    
+    //read and clear status register
+    maxim_max30102_read_reg(0,&uch_dummy);
+    
+    //wait until the user presses a key
+    while(pc.readable()==0)
+    {
+        pc.printf("\x1B[2J");  //clear terminal program screen
+        pc.printf("Press any key to start conversion\n\r");
+        wait(1);
+    }
+    uch_dummy=getchar();
+    
+    maxim_max30102_init();  //initializes the MAX30102
+        
+        
+    n_brightness=0;
+    un_min=0x3FFFF;
+    un_max=0;
+  
+    n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
+    
+    //read the first 500 samples, and determine the signal range
+    for(i=0;i<n_ir_buffer_length;i++)
+    {
+        while(INT.read()==1);   //wait until the interrupt pin asserts
+        
+        maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
+            
+        if(un_min>aun_red_buffer[i])
+            un_min=aun_red_buffer[i];    //update signal min
+        if(un_max<aun_red_buffer[i])
+            un_max=aun_red_buffer[i];    //update signal max
+        pc.printf("red=");
+        pc.printf("%i", aun_red_buffer[i]);
+        pc.printf(", ir=");
+        pc.printf("%i\n\r", aun_ir_buffer[i]);
+    }
+    un_prev_data=aun_red_buffer[i];
+    
+    
+    //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
+    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
+    
+    //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
+    while(1)
+    {
+        i=0;
+        un_min=0x3FFFF;//???
+        un_max=0;
+        
+        //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
+        for(i=100;i<500;i++)
+        {
+            aun_red_buffer[i-100]=aun_red_buffer[i];
+            aun_ir_buffer[i-100]=aun_ir_buffer[i];
+            
+            //update the signal min and max
+            if(un_min>aun_red_buffer[i])
+            un_min=aun_red_buffer[i];
+            if(un_max<aun_red_buffer[i])
+            un_max=aun_red_buffer[i];
+        }
+        
+        //take 100 sets of samples before calculating the heart rate.
+        for(i=400;i<500;i++)
+        {
+            un_prev_data=aun_red_buffer[i-1];
+            while(INT.read()==1);
+            maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
+        
+            if(aun_red_buffer[i]>un_prev_data)//just to determine the brightness of LED according to the deviation of adjacent two AD data
+            {
+                f_temp=aun_red_buffer[i]-un_prev_data;
+                f_temp/=(un_max-un_min);
+                f_temp*=MAX_BRIGHTNESS;
+                n_brightness-=(int)f_temp;
+                if(n_brightness<0)
+                    n_brightness=0;
+            }
+            else
+            {
+                f_temp=un_prev_data-aun_red_buffer[i];
+                f_temp/=(un_max-un_min);
+                f_temp*=MAX_BRIGHTNESS;
+                n_brightness+=(int)f_temp;
+                if(n_brightness>MAX_BRIGHTNESS)
+                    n_brightness=MAX_BRIGHTNESS;
+            }
+
+            led.write(1-(float)n_brightness/256);//pwm control led brightness
+
+            //send samples and calculation result to terminal program through UART
+            pc.printf("red=");
+            pc.printf("%i", aun_red_buffer[i]);
+            pc.printf(", ir=");
+            pc.printf("%i", aun_ir_buffer[i]);
+            pc.printf(", HR=%i, ", n_heart_rate); 
+            pc.printf("HRvalid=%i, ", ch_hr_valid);
+            pc.printf("SpO2=%i, ", n_sp02);
+            pc.printf("SPO2Valid=%i\n\r", ch_spo2_valid);
+        }
+        maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
+    }
+}
+ 
\ No newline at end of file
diff -r 000000000000 -r 82a90765baa2 mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Oct 26 10:30:22 2018 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/e95d10626187
\ No newline at end of file