
maxrefdes117
MAX30105/MAX30105.cpp
- Committer:
- zinnetyazicii53
- Date:
- 2019-08-06
- Revision:
- 0:78a2573ad768
File content as of revision 0:78a2573ad768:
#include "mbed.h" #include "MAX30105.h" #include "millis.h" //****************************************************************************** MAX30105::MAX30105(I2C &i2c): _i2c(i2c) { } //****************************************************************************** int MAX30105::writeRegValue(uint8_t reg, char value) { char cmdData[2] = { (char)reg, value }; if (_i2c.write(MAX30105_ADDRESS, cmdData, sizeof(cmdData)) != 0) { return MAX30105_ERROR; } return MAX30105_NO_ERROR; } //****************************************************************************** int MAX30105::writeReg(uint8_t reg ) { char cmdData[1] = { (char)reg }; if (_i2c.write(MAX30105_ADDRESS, cmdData, sizeof(cmdData)) != 0) { return MAX30105_ERROR; } return MAX30105_NO_ERROR; } //****************************************************************************** int MAX30105::readReg(uint8_t reg, char *value) { char cmdData[1] = { (char)reg }; if (_i2c.write(MAX30105_ADDRESS, cmdData, sizeof(cmdData)) != 0) { return MAX30105_ERROR; } if (_i2c.read(MAX30105_ADDRESS, value, 1) != 0) { return MAX30105_ERROR; } return MAX30105_NO_ERROR; } //****************************************************************************** uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg){ _i2c.write(reg); char *data = new char[5]; _i2c.read(address,data,5); return (uint8_t)data; } //****************************************************************************** void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value){ writeRegValue(reg, value); } //****************************************************************************** bool MAX30105::safeCheck(uint8_t maxTimeToCheck) { uint32_t markTime = millis(); while(1) { if(millis() - markTime > maxTimeToCheck) return(false); if(check() == true) //We found new data! return(true); wait(1); } } //****************************************************************************** // NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical) // See datasheet, page 21 void MAX30105::setPulseAmplitudeRed(uint8_t amplitude) { writeRegister8(_i2caddr, MAX30105_LED1_PULSEAMP, amplitude); } //****************************************************************************** void MAX30105::setPulseAmplitudeIR(uint8_t amplitude) { writeRegister8(_i2caddr, MAX30105_LED2_PULSEAMP, amplitude); } //****************************************************************************** void MAX30105::setPulseAmplitudeGreen(uint8_t amplitude) { writeRegister8(_i2caddr, MAX30105_LED3_PULSEAMP, amplitude); } //****************************************************************************** void MAX30105::setPulseAmplitudeProximity(uint8_t amplitude) { writeRegister8(_i2caddr, MAX30105_LED_PROX_AMP, amplitude); } //Begin Interrupt configuration //****************************************************************************** uint8_t MAX30105::getINT1(void) { return (readRegister8(_i2caddr, MAX30105_INTSTAT1)); } //****************************************************************************** uint8_t MAX30105::getINT2(void) { return (readRegister8(_i2caddr, MAX30105_INTSTAT2)); } //****************************************************************************** void MAX30105::enableAFULL(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_ENABLE); } //****************************************************************************** void MAX30105::disableAFULL(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_DISABLE); } //****************************************************************************** void MAX30105::enableDATARDY(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_ENABLE); } //****************************************************************************** void MAX30105::disableDATARDY(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_DISABLE); } //****************************************************************************** void MAX30105::enableALCOVF(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_ENABLE); } //****************************************************************************** void MAX30105::disableALCOVF(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_DISABLE); } //****************************************************************************** void MAX30105::enablePROXINT(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_ENABLE); } //****************************************************************************** void MAX30105::disablePROXINT(void) { bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_DISABLE); } //****************************************************************************** void MAX30105::enableDIETEMPRDY(void) { bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_ENABLE); } //****************************************************************************** void MAX30105::disableDIETEMPRDY(void) { bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_DISABLE); } //End Interrupt configuration //****************************************************************************** void MAX30105::softReset(void) { bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET); // Poll for bit to clear, reset is then complete // Timeout after 100ms unsigned long startTime = millis(); while (millis() - startTime < 100) { uint8_t response = readRegister8(_i2caddr, MAX30105_MODECONFIG); if ((response & MAX30105_RESET) == 0) break; //We're done! wait(1); //Let's not over burden the I2C bus } } //****************************************************************************** void MAX30105::shutDown(void) { // Put IC into low power mode (datasheet pg. 19) // During shutdown the IC will continue to respond to I2C commands but will // not update with or take new readings (such as temperature) bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_SHUTDOWN); } //****************************************************************************** void MAX30105::wakeUp(void) { // Pull IC out of low power mode (datasheet pg. 19) bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_WAKEUP); } //****************************************************************************** void MAX30105::setLEDMode(uint8_t mode) { // Set which LEDs are used for sampling -- Red only, RED+IR only, or custom. // See datasheet, page 19 bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode); } //****************************************************************************** void MAX30105::setADCRange(uint8_t adcRange) { // adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384 bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange); } //****************************************************************************** void MAX30105::setSampleRate(uint8_t sampleRate) { // sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200 bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate); } //****************************************************************************** void MAX30105::setPulseWidth(uint8_t pulseWidth) { // pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411 bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth); } //****************************************************************************** void MAX30105::enableSlot(uint8_t slotNumber, uint8_t device) { uint8_t originalContents; switch (slotNumber) { case (1): bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device); break; case (2): bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4); break; case (3): bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device); break; case (4): bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4); break; default: //Shouldn't be here! break; } } //****************************************************************************** void MAX30105::disableSlots(void) { writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG1, 0); writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG2, 0); } // // FIFO Configuration // //****************************************************************************** //Set sample average (Table 3, Page 18) void MAX30105::setFIFOAverage(uint8_t numberOfSamples) { bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples); } //Resets all points to start in a known state //Page 15 recommends clearing FIFO before beginning a read void MAX30105::clearFIFO(void) { writeRegister8(_i2caddr, MAX30105_FIFOWRITEPTR, 0); writeRegister8(_i2caddr, MAX30105_FIFOOVERFLOW, 0); writeRegister8(_i2caddr, MAX30105_FIFOREADPTR, 0); } //****************************************************************************** //Enable roll over if FIFO over flows void MAX30105::enableFIFORollover(void) { bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE); } //****************************************************************************** //Disable roll over if FIFO over flows void MAX30105::disableFIFORollover(void) { bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_DISABLE); } //****************************************************************************** //Set number of samples to trigger the almost full interrupt (Page 18) //Power on default is 32 samples //Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples void MAX30105::setFIFOAlmostFull(uint8_t numberOfSamples) { bitMask(MAX30105_FIFOCONFIG, MAX30105_A_FULL_MASK, numberOfSamples); } //****************************************************************************** //Read the FIFO Write Pointer uint8_t MAX30105::getWritePointer(void) { return (readRegister8(_i2caddr, MAX30105_FIFOWRITEPTR)); } //****************************************************************************** //Read the FIFO Read Pointer uint8_t MAX30105::getReadPointer(void) { return (readRegister8(_i2caddr, MAX30105_FIFOREADPTR)); } //****************************************************************************** float MAX30105::readTemperature() { //DIE_TEMP_RDY interrupt must be enabled //See issue 19: https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/issues/19 // Step 1: Config die temperature register to take 1 temperature sample writeRegister8(_i2caddr, MAX30105_DIETEMPCONFIG, 0x01); // Poll for bit to clear, reading is then complete // Timeout after 100ms unsigned long startTime = millis(); while (millis() - startTime < 100) { //uint8_t response = readRegister8(_i2caddr, MAX30105_DIETEMPCONFIG); //Original way //if ((response & 0x01) == 0) break; //We're done! //Check to see if DIE_TEMP_RDY interrupt is set uint8_t response = readRegister8(_i2caddr, MAX30105_INTSTAT2); if ((response & MAX30105_INT_DIE_TEMP_RDY_ENABLE) > 0) break; //We're done! wait(1); //Let's not over burden the I2C bus } //TODO How do we want to fail? With what type of error? //? if(millis() - startTime >= 100) return(-999.0); // Step 2: Read die temperature register (integer) int8_t tempInt = readRegister8(_i2caddr, MAX30105_DIETEMPINT); uint8_t tempFrac = readRegister8(_i2caddr, MAX30105_DIETEMPFRAC); //Causes the clearing of the DIE_TEMP_RDY interrupt // Step 3: Calculate temperature (datasheet pg. 23) return (float)tempInt + ((float)tempFrac * 0.0625); } //****************************************************************************** // Returns die temp in F float MAX30105::readTemperatureF() { float temp = readTemperature(); if (temp != -999.0) temp = temp * 1.8 + 32.0; return (temp); } //****************************************************************************** // Set the PROX_INT_THRESHold void MAX30105::setPROXINTTHRESH(uint8_t val) { writeRegister8(_i2caddr, MAX30105_PROXINTTHRESH, val); } //****************************************************************************** // // Device ID and Revision // uint8_t MAX30105::readPartID() { return readRegister8(_i2caddr, MAX30105_PARTID); } //****************************************************************************** void MAX30105::readRevisionID() { revisionID = readRegister8(_i2caddr, MAX30105_REVISIONID); } //****************************************************************************** uint8_t MAX30105::getRevisionID() { return revisionID; } //****************************************************************************** //Setup the sensor //The MAX30105 has many settings. By default we select: // Sample Average = 4 // Mode = MultiLED // ADC Range = 16384 (62.5pA per LSB) // Sample rate = 50 //Use the default setup if you are just getting started with the MAX30105 sensor void MAX30105::setup(uint8_t powerLevel, uint8_t sampleAverage, uint8_t ledMode, int sampleRate, int pulseWidth, int adcRange) { softReset(); //Reset all configuration, threshold, and data registers to POR values //FIFO Configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //The chip will average multiple samples of same type together if you wish if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2); else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4); else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8); else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16); else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32); else setFIFOAverage(MAX30105_SAMPLEAVG_4); //setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt enableFIFORollover(); //Allow FIFO to wrap/roll over //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Mode Configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR else setLEDMode(MAX30105_MODE_REDONLY); //Red only activeLEDs = ledMode; //Used to control how many uint8_ts to read from FIFO buffer //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Particle Sensing Configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB else setADCRange(MAX30105_ADCRANGE_2048); if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100); else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200); else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400); else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800); else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000); else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600); else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200); else setSampleRate(MAX30105_SAMPLERATE_50); //The longer the pulse width the longer range of detection you'll have //At 69us and 0.4mA it's about 2 inches //At 411us and 0.4mA it's about 6 inches if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution else setPulseWidth(MAX30105_PULSEWIDTH_69); //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //LED Pulse Amplitude Configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Default is 0x1F which gets us 6.4mA //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch setPulseAmplitudeRed(powerLevel); setPulseAmplitudeIR(powerLevel); setPulseAmplitudeGreen(powerLevel); setPulseAmplitudeProximity(powerLevel); //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Multi-LED Mode Configuration, Enable the reading of the three LEDs //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- enableSlot(1, SLOT_RED_LED); if (ledMode > 1) enableSlot(2, SLOT_IR_LED); if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED); //enableSlot(1, SLOT_RED_PILOT); //enableSlot(2, SLOT_IR_PILOT); //enableSlot(3, SLOT_GREEN_PILOT); //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- clearFIFO(); //Reset the FIFO before we begin checking the sensor } // // Data Collection // //Tell caller how many samples are available uint8_t MAX30105::available(void) { int8_t numberOfSamples = sense.head - sense.tail; if (numberOfSamples < 0) numberOfSamples += STORAGE_SIZE; return (numberOfSamples); } //****************************************************************************** //Report the most recent red value uint32_t MAX30105::getRed(void) { //Check the sensor for new data for 250ms if(safeCheck(250)) return (sense.red[sense.head]); else return(0); //Sensor failed to find new data } //****************************************************************************** //Report the most recent IR value uint32_t MAX30105::getIR(void) { //Check the sensor for new data for 250ms if(safeCheck(250)) return (sense.IR[sense.head]); else return(0); //Sensor failed to find new data } //****************************************************************************** //Report the most recent Green value uint32_t MAX30105::getGreen(void) { //Check the sensor for new data for 250ms if(safeCheck(250)) return (sense.green[sense.head]); else return(0); //Sensor failed to find new data } //****************************************************************************** //Report the next Red value in the FIFO uint32_t MAX30105::getFIFORed(void) { return (sense.red[sense.tail]); } //****************************************************************************** //Report the next IR value in the FIFO uint32_t MAX30105::getFIFOIR(void) { return (sense.IR[sense.tail]); } //****************************************************************************** //Report the next Green value in the FIFO uint32_t MAX30105::getFIFOGreen(void) { return (sense.green[sense.tail]); } //****************************************************************************** //Advance the tail void MAX30105::nextSample(void) { if(available()) //Only advance the tail if new data is available { sense.tail++; sense.tail %= STORAGE_SIZE; //Wrap condition } } //****************************************************************************** //Polls the sensor for new data //Call regularly //If new data is available, it updates the head and tail in the main struct //Returns number of new samples obtained uint16_t MAX30105::check(void) { /* //Read register FIDO_DATA in (3-uint8_t * number of active LED) chunks //Until FIFO_RD_PTR = FIFO_WR_PTR uint8_t readPointer = getReadPointer(); uint8_t writePointer = getWritePointer(); int numberOfSamples = 0; //Do we have new data? if (readPointer != writePointer) { //Calculate the number of readings we need to get from sensor numberOfSamples = writePointer - readPointer; if (numberOfSamples < 0) numberOfSamples += 32; //Wrap condition //We now have the number of readings, now calc uint8_ts to read //For this example we are just doing Red and IR (3 uint8_ts each) int uint8_tsLeftToRead = numberOfSamples * activeLEDs * 3; //Get ready to read a burst of data from the FIFO register //buraya bak _i2c.writeReg(MAX30105_FIFODATA); //We may need to read as many as 288 uint8_ts so we read in blocks no larger than I2C_BUFFER_LENGTH //I2C_BUFFER_LENGTH changes based on the platform. 64 uint8_ts for SAMD21, 32 uint8_ts for Uno. //Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno while (uint8_tsLeftToRead > 0) { int toGet = uint8_tsLeftToRead; if (toGet > I2C_BUFFER_LENGTH) { //If toGet is 32 this is bad because we read 6 uint8_ts (Red+IR * 3 = 6) at a time //32 % 6 = 2 left over. We don't want to request 32 uint8_ts, we want to request 30. //32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27. toGet = I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH % (activeLEDs * 3)); //Trim toGet to be a multiple of the samples we need to read } uint8_tsLeftToRead -= toGet; //Request toGet number of uint8_ts from sensor //buraya bak _i2c.requestFrom(MAX30105_ADDRESS, toGet); while (toGet > 0) { sense.head++; //Advance the head of the storage struct sense.head %= STORAGE_SIZE; //Wrap condition uint8_t temp[sizeof(uint32_t)]; //Array of 4 uint8_ts that we will convert into long uint32_t tempLong; //Burst read three uint8_ts - RED //buraya bak temp[3] = 0; temp[2] = _i2cPort->read(); temp[1] = _i2cPort->read(); temp[0] = _i2cPort->read(); //Convert array to long memcpy(&tempLong, temp, sizeof(tempLong)); tempLong &= 0x3FFFF; //Zero out all but 18 bits sense.red[sense.head] = tempLong; //Store this reading into the sense array if (activeLEDs > 1) { //Burst read three more uint8_ts - IR temp[3] = 0; temp[2] = _i2cPort->read(); temp[1] = _i2cPort->read(); temp[0] = _i2cPort->read(); //Convert array to long memcpy(&tempLong, temp, sizeof(tempLong)); tempLong &= 0x3FFFF; //Zero out all but 18 bits sense.IR[sense.head] = tempLong; } if (activeLEDs > 2) { //Burst read three more uint8_ts - Green temp[3] = 0; temp[2] = _i2cPort->read(); temp[1] = _i2cPort->read(); temp[0] = _i2cPort->read(); //Convert array to long memcpy(&tempLong, temp, sizeof(tempLong)); tempLong &= 0x3FFFF; //Zero out all but 18 bits sense.green[sense.head] = tempLong; } toGet -= activeLEDs * 3; } } //End while (uint8_tsLeftToRead > 0) } //End readPtr != writePtr return (numberOfSamples); //Let the world know how much new data we found */ } //****************************************************************************** //Check for new data but give up after a certain amount of time //Returns true if new data was found //Returns false if new data was not found //Given a register, read it, mask it, and then set the thing void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing) { // Grab current register context uint8_t originalContents = readRegister8(_i2caddr, reg); // Zero-out the portions of the register we're interested in originalContents = originalContents & mask; // Change contents writeRegister8(_i2caddr, reg, originalContents | thing); }