Pulse Oximeter and Heart-Rate Sensor IC for Wearable Health
MAX30100.cpp
- Committer:
- mcm
- Date:
- 2018-07-13
- Revision:
- 4:b914185995a1
- Parent:
- 3:318e993aaf54
File content as of revision 4:b914185995a1:
/** * @brief MAX30100.c * @details Pulse Oximeter and Heart-Rate Sensor IC for Wearable Health. * Functions file. * * * @return N/A * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A * @pre This code belongs to AqueronteBlog ( http://unbarquero.blogspot.com ). */ #include "MAX30100.h" MAX30100::MAX30100 ( PinName sda, PinName scl, uint32_t addr, uint32_t freq ) : _i2c ( sda, scl ) , _MAX30100_Addr ( addr ) { _i2c.frequency( freq ); } MAX30100::~MAX30100() { } /** * @brief MAX30100_ReadInterruptStatus ( MAX30100_vector_data_t* ) * * @details It gets the interrupt status value. * * @param[in] N/A * * @param[out] myInterruptStatus: Interrupt status register. * * * @return Status of MAX30100_ReadInterruptStatus. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_ReadInterruptStatus ( MAX30100_vector_data_t* myInterruptStatus ) { char cmd = 0; uint32_t aux; /* Read INTERRUPT STATUS register */ cmd = MAX30100_INTERRUPT_STATUS; aux = _i2c.write ( _MAX30100_Addr, &cmd, 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd, 1 ); /* Parse data */ myInterruptStatus->InterruptStatus = cmd; if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_InterrupEnable ( uint8_t ) * * @details It sets which interrupt is enabled/disabled. * * @param[in] myInterruptEnable Interrupts to be enabled. * * @param[out] N/A. * * * @return Status of MAX30100_InterrupEnable. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_InterrupEnable ( uint8_t myInterruptEnable ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read INTERRUPT ENABLE register */ cmd[0] = MAX30100_INTERRUPT_ENABLE; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~( INTERRUPT_ENABLE_ENB_A_FULL_MASK | INTERRUPT_ENABLE_ENB_TEP_RDY_MASK | INTERRUPT_ENABLE_ENB_HR_RDY_MASK | INTERRUPT_ENABLE_ENB_SO2_RDY_MASK ); cmd[1] |= myInterruptEnable; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_ShutdownControl ( MAX30100_mode_configuration_shdn_t ) * * @details It sets the power mode. * * @param[in] myPowerMode power mode. * * @param[out] N/A. * * * @return Status of MAX30100_ShutdownControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_ShutdownControl ( MAX30100_mode_configuration_shdn_t myPowerMode ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd[0] = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~MODE_CONFIGURATION_SHDN_MASK; cmd[1] |= myPowerMode; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_SoftwareReset ( void ) * * @details It performs a software reset. * * @param[in] N/A. * * @param[out] N/A. * * * @return Status of MAX30100_SoftwareReset. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre The RESET bit is cleared automatically back to zero after the reset sequence is completed. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_SoftwareReset ( void ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd[0] = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~MODE_CONFIGURATION_RESET_MASK; cmd[1] |= MODE_CONFIGURATION_RESET_ENABLE; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_TriggerTemperature ( void ) * * @details It initiates a single temperature reading from the temperature sensor. * * @param[in] N/A. * * @param[out] N/A. * * * @return Status of MAX30100_TriggerTemperature. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre This bit is cleared automatically back to zero at the conclusion of the temperature * reading when the bit is set to one in heart rate or SpO2 mode. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_TriggerTemperature ( void ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd[0] = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~MODE_CONFIGURATION_TEMP_EN_MASK; cmd[1] |= MODE_CONFIGURATION_TEMP_EN_ENABLE; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_ModeControl ( MAX30100_mode_configuration_mode_t ) * * @details It sets the operating state of the MAX30100. * * @param[in] myModeControl: Mode Control. * * @param[out] N/A. * * * @return Status of MAX30100_ModeControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre Changing modes does not change any other setting, nor does it erase any previously stored * data inside the data registers. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_ModeControl ( MAX30100_mode_configuration_mode_t myModeControl ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd[0] = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~MODE_CONFIGURATION_MODE_MASK; cmd[1] |= myModeControl; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_SpO2_HighResolution ( MAX30100_spo2_configuration_spo2_hi_res_en_t ) * * @details It sets the SpO2 ADC resolution is 16-bit with 1.6ms LED pulse width. * * @param[in] myRes: SpO2 High Resolution Enable. * * @param[out] N/A. * * * @return Status of MAX30100_SpO2_HighResolution. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_SpO2_HighResolution ( MAX30100_spo2_configuration_spo2_hi_res_en_t myRes ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read SPO2 CONFIGURATION register */ cmd[0] = MAX30100_SPO2_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~SPO2_CONFIGURATION_SPO2_HI_RES_EN_MASK; cmd[1] |= myRes; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_SpO2_SampleRateControl ( MAX30100_spo2_configuration_spo2_hi_res_en_t ) * * @details It defines the effective sampling rate. * * @param[in] mySampleRate: Effective sampling rate. * * @param[out] N/A. * * * @return Status of MAX30100_SpO2_SampleRateControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning The allowed sample rates for both SpO2 and HR mode are summarized in Table 8 and Table 9 in the Data-sheet. * The user MUST take care of this. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_SpO2_SampleRateControl ( MAX30100_spo2_configuration_spo2_sr_t mySampleRate ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read SPO2 CONFIGURATION register */ cmd[0] = MAX30100_SPO2_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~SPO2_CONFIGURATION_SPO2_SR_MASK; cmd[1] |= mySampleRate; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_LED_PulseWidthControl ( MAX30100_vector_data_t ) * * @details It sets the LED pulse width. * * @param[in] myLEDWidth: Pulse width/Resolution. * * @param[out] N/A. * * * @return Status of MAX30100_LED_PulseWidthControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning The allowed sample rates for both SpO2 and HR mode are summarized in Table 8 and Table 9 in the Data-sheet. * The user MUST take care of this. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_LED_PulseWidthControl ( MAX30100_vector_data_t myLEDWidth ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read SPO2 CONFIGURATION register */ cmd[0] = MAX30100_SPO2_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~SPO2_CONFIGURATION_LED_PW_MASK; cmd[1] |= myLEDWidth.Resolution; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_Get_LED_PulseWidthControl ( MAX30100_vector_data_t* ) * * @details It gets the LED pulse width. * * @param[in] N/A. * * @param[out] myLEDWidth: Pulse width/Resolution. * * * @return Status of MAX30100_Get_LED_PulseWidthControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_Get_LED_PulseWidthControl ( MAX30100_vector_data_t* myLEDWidth ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read SPO2 CONFIGURATION register */ cmd[0] = MAX30100_SPO2_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ myLEDWidth->Resolution = (MAX30100::MAX30100_spo2_configuration_led_pw_t)( cmd[1] & SPO2_CONFIGURATION_LED_PW_MASK ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_SetRed_LED_CurrentControl ( MAX30100_led_configuration_red_pa_t ) * * @details It sets the current level of the Red LED. * * @param[in] myRedLED: Current level of the Red LED. * * @param[out] N/A. * * * @return Status of MAX30100_SetRed_LED_CurrentControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_SetRed_LED_CurrentControl ( MAX30100_led_configuration_red_pa_t myRedLED ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read LED CONFIGURATION register */ cmd[0] = MAX30100_LED_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~LED_CONFIGURATION_RED_PA_MASK; cmd[1] |= myRedLED; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_SetIR_LED_CurrentControl ( MAX30100_led_configuration_red_pa_t ) * * @details It sets the current level of the IR LED. * * @param[in] myRedLED: Current level of the IR LED. * * @param[out] N/A. * * * @return Status of MAX30100_SetIR_LED_CurrentControl. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_SetIR_LED_CurrentControl ( MAX30100_led_configuration_ir_pa_t myIRLED ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read LED CONFIGURATION register */ cmd[0] = MAX30100_LED_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[1], 1 ); /* Parse data */ cmd[1] &= ~LED_CONFIGURATION_IR_PA_MASK; cmd[1] |= myIRLED; /* Update the register */ aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_GetRawTemperature ( MAX30100_vector_data_t* ) * * @details It gets the raw temperature data ( temperature integer and temperature fraction ). * * @param[in] N/A. * * @param[out] myRawTemperature: Temperature integer and temperature fraction * * * @return Status of MAX30100_GetRawTemperature. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_GetRawTemperature ( MAX30100_vector_data_t* myRawTemperature ) { char cmd[] = { 0, 0 }; uint32_t aux; /* Read TEMP_INTEGER and TEMP_FRACTION register ( auto-increment ) */ cmd[0] = MAX30100_TEMP_INTEGER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) ); /* Parse data */ myRawTemperature->Temp_Integer = cmd[0]; myRawTemperature->Temp_Fraction = cmd[1]; if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_GetTemperature ( MAX30100_vector_data_t* ) * * @details It gets the temperature value. * * @param[in] N/A. * * @param[out] myTemperature: Temperature * * * @return Status of MAX30100_GetTemperature. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre This function updates both Temp_Integer and Temp_Fraction values. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_GetTemperature ( MAX30100_vector_data_t* myTemperature ) { MAX30100::MAX30100_status_t myMAX30100Status; /* Get the raw temperature */ myMAX30100Status = MAX30100_GetRawTemperature ( myTemperature ); /* Process the temperature */ if ( ( myTemperature->Temp_Integer & 0x80 ) == 0x80 ) { /* Negative temperature */ myTemperature->Temp_Integer = ~myTemperature->Temp_Integer; myTemperature->Temp_Integer += 1; myTemperature->Temperature = ( -1.0 ) * ( (float)( myTemperature->Temp_Integer + (float)( myTemperature->Temp_Fraction * 0.0625 ) ) ); } else { /* Positive temperature */ myTemperature->Temperature = (float)( myTemperature->Temp_Integer + (float)( myTemperature->Temp_Fraction * 0.0625 ) ); } if ( myMAX30100Status == MAX30100_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_GetRevisionID ( MAX30100_vector_data_t* ) * * @details It gets the revision ID. * * @param[in] N/A. * * @param[out] myRevisionID: Revision ID * * * @return Status of MAX30100_GetRevisionID. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_GetRevisionID ( MAX30100_vector_data_t* myRevisionID ) { char cmd = 0; uint32_t aux; /* Read REVISION ID register */ cmd = MAX30100_REVISION_ID; aux = _i2c.write ( _MAX30100_Addr, &cmd, 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd, 1 ); /* Parse data */ myRevisionID->RevisionID = cmd; if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_GetPartID ( MAX30100_vector_data_t* ) * * @details It gets the part ID. * * @param[in] N/A. * * @param[out] myPartID: Part ID * * * @return Status of MAX30100_GetPartID. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_GetPartID ( MAX30100_vector_data_t* myPartID ) { char cmd = 0; uint32_t aux; /* Read PART ID register */ cmd = MAX30100_PART_ID; aux = _i2c.write ( _MAX30100_Addr, &cmd, 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd, 1 ); /* Parse data */ myPartID->PartID = cmd; if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_PollingTemperatureConversion ( MAX30100_vector_data_t* ) * * @details It gets the TEMP_EN value in Mode Configuration register. * * @param[in] N/A. * * @param[out] myTempFlag: Temperature flag * * * @return Status of MAX30100_PollingTemperatureConversion. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_PollingTemperatureConversion ( MAX30100_vector_data_t* myTempFlag ) { char cmd = 0; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd, 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd, 1 ); /* Parse data */ myTempFlag->TemperatureFlag = (MAX30100::MAX30100_mode_configuration_temp_en_t)( cmd & MODE_CONFIGURATION_TEMP_EN_MASK ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_PollingSoftwareReset ( MAX30100_vector_data_t* ) * * @details It checks if the software reset was completed by polling mode. * * @param[in] N/A. * * @param[out] myResetFlag: Reset flag * * * @return Status of MAX30100_PollingSoftwareReset. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_PollingSoftwareReset ( MAX30100_vector_data_t* myResetFlag ) { char cmd = 0; uint32_t aux; /* Read MODE CONFIGURATION register */ cmd = MAX30100_MODE_CONFIGURATION; aux = _i2c.write ( _MAX30100_Addr, &cmd, 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd, 1 ); /* Parse data */ myResetFlag->ResetFlag = (MAX30100_mode_configuration_reset_t)( cmd & MODE_CONFIGURATION_RESET_MASK ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; } /** * @brief MAX30100_ReadFIFO ( MAX30100_vector_data_t* , uint32_t ) * * @details It checks if the software reset was completed by polling mode. * * @param[in] myNumSamplesToRead: Number of samples to read. * * @param[out] myDATA: Data from the FIFO * * * @return Status of MAX30100_ReadFIFO. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_ReadFIFO ( MAX30100_vector_data_t* myDATA, uint32_t myNumSamplesToRead ) { char cmd[3] = { 0 } ; uint32_t num_available_samples = 0; uint32_t i = 0; uint32_t myShift = 0; uint32_t aux; MAX30100::MAX30100_status_t myMAX30100Status; /* Check the sample size */ if ( ( myNumSamplesToRead > 0 ) && ( myNumSamplesToRead < 17 ) ) { /* FIRST TRANSACTION: Get the FIFO_WR_PTR, OVF_COUNTER and FIFO_RD_PTR */ cmd[0] = MAX30100_FIFO_WRITE_POINTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) ); /* Parse data */ myDATA->FIFO_wr_ptr = ( cmd[0] & FIFO_FIFO_WRITE_POINTER_MASK ); myDATA->OVF_counter = ( cmd[1] & FIFO_OVER_FLOW_COUNTER_MASK ); myDATA->FIFO_rd_ptr = ( cmd[2] & FIFO_FIFO_READ_POINTER_MASK ); /* If the FIFO is full, just start reading data */ if ( myDATA->OVF_counter == 0xF ) { /* Get data from FIFO */ cmd[0] = MAX30100_FIFO_DATA_REGISTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, (char*)&myDATA->FIFO_buff[0], ( myNumSamplesToRead << 2U ) ); /* Parse the data */ for ( i = 0; i < myNumSamplesToRead; i++ ) { myDATA->FIFO_IR_samples[i] = myDATA->FIFO_buff[ ( i << 2 ) ]; myDATA->FIFO_IR_samples[i] <<= 8U; myDATA->FIFO_IR_samples[i] |= myDATA->FIFO_buff[ ( i << 2 ) + 1 ]; myDATA->FIFO_RED_samples[i] = myDATA->FIFO_buff[ ( i << 2 ) + 2 ]; myDATA->FIFO_RED_samples[i] <<= 8U; myDATA->FIFO_RED_samples[i] |= myDATA->FIFO_buff[ ( ( i << 2 ) + 2 ) + 1 ]; } } else { /* Evaluate the number of samples to be read from the FIFO */ num_available_samples = (uint32_t)( myDATA->FIFO_wr_ptr - myDATA->FIFO_rd_ptr ); /* SECOND TRANSACTION: Read 'myNumSamplesToRead' samples from the FIFO */ if ( myNumSamplesToRead <= num_available_samples ) { /* Get data from FIFO */ cmd[0] = MAX30100_FIFO_DATA_REGISTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, (char*)&myDATA->FIFO_buff[0], ( myNumSamplesToRead << 2U ) ); /* Parse the data */ for ( i = 0; i < myNumSamplesToRead; i++ ) { myDATA->FIFO_IR_samples[i] = myDATA->FIFO_buff[ ( i << 2 ) ]; myDATA->FIFO_IR_samples[i] <<= 8U; myDATA->FIFO_IR_samples[i] |= myDATA->FIFO_buff[ ( i << 2 ) + 1 ]; myDATA->FIFO_RED_samples[i] = myDATA->FIFO_buff[ ( i << 2 ) + 2 ]; myDATA->FIFO_RED_samples[i] <<= 8U; myDATA->FIFO_RED_samples[i] |= myDATA->FIFO_buff[ ( ( i << 2 ) + 2 ) + 1 ]; } } else { /* THIRD TRANSACTION: Write to FIFO_RD_PTR register. If the second transaction was successful, FIFO_RD_PTR points to the next sample in the FIFO, and this third transaction is not necessary. Otherwise, the processor updates the FIFO_RD_PTR appropriately, so that the samples are reread */ // [TODO] NOT IMPLEMENTED return MAX30100_FAILURE; } } } else { return MAX30100_FAILURE; } /* Parse the data according to the ADC resolution */ /* Get the pulse width/resolution */ myMAX30100Status = MAX30100_Get_LED_PulseWidthControl ( myDATA ); switch ( myDATA->Resolution ) { default: case SPO2_CONFIGURATION_LED_PW_200US_13BITS: myShift = 3U; break; case SPO2_CONFIGURATION_LED_PW_400US_14BITS: myShift = 2U; break; case SPO2_CONFIGURATION_LED_PW_800US_15BITS: myShift = 1U; break; case SPO2_CONFIGURATION_LED_PW_1600US_16BITS: myShift = 0U; break; } /* Update the data */ for ( i = 0; i < myNumSamplesToRead; i++ ) { myDATA->FIFO_IR_samples[i] >>= myShift; myDATA->FIFO_RED_samples[i] >>= myShift; } /* Update FIFO pointers: Get the FIFO_WR_PTR, OVF_COUNTER and FIFO_RD_PTR */ cmd[0] = MAX30100_FIFO_WRITE_POINTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) ); /* Parse data */ myDATA->FIFO_wr_ptr = ( cmd[0] & FIFO_FIFO_WRITE_POINTER_MASK ); myDATA->OVF_counter = ( cmd[1] & FIFO_OVER_FLOW_COUNTER_MASK ); myDATA->FIFO_rd_ptr = ( cmd[2] & FIFO_FIFO_READ_POINTER_MASK ); if ( ( aux == I2C_SUCCESS ) && ( myMAX30100Status == MAX30100_SUCCESS ) ) { return MAX30100_SUCCESS; } else { return MAX30100_FAILURE; } } /** * @brief MAX30100_ClearFIFO ( MAX30100_vector_data_t* ) * * @details It clears the FIFO_WR_PTR, OVF_COUNTER, and FIFO_RD_PTR * registers to all zeros (0x00) to ensure the FIFO is empty and in a known state. * * @param[in] N/A. * * @param[out] myDATA: Data from the FIFO * * * @return Status of MAX30100_ClearFIFO. * * * @author Manuel Caballero * @date 13/July/2018 * @version 13/July/2018 The ORIGIN * @pre N/A. * @warning N/A. */ MAX30100::MAX30100_status_t MAX30100::MAX30100_ClearFIFO ( MAX30100_vector_data_t* myDATA ) { char cmd[] = { 0, 0, 0, 0 } ; uint32_t aux; /* Update the FIFO_WR_PTR, FIFO_WR_PTR and OVF_COUNTER registers */ cmd[0] = MAX30100_FIFO_WRITE_POINTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); /* Get the FIFO_WR_PTR, OVF_COUNTER and FIFO_RD_PTR */ cmd[0] = MAX30100_FIFO_WRITE_POINTER; aux = _i2c.write ( _MAX30100_Addr, &cmd[0], 1, true ); aux = _i2c.read ( _MAX30100_Addr, &cmd[0], 3 ); /* Update data */ myDATA->FIFO_wr_ptr = ( cmd[0] & FIFO_FIFO_WRITE_POINTER_MASK ); myDATA->OVF_counter = ( cmd[1] & FIFO_OVER_FLOW_COUNTER_MASK ); myDATA->FIFO_rd_ptr = ( cmd[2] & FIFO_FIFO_READ_POINTER_MASK ); if ( aux == I2C_SUCCESS ) return MAX30100_SUCCESS; else return MAX30100_FAILURE; }