Skip to content
Snippets Groups Projects
Commit 132ab9c0 authored by Jakob's avatar Jakob Committed by Arist
Browse files

fix(max86150): replace Arduino library with a better version

Get samples from FIFO by small junks due to a recommendation from Maxim
not to read the entire FIFO, but rather a fixed number of samples.
See https://os.mbed.com/users/laserdad/code/MAX86150_ECG_PPG//file/3c728f3d1f10/main.cpp/



Additional tuning for particular configuration of max86150 in card10.

Co-Authored-by: default avatarArist <aristkojevnikov@gmail.com>
parent f17417e1
No related branches found
No related tags found
No related merge requests found
...@@ -18,151 +18,132 @@ ...@@ -18,151 +18,132 @@
typedef uint8_t byte; typedef uint8_t byte;
static const uint8_t MAX86150_INTSTAT1 = 0x00; static const uint8_t MAX86150_INTSTAT1 = 0x00;
static const uint8_t MAX86150_INTSTAT2 = 0x01; static const uint8_t MAX86150_INTSTAT2 = 0x01;
static const uint8_t MAX86150_INTENABLE1 = 0x02; static const uint8_t MAX86150_INTENABLE1 = 0x02;
static const uint8_t MAX86150_INTENABLE2 = 0x03; static const uint8_t MAX86150_INTENABLE2 = 0x03;
static const uint8_t MAX86150_FIFOWRITEPTR = 0x04; static const uint8_t MAX86150_FIFOWRITEPTR = 0x04;
static const uint8_t MAX86150_FIFOOVERFLOW = 0x05; static const uint8_t MAX86150_FIFOOVERFLOW = 0x05;
static const uint8_t MAX86150_FIFOREADPTR = 0x06; static const uint8_t MAX86150_FIFOREADPTR = 0x06;
static const uint8_t MAX86150_FIFODATA = 0x07; static const uint8_t MAX86150_FIFODATA = 0x07;
static const uint8_t MAX86150_FIFOCONFIG = 0x08; static const uint8_t MAX86150_FIFOCONFIG = 0x08;
static const uint8_t MAX86150_FIFOCONTROL1= 0x09; static const uint8_t MAX86150_FIFOCONTROL1 = 0x09;
static const uint8_t MAX86150_FIFOCONTROL2 = 0x0A; static const uint8_t MAX86150_FIFOCONTROL2 = 0x0A;
static const uint8_t MAX86150_SYSCONTROL = 0x0D; static const uint8_t MAX86150_SYSCONTROL = 0x0D;
static const uint8_t MAX86150_PPGCONFIG1 = 0x0E; static const uint8_t MAX86150_PPGCONFIG1 = 0x0E;
static const uint8_t MAX86150_PPGCONFIG2 = 0x0F; static const uint8_t MAX86150_PPGCONFIG2 = 0x0F;
static const uint8_t MAX86150_LED_PROX_AMP = 0x10; static const uint8_t MAX86150_LED_PROX_AMP = 0x10;
static const uint8_t MAX86150_LED1_PULSEAMP = 0x11; static const uint8_t MAX86150_LED1_PULSEAMP = 0x11;
static const uint8_t MAX86150_LED2_PULSEAMP = 0x12; static const uint8_t MAX86150_LED2_PULSEAMP = 0x12;
static const uint8_t MAX86150_LED_RANGE = 0x14; static const uint8_t MAX86150_LED_RANGE = 0x14;
static const uint8_t MAX86150_LED_PILOT_PA = 0x15; static const uint8_t MAX86150_LED_PILOT_PA = 0x15;
static const uint8_t MAX86150_ECG_CONFIG1 = 0x3C; static const uint8_t MAX86150_ECG_CONFIG1 = 0x3C;
static const uint8_t MAX86150_ECG_CONFIG3 = 0x3E; static const uint8_t MAX86150_ECG_CONFIG3 = 0x3E;
static const uint8_t MAX86150_PROXINTTHRESH = 0x10; static const uint8_t MAX86150_PROXINTTHRESH = 0x10;
static const uint8_t MAX86150_PARTID = 0xFF; static const uint8_t MAX86150_PARTID = 0xFF;
// MAX86150 Commands // MAX86150 Commands
static const uint8_t MAX86150_INT_A_FULL_MASK = (byte)~0b10000000; static const uint8_t MAX86150_INT_A_FULL_MASK = (byte)~0b10000000;
static const uint8_t MAX86150_INT_A_FULL_ENABLE = 0x80; static const uint8_t MAX86150_INT_A_FULL_ENABLE = 0x80;
static const uint8_t MAX86150_INT_A_FULL_DISABLE = 0x00; static const uint8_t MAX86150_INT_A_FULL_DISABLE = 0x00;
static const uint8_t MAX86150_INT_DATA_RDY_MASK = (byte)~0b01000000; static const uint8_t MAX86150_INT_DATA_RDY_MASK = (byte)~0b01000000;
static const uint8_t MAX86150_INT_DATA_RDY_ENABLE = 0x40; static const uint8_t MAX86150_INT_DATA_RDY_ENABLE = 0x40;
static const uint8_t MAX86150_INT_DATA_RDY_DISABLE = 0x00; static const uint8_t MAX86150_INT_DATA_RDY_DISABLE = 0x00;
static const uint8_t MAX86150_INT_ALC_OVF_MASK = (byte)~0b00100000; static const uint8_t MAX86150_INT_ALC_OVF_MASK = (byte)~0b00100000;
static const uint8_t MAX86150_INT_ALC_OVF_ENABLE = 0x20; static const uint8_t MAX86150_INT_ALC_OVF_ENABLE = 0x20;
static const uint8_t MAX86150_INT_ALC_OVF_DISABLE = 0x00; static const uint8_t MAX86150_INT_ALC_OVF_DISABLE = 0x00;
static const uint8_t MAX86150_INT_PROX_INT_MASK = (byte)~0b00010000; static const uint8_t MAX86150_INT_PROX_INT_MASK = (byte)~0b00010000;
static const uint8_t MAX86150_INT_PROX_INT_ENABLE = 0x10; static const uint8_t MAX86150_INT_PROX_INT_ENABLE = 0x10;
static const uint8_t MAX86150_INT_PROX_INT_DISABLE = 0x00; static const uint8_t MAX86150_INT_PROX_INT_DISABLE = 0x00;
static const uint8_t MAX86150_SAMPLEAVG_MASK = (byte)~0b11100000; static const uint8_t MAX86150_SAMPLEAVG_MASK = (byte)~0b00000111;
static const uint8_t MAX86150_SAMPLEAVG_1 = 0x00;
static const uint8_t MAX86150_SAMPLEAVG_2 = 0x20; static const uint8_t MAX86150_ROLLOVER_MASK = (byte)~0b00010000;
static const uint8_t MAX86150_SAMPLEAVG_4 = 0x40; static const uint8_t MAX86150_ROLLOVER_ENABLE = 0b00010000;
static const uint8_t MAX86150_SAMPLEAVG_8 = 0x60; static const uint8_t MAX86150_ROLLOVER_DISABLE = 0b00000000;
static const uint8_t MAX86150_SAMPLEAVG_16 = 0x80;
static const uint8_t MAX86150_SAMPLEAVG_32 = 0xA0; static const uint8_t MAX86150_ALMOST_FULL_CLEAR_MASK = (byte)~0b01000000;
static const uint8_t MAX86150_ALMOST_FULL_CLEAR_ENABLE = 0b01000000;
static const uint8_t MAX86150_ROLLOVER_MASK = 0xEF; static const uint8_t MAX86150_ALMOST_FULL_CLEAR_DISABLE = 0b00000000;
static const uint8_t MAX86150_ROLLOVER_ENABLE = 0x10;
static const uint8_t MAX86150_ROLLOVER_DISABLE = 0x00; static const uint8_t MAX86150_ALMOST_FULL_REPEAT_MASK = (byte)~0b00100000;
static const uint8_t MAX86150_ALMOST_FULL_REPEAT_ENABLE = 0b00100000;
static const uint8_t MAX86150_A_FULL_MASK = 0xF0; static const uint8_t MAX86150_ALMOST_FULL_REPEAT_DISABLE = 0b00000000;
static const uint8_t MAX86150_SHUTDOWN_MASK = 0x7F; static const uint8_t MAX86150_A_FULL_MASK = (byte)~0b00001111;
static const uint8_t MAX86150_SHUTDOWN = 0x80;
static const uint8_t MAX86150_WAKEUP = 0x00; static const uint8_t MAX86150_SHUTDOWN_MASK = (byte)~0b00000010;
static const uint8_t MAX86150_SHUTDOWN = 0b10;
static const uint8_t MAX86150_RESET_MASK = 0xFE; static const uint8_t MAX86150_WAKEUP = 0b00;
static const uint8_t MAX86150_RESET = 0x01;
static const uint8_t MAX86150_FIFO_ENABLE_MASK = (byte)~0b00000100;
static const uint8_t MAX86150_MODE_MASK = 0xF8; static const uint8_t MAX86150_FIFO_ENABLE = 0b100;
static const uint8_t MAX86150_MODE_REDONLY = 0x02; static const uint8_t MAX86150_FIFO_DISABLE = 0b000;
static const uint8_t MAX86150_MODE_REDIRONLY = 0x03;
static const uint8_t MAX86150_MODE_MULTILED = 0x07; static const uint8_t MAX86150_RESET_MASK = (byte)~0b00000001;
static const uint8_t MAX86150_RESET = 0b1;
static const uint8_t MAX86150_ADCRANGE_MASK = 0x9F;
static const uint8_t MAX86150_ADCRANGE_2048 = 0x00; static const uint8_t MAX86150_ADCRANGE_MASK = (byte)~0b11000000;
static const uint8_t MAX86150_ADCRANGE_4096 = 0x20;
static const uint8_t MAX86150_ADCRANGE_8192 = 0x40; static const uint8_t MAX86150_PPG_SAMPLERATE_MASK = (byte)~0b00111100;
static const uint8_t MAX86150_ADCRANGE_16384 = 0x60;
static const uint8_t MAX86150_PPG_PULSEWIDTH_MASK = (byte)~0b00000011;
static const uint8_t MAX86150_SAMPLERATE_MASK = 0xE3;
static const uint8_t MAX86150_SAMPLERATE_50 = 0x00; static const uint8_t MAX86150_SLOT1_MASK = 0xF0;
static const uint8_t MAX86150_SAMPLERATE_100 = 0x04; static const uint8_t MAX86150_SLOT2_MASK = 0x0F;
static const uint8_t MAX86150_SAMPLERATE_200 = 0x08; static const uint8_t MAX86150_SLOT3_MASK = 0xF0;
static const uint8_t MAX86150_SAMPLERATE_400 = 0x0C; static const uint8_t MAX86150_SLOT4_MASK = 0x0F;
static const uint8_t MAX86150_SAMPLERATE_800 = 0x10;
static const uint8_t MAX86150_SAMPLERATE_1000 = 0x14; static const uint8_t MAX86150_LED1_RANGE_MASK = (byte)~0b00000011;
static const uint8_t MAX86150_SAMPLERATE_1600 = 0x18; static const uint8_t MAX86150_LED2_RANGE_MASK = (byte)~0b00001100;
static const uint8_t MAX86150_SAMPLERATE_3200 = 0x1C;
static const uint8_t MAX86150_ECG_SAMPLERATE_MASK = (byte)~0b00000111;
static const uint8_t MAX86150_PULSEWIDTH_MASK = 0xFC;
static const uint8_t MAX86150_PULSEWIDTH_69 = 0x00; static const uint8_t MAX86150_ECG_PGA_GAIN_MASK = (byte)~0b00001100;
static const uint8_t MAX86150_PULSEWIDTH_118 = 0x01;
static const uint8_t MAX86150_PULSEWIDTH_215 = 0x02; static const uint8_t MAX86150_ECG_IA_GAIN_MASK = (byte)~0b00000011;
static const uint8_t MAX86150_PULSEWIDTH_411 = 0x03;
static const uint8_t MAX86150_EXPECTEDPARTID = 0x1E;
static const uint8_t MAX86150_SLOT1_MASK = 0xF0;
static const uint8_t MAX86150_SLOT2_MASK = 0x0F; static byte activeDevices =
static const uint8_t MAX86150_SLOT3_MASK = 0xF0; 3; //Gets set during max86150_setup. Allows max86150_check() to calculate how many bytes to read from FIFO
static const uint8_t MAX86150_SLOT4_MASK = 0x0F;
#define STORAGE_SIZE \
static const uint8_t SLOT_NONE = 0x00; 128 //Each long is 4 bytes so limit this to fit on your micro
static const uint8_t SLOT_RED_LED = 0x01; typedef struct Record {
static const uint8_t SLOT_IR_LED = 0x02; uint32_t red[STORAGE_SIZE];
static const uint8_t SLOT_RED_PILOT = 0x09; uint32_t IR[STORAGE_SIZE];
static const uint8_t SLOT_IR_PILOT = 0x0A; int32_t ecg[STORAGE_SIZE];
static const uint8_t SLOT_ECG = 0x0D; byte head;
byte tail;
static const uint8_t MAX_30105_EXPECTEDPARTID = 0x1E;
static uint8_t _i2caddr;
//activeLEDs is the number of channels turned on, and can be 1 to 3. 2 is common for Red+IR.
static byte activeDevices; //Gets set during max86150_setup. Allows max86150_check() to calculate how many bytes to read from FIFO
static void max86150_bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
#define STORAGE_SIZE 128 //Each long is 4 bytes so limit this to fit on your micro
typedef struct Record
{
uint32_t red[STORAGE_SIZE];
uint32_t IR[STORAGE_SIZE];
int32_t ecg[STORAGE_SIZE];
byte head;
byte tail;
} sense_struct; //This is our circular buffer of readings from the sensor } sense_struct; //This is our circular buffer of readings from the sensor
static sense_struct sense; static sense_struct sense;
static void delay(int ms) static void delay(int ms)
{ {
TMR_Delay(MXC_TMR0, MSEC(ms), 0); TMR_Delay(MXC_TMR0, MSEC(ms), 0);
} }
bool max86150_begin(void) bool max86150_begin(void)
{ {
_i2caddr = MAX86150_ADDRESS; // Step 1: Initial Communication and Verification
// Check that a MAX86150 is connected
// Step 1: Initial Communication and Verification if (max86150_read_part_id() != MAX86150_EXPECTEDPARTID) {
// Check that a MAX86150 is connected // Error -- Part ID read from MAX86150 does not match expected part ID.
if (max86150_readPartID() != MAX_30105_EXPECTEDPARTID) { // This may mean there is a physical connectivity problem (broken wire, unpowered, etc).
// Error -- Part ID read from MAX86150 does not match expected part ID. return false;
// This may mean there is a physical connectivity problem (broken wire, unpowered, etc). }
return false; return true;
}
return true;
} }
// //
...@@ -170,498 +151,704 @@ bool max86150_begin(void) ...@@ -170,498 +151,704 @@ bool max86150_begin(void)
// //
//Begin Interrupt configuration //Begin Interrupt configuration
uint8_t max86150_getINT1(void) uint8_t max86150_get_int1(void)
{ {
return (max86150_readRegister8(_i2caddr, MAX86150_INTSTAT1)); return (max86150_read_register(MAX86150_ADDRESS, MAX86150_INTSTAT1));
}
uint8_t max86150_getINT2(void) {
return (max86150_readRegister8(_i2caddr, MAX86150_INTSTAT2));
}
void max86150_enableAFULL(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_A_FULL_MASK, MAX86150_INT_A_FULL_ENABLE);
} }
void max86150_disableAFULL(void) { uint8_t max86150_get_int2(void)
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_A_FULL_MASK, MAX86150_INT_A_FULL_DISABLE); {
} return (max86150_read_register(MAX86150_ADDRESS, MAX86150_INTSTAT2));
void max86150_enableDATARDY(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_DATA_RDY_MASK, MAX86150_INT_DATA_RDY_ENABLE);
}
void max86150_disableDATARDY(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_DATA_RDY_MASK, MAX86150_INT_DATA_RDY_DISABLE);
}
void max86150_enableALCOVF(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_ALC_OVF_MASK, MAX86150_INT_ALC_OVF_ENABLE);
}
void max86150_disableALCOVF(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_ALC_OVF_MASK, MAX86150_INT_ALC_OVF_DISABLE);
} }
void max86150_enablePROXINT(void) { void max86150_set_int_full(bool enabled)
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_PROX_INT_MASK, MAX86150_INT_PROX_INT_ENABLE); {
} if (enabled) {
void max86150_disablePROXINT(void) { max86150_bit_mask(
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_PROX_INT_MASK, MAX86150_INT_PROX_INT_DISABLE); MAX86150_INTENABLE1,
MAX86150_INT_A_FULL_MASK,
MAX86150_INT_A_FULL_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_A_FULL_MASK,
MAX86150_INT_A_FULL_DISABLE
);
}
}
void max86150_set_int_datardy(bool enabled)
{
if (enabled) {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_DATA_RDY_MASK,
MAX86150_INT_DATA_RDY_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_DATA_RDY_MASK,
MAX86150_INT_DATA_RDY_DISABLE
);
}
}
void max86150_set_int_ambient_light_overflow(bool enabled)
{
if (enabled) {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_ALC_OVF_MASK,
MAX86150_INT_ALC_OVF_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_ALC_OVF_MASK,
MAX86150_INT_ALC_OVF_DISABLE
);
}
}
void max86150_set_int_proximity(bool enabled)
{
if (enabled) {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_PROX_INT_MASK,
MAX86150_INT_PROX_INT_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_INTENABLE1,
MAX86150_INT_PROX_INT_MASK,
MAX86150_INT_PROX_INT_DISABLE
);
}
} }
//End Interrupt configuration //End Interrupt configuration
void max86150_softReset(void) { void max86150_soft_reset(void)
max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_RESET_MASK, MAX86150_RESET); {
max86150_bit_mask(
// Poll for bit to clear, reset is then complete MAX86150_SYSCONTROL, MAX86150_RESET_MASK, MAX86150_RESET
// Timeout after 100ms );
//TODO
//unsigned long startTime = millis(); // Poll for bit to clear, reset is then complete
//while (millis() - startTime < 100) // Timeout after 100 tries
{ uint8_t tries = 0;
//uint8_t response = max86150_readRegister8(_i2caddr, MAX86150_SYSCONTROL); while (tries < 100) {
//if ((response & MAX86150_RESET) == 0) break; //We're done! uint8_t response = max86150_read_register(
delay(1); //Let's not over burden the I2C bus MAX86150_ADDRESS, MAX86150_SYSCONTROL
} );
} if ((response & MAX86150_RESET) == 0)
break; //We're done!
void max86150_shutDown(void) { tries++;
// Put IC into low power mode (datasheet pg. 19) delay(1); //Let's not over burden the I2C bus
// During shutdown the IC will continue to respond to I2C commands but will }
// not update with or take new readings (such as temperature) }
max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN);
} void max86150_shut_down(void)
{
void max86150_wakeUp(void) { // Put IC into low power mode (datasheet pg. 19)
// Pull IC out of low power mode (datasheet pg. 19) // During shutdown the IC will continue to respond to I2C commands but will
max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP); // not update with or take new readings (such as temperature)
max86150_bit_mask(
MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN
);
} }
void max86150_setLEDMode(uint8_t mode) { void max86150_wake_up(void)
// Set which LEDs are used for sampling -- Red only, RED+IR only, or custom. {
// See datasheet, page 19 // Pull IC out of low power mode (datasheet pg. 19)
//max86150_bitMask(MAX86150_PPGCONFIG1, MAX86150_MODE_MASK, mode); max86150_bit_mask(
MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP
);
} }
void max86150_setADCRange(uint8_t adcRange) { void max86150_set_fifo_enable(bool enabled)
// adcRange: one of MAX86150_ADCRANGE_2048, _4096, _8192, _16384 {
//max86150_bitMask(MAX86150_PARTICLECONFIG, MAX86150_ADCRANGE_MASK, adcRange); if (enabled) {
max86150_bit_mask(
MAX86150_SYSCONTROL,
MAX86150_FIFO_ENABLE_MASK,
MAX86150_FIFO_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_SYSCONTROL,
MAX86150_FIFO_ENABLE_MASK,
MAX86150_FIFO_DISABLE
);
}
}
void max86150_set_ppg_adc_range(uint8_t adcRange)
{
// adcRange: one of MAX86150_ADCRANGE_*
max86150_bit_mask(
MAX86150_PPGCONFIG1, MAX86150_ADCRANGE_MASK, adcRange
);
} }
void max86150_setSampleRate(uint8_t sampleRate) { void max86150_set_ppg_sample_rate(uint8_t sampleRate)
// sampleRate: one of MAX86150_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200 {
//max86150_bitMask(MAX86150_PARTICLECONFIG, MAX86150_SAMPLERATE_MASK, sampleRate); // sampleRate: one of MAX86150_PPG_SAMPLERATE_*
max86150_bit_mask(
MAX86150_PPGCONFIG1, MAX86150_PPG_SAMPLERATE_MASK, sampleRate
);
} }
void max86150_setPulseWidth(uint8_t pulseWidth) { void max86150_set_ppg_pulse_width(uint8_t pulseWidth)
// pulseWidth: one of MAX86150_PULSEWIDTH_69, _188, _215, _411 {
//max86150_bitMask(MAX86150_PPGCONFIG1, MAX86150_PULSEWIDTH_MASK, pulseWidth); // pulseWidth: one of MAX86150_PPG_PULSEWIDTH_*
max86150_bit_mask(
MAX86150_PPGCONFIG1, MAX86150_PPG_PULSEWIDTH_MASK, pulseWidth
);
} }
// NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical) // NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
// See datasheet, page 21 // See datasheet, page 21
void max86150_setPulseAmplitudeRed(uint8_t amplitude) void max86150_set_led_red_amplitude(uint8_t amplitude)
{ {
max86150_writeRegister8(_i2caddr, MAX86150_LED2_PULSEAMP, amplitude); max86150_write_register(
MAX86150_ADDRESS, MAX86150_LED2_PULSEAMP, amplitude
);
max86150_bit_mask(
MAX86150_LED_RANGE,
MAX86150_LED2_RANGE_MASK,
MAX86150_LED2_RANGE_50
);
} }
void max86150_setPulseAmplitudeIR(uint8_t amplitude) void max86150_set_led_ir_amplitude(uint8_t amplitude)
{ {
max86150_writeRegister8(_i2caddr, MAX86150_LED1_PULSEAMP, amplitude); max86150_write_register(
MAX86150_ADDRESS, MAX86150_LED1_PULSEAMP, amplitude
);
max86150_bit_mask(
MAX86150_LED_RANGE,
MAX86150_LED1_RANGE_MASK,
MAX86150_LED1_RANGE_50
);
} }
void max86150_setPulseAmplitudeProximity(uint8_t amplitude) { void max86150_set_led_proximity_amplitude(uint8_t amplitude)
max86150_writeRegister8(_i2caddr, MAX86150_LED_PROX_AMP, amplitude); {
max86150_write_register(
MAX86150_ADDRESS, MAX86150_LED_PROX_AMP, amplitude
);
} }
void max86150_setProximityThreshold(uint8_t threshMSB) void max86150_set_proximity_threshold(uint8_t threshMSB)
{ {
// The threshMSB signifies only the 8 most significant-bits of the ADC count. // The threshMSB signifies only the 8 most significant-bits of the ADC count.
max86150_writeRegister8(_i2caddr, MAX86150_PROXINTTHRESH, threshMSB); max86150_write_register(
MAX86150_ADDRESS, MAX86150_PROXINTTHRESH, threshMSB
);
} }
//Given a slot number assign a thing to it //Given a slot number assign a thing to it
//Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity) //Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity)
//Assigning a SLOT_RED_LED will pulse LED //Assigning a SLOT_RED_LED will pulse LED
//Assigning a SLOT_RED_PILOT will ?? //Assigning a SLOT_RED_PILOT will ??
void max86150_enableSlot(uint8_t slotNumber, uint8_t device) void max86150_fifo_enable_slot(uint8_t slotNumber, uint8_t device)
{ {
//uint8_t originalContents; switch (slotNumber) {
case (1):
switch (slotNumber) { max86150_bit_mask(
case (1): MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device
max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device); );
break; break;
case (2): case (2):
max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT2_MASK, device << 4); max86150_bit_mask(
break; MAX86150_FIFOCONTROL1,
case (3): MAX86150_SLOT2_MASK,
max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device); device << 4
break; );
case (4): break;
max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT4_MASK, device << 4); case (3):
break; max86150_bit_mask(
default: MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device
//Shouldn't be here! );
break; break;
} case (4):
max86150_bit_mask(
MAX86150_FIFOCONTROL2,
MAX86150_SLOT4_MASK,
device << 4
);
break;
default:
//Shouldn't be here!
break;
}
} }
//Clears all slot assignments //Clears all slot assignments
void max86150_disableSlots(void) void max86150_disableSlots(void)
{ {
max86150_writeRegister8(_i2caddr, MAX86150_FIFOCONTROL1, 0); max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOCONTROL1, 0);
max86150_writeRegister8(_i2caddr, MAX86150_FIFOCONTROL2, 0); max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOCONTROL2, 0);
} }
// //
// FIFO Configuration // FIFO Configuration
// //
void max86150_setFIFOAverage(uint8_t numberOfSamples) void max86150_set_ppg_averaging(uint8_t numberOfSamples)
{ {
max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_SAMPLEAVG_MASK, numberOfSamples); max86150_bit_mask(
MAX86150_FIFOCONFIG, MAX86150_SAMPLEAVG_MASK, numberOfSamples
);
} }
//Resets all points to start in a known state //Resets all points to start in a known state
void max86150_clearFIFO(void) { void max86150_clear_fifo(void)
max86150_writeRegister8(_i2caddr, MAX86150_FIFOWRITEPTR, 0); {
max86150_writeRegister8(_i2caddr, MAX86150_FIFOOVERFLOW, 0); max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOWRITEPTR, 0);
max86150_writeRegister8(_i2caddr, MAX86150_FIFOREADPTR, 0); max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOOVERFLOW, 0);
max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOREADPTR, 0);
} }
//Enable roll over if FIFO over flows //Enable roll over if FIFO over flows
void max86150_enableFIFORollover(void) { void max86150_set_fifo_rollover(bool enabled)
max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_ROLLOVER_MASK, MAX86150_ROLLOVER_ENABLE); {
} if (enabled) {
max86150_bit_mask(
//Disable roll over if FIFO over flows MAX86150_FIFOCONFIG,
void max86150_disableFIFORollover(void) { MAX86150_ROLLOVER_MASK,
max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_ROLLOVER_MASK, MAX86150_ROLLOVER_DISABLE); MAX86150_ROLLOVER_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_FIFOCONFIG,
MAX86150_ROLLOVER_MASK,
MAX86150_ROLLOVER_DISABLE
);
}
}
//Enable fifo almost full flag clear on data read
void max86150_set_fifo_almost_full_clear(bool enabled)
{
if (enabled) {
max86150_bit_mask(
MAX86150_FIFOCONFIG,
MAX86150_ALMOST_FULL_CLEAR_MASK,
MAX86150_ALMOST_FULL_CLEAR_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_FIFOCONFIG,
MAX86150_ALMOST_FULL_CLEAR_MASK,
MAX86150_ALMOST_FULL_CLEAR_DISABLE
);
}
}
//Enable fifo almost full flag repeated assertion
void max86150_set_fifo_almost_full_repeat(bool enabled)
{
if (enabled) {
max86150_bit_mask(
MAX86150_FIFOCONFIG,
MAX86150_ALMOST_FULL_REPEAT_MASK,
MAX86150_ALMOST_FULL_REPEAT_ENABLE
);
} else {
max86150_bit_mask(
MAX86150_FIFOCONFIG,
MAX86150_ALMOST_FULL_REPEAT_MASK,
MAX86150_ALMOST_FULL_REPEAT_DISABLE
);
}
} }
//Power on default is 32 samples //Power on default is 32 samples
//Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples //Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples
void max86150_setFIFOAlmostFull(uint8_t numberOfSamples) { void max86150_set_fifo_almost_full(uint8_t numberOfSamples)
max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_A_FULL_MASK, numberOfSamples); {
max86150_bit_mask(
MAX86150_FIFOCONFIG, MAX86150_A_FULL_MASK, numberOfSamples
);
} }
//Read the FIFO Write Pointer //Read the FIFO Write Pointer
uint8_t max86150_getWritePointer(void) { uint8_t max86150_get_fifo_write_pointer(void)
return (max86150_readRegister8(_i2caddr, MAX86150_FIFOWRITEPTR)); {
return (max86150_read_register(MAX86150_ADDRESS, MAX86150_FIFOWRITEPTR));
} }
//Read the FIFO Read Pointer //Read the FIFO Read Pointer
uint8_t max86150_getReadPointer(void) { uint8_t max86150_get_fifo_read_pointer(void)
return (max86150_readRegister8(_i2caddr, MAX86150_FIFOREADPTR)); {
} return (max86150_read_register(MAX86150_ADDRESS, MAX86150_FIFOREADPTR));
// Set the PROX_INT_THRESHold
void max86150_setPROXINTTHRESH(uint8_t val) {
max86150_writeRegister8(_i2caddr, MAX86150_PROXINTTHRESH, val);
} }
// //
// Device ID and Revision // Device ID and Revision
// //
uint8_t max86150_readPartID() { uint8_t max86150_read_part_id()
return max86150_readRegister8(_i2caddr, MAX86150_PARTID); {
return max86150_read_register(MAX86150_ADDRESS, MAX86150_PARTID);
} }
//Setup the sensor //Set ecg sample rate
//The MAX86150 has many settings. By default we select: void max86150_set_ecg_sample_rate(uint8_t sampleRate)
// Sample Average = 4
// Mode = MultiLED
// ADC Range = 16384 (62.5pA per LSB)
// Sample rate = 50
//Use the default max86150_setup if you are just getting started with the MAX86150 sensor
void max86150_setup(byte powerLevel, byte sampleAverage, byte ledMode, int sampleRate, int pulseWidth, int adcRange)
{ {
activeDevices=3; // sampleRate: one of MAX86150_ECG_SAMPLERATE_*
max86150_writeRegister8(_i2caddr,MAX86150_SYSCONTROL,0x01); max86150_bit_mask(
delay(100); MAX86150_ECG_CONFIG1, MAX86150_ECG_SAMPLERATE_MASK, sampleRate
max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONFIG,0x7F); );
}
//FIFO Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//The chip will average multiple samples of same type together if you wish
if (sampleAverage == 1) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_2);
else if (sampleAverage == 4) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_4);
else if (sampleAverage == 8) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_8);
else if (sampleAverage == 16) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_16);
else if (sampleAverage == 32) max86150_setFIFOAverage(MAX86150_SAMPLEAVG_32);
else max86150_setFIFOAverage(MAX86150_SAMPLEAVG_4);
uint16_t FIFOCode = 0x00;
FIFOCode = FIFOCode<<4 | 0x0009;// : FIFOCode; //insert ECG front of ETI in FIFO
FIFOCode = FIFOCode<<8 | 0x0021;//) : FIFOCode; //insert Red(2) and IR (1) in front of ECG in FIFO
//Set ecg pga gain
void max86150_set_ecg_pga_gain(uint8_t gain)
{
// sampleRate: one of MAX86150_ECG_PGA_GAIN_*
max86150_bit_mask(
MAX86150_ECG_CONFIG3, MAX86150_ECG_PGA_GAIN_MASK, gain
);
}
//FIFO Control 1 = FD2|FD1, FIFO Control 2 = FD4|FD3 //Set ecg pga gain
void max86150_set_ecg_instrumentation_amplifier_gain(uint8_t gain)
{
// sampleRate: one of MAX86150_ECG_IA_GAIN_*
max86150_bit_mask(
MAX86150_ECG_CONFIG3, MAX86150_ECG_IA_GAIN_MASK, gain
);
}
max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1,(0b00100001)); //Setup the sensor
//max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1,(0b00001001)); //The MAX86150 has many settings.
//max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1,(0b00000010)); //Use the default max86150_setup if you are just getting started with the MAX86150 sensor
max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2,(0b00001001)); void max86150_setup(const uint8_t ppg_sample_rate)
//max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2,(0b00000000)); {
//max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1, (char)(FIFOCode & 0x00FF) ); max86150_soft_reset();
//max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2, (char)(FIFOCode >>8) ); max86150_set_ppg_averaging(MAX86150_SAMPLEAVG_4);
max86150_set_fifo_rollover(true);
max86150_set_fifo_almost_full(8);
max86150_set_fifo_almost_full_clear(true);
max86150_set_fifo_almost_full_repeat(true);
max86150_writeRegister8(_i2caddr,MAX86150_PPGCONFIG1,0b11010001); max86150_fifo_enable_slot(1, MAX86150_SLOT_RED_LED);
//max86150_writeRegister8(_i2caddr,MAX86150_PPGCONFIG1,0b11100111); max86150_fifo_enable_slot(2, MAX86150_SLOT_IR_LED);
max86150_fifo_enable_slot(3, MAX86150_SLOT_ECG);
//max86150_fifo_enable_slot(4, MAX86150_SLOT_NONE);
max86150_writeRegister8(_i2caddr,MAX86150_PPGCONFIG2, 0x06); max86150_set_ppg_adc_range(MAX86150_ADCRANGE_16384);
max86150_writeRegister8(_i2caddr,MAX86150_LED_RANGE, 0x00 ); // PPG_ADC_RGE: 32768nA max86150_set_ppg_sample_rate(ppg_sample_rate);
max86150_set_ppg_pulse_width(MAX86150_PPG_PULSEWIDTH_100);
max86150_writeRegister8(_i2caddr,MAX86150_SYSCONTROL,0x04);//start FIFO max86150_set_led_ir_amplitude(0x66);
max86150_set_led_red_amplitude(0x66);
max86150_writeRegister8(_i2caddr,MAX86150_ECG_CONFIG1,0b00000011); max86150_set_ecg_sample_rate(MAX86150_ECG_SAMPLERATE_200);
//max86150_writeRegister8(_i2caddr,MAX86150_ECG_CONFIG1,0b00000001); max86150_set_ecg_pga_gain(MAX86150_ECG_PGA_GAIN_8);
max86150_writeRegister8(_i2caddr,MAX86150_ECG_CONFIG3,0b00001101); max86150_set_ecg_instrumentation_amplifier_gain(
MAX86150_ECG_IA_GAIN_9_5
);
max86150_setPulseAmplitudeRed(0xFF); max86150_set_int_datardy(false);
max86150_setPulseAmplitudeIR(0xFF); max86150_set_int_full(true);
//Multi-LED Mode Configuration, Enable the reading of the three LEDs max86150_clear_fifo(); //Reset the FIFO before we begin checking the sensor
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//max86150_enableSlot(1, SLOT_RED_LED);
//if (ledMode > 1)
//max86150_enableSlot(2, SLOT_IR_LED);
//if (ledMode > 2)
//max86150_enableSlot(3, SLOT_ECG);
//max86150_enableSlot(1, SLOT_RED_PILOT);
//max86150_enableSlot(2, SLOT_IR_PILOT);
//max86150_enableSlot(3, SLOT_GREEN_PILOT);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
max86150_clearFIFO(); //Reset the FIFO before we begin checking the sensor max86150_set_fifo_enable(true);
} }
//Tell caller how many samples are max86150_available //Tell caller how many samples are max86150_available
uint8_t max86150_available(void) uint8_t max86150_available(void)
{ {
int8_t numberOfSamples = sense.head - sense.tail; int8_t numberOfSamples = sense.head - sense.tail;
if (numberOfSamples < 0) numberOfSamples += STORAGE_SIZE; if (numberOfSamples < 0)
numberOfSamples += STORAGE_SIZE;
return (numberOfSamples); return (numberOfSamples);
} }
//Report the most recent red value //Report the most recent red value
uint32_t max86150_getRed(void) uint32_t max86150_get_red(void)
{ {
//Check the sensor for new data for 250ms //Check the sensor for new data for 250ms
if(max86150_safeCheck(250)) if (max86150_safeCheck(250))
return (sense.red[sense.head]); return (sense.red[sense.head]);
else else
return(0); //Sensor failed to find new data return (0); //Sensor failed to find new data
} }
//Report the most recent IR value //Report the most recent IR value
uint32_t max86150_getIR(void) uint32_t max86150_get_ir(void)
{ {
//Check the sensor for new data for 250ms //Check the sensor for new data for 250ms
if(max86150_safeCheck(250)) if (max86150_safeCheck(250))
return (sense.IR[sense.head]); return (sense.IR[sense.head]);
else else
return(0); //Sensor failed to find new data return (0); //Sensor failed to find new data
} }
//Report the most recent Green value //Report the most recent Green value
int32_t max86150_getECG(void) int32_t max86150_get_ecg(void)
{ {
//Check the sensor for new data for 250ms //Check the sensor for new data for 250ms
if(max86150_safeCheck(250)) if (max86150_safeCheck(250))
return (sense.ecg[sense.head]); return (sense.ecg[sense.head]);
else else
return(0); //Sensor failed to find new data return (0); //Sensor failed to find new data
} }
//Report the next Red value in the FIFO //Report the next Red value in the FIFO
uint32_t max86150_getFIFORed(void) uint32_t max86150_get_fifo_red(void)
{ {
return (sense.red[sense.tail]); return (sense.red[sense.tail]);
} }
//Report the next IR value in the FIFO //Report the next IR value in the FIFO
uint32_t max86150_getFIFOIR(void) uint32_t max86150_get_fifo_ir(void)
{ {
return (sense.IR[sense.tail]); return (sense.IR[sense.tail]);
} }
//Report the next Green value in the FIFO //Report the next Green value in the FIFO
int32_t max86150_getFIFOECG(void) int32_t max86150_get_fifo_ecg(void)
{ {
return (sense.ecg[sense.tail]); return (sense.ecg[sense.tail]);
} }
//Advance the tail //Advance the tail
void max86150_nextSample(void) void max86150_next_sample(void)
{ {
if(max86150_available()) //Only advance the tail if new data is max86150_available if (max86150_available()) { //Only advance the tail if new data is max86150_available
{ sense.tail++;
sense.tail++; sense.tail %= STORAGE_SIZE; //Wrap condition
sense.tail %= STORAGE_SIZE; //Wrap condition }
}
} }
//Polls the sensor for new data
//Call regularly
//If new data is max86150_available, it updates the head and tail in the main struct
//Returns number of new samples obtained
uint8_t max86150_get_sample(uint32_t *red, uint32_t *ir, int32_t *ecg)
{
//Read register FIDO_DATA in (3-byte * number of active LED) chunks
//Until FIFO_RD_PTR = FIFO_WR_PTR
byte readPointer = max86150_get_fifo_read_pointer();
byte writePointer = max86150_get_fifo_write_pointer();
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
//Get ready to read a burst of data from the FIFO register
uint8_t command[] = { MAX86150_FIFODATA };
// Important! true is for repeated start (since we are not reading complete fifo)
// See https://os.mbed.com/users/laserdad/code/MAX86150_ECG_PPG//file/3c728f3d1f10/main.cpp/
I2C_MasterWrite(
MXC_I2C1_BUS0, MAX86150_ADDRESS << 1, command, 1, true
);
if (numberOfSamples > 0) {
uint8_t data[3 * 3];
I2C_MasterRead(
MXC_I2C1_BUS0,
MAX86150_ADDRESS << 1,
data,
3 * 3,
0
);
*red = (data[0] << 16) | (data[1] << 8) | (data[2]);
*ir = (data[3] << 16) | (data[4] << 8) | (data[5]);
*ecg = (data[6] << 16) | (data[7] << 8) | (data[8]);
}
} //End readPtr != writePtr
return (numberOfSamples); //Let the world know how much new data we found
}
//Polls the sensor for new data //Polls the sensor for new data
//Call regularly //Call regularly
//If new data is max86150_available, it updates the head and tail in the main struct //If new data is max86150_available, it updates the head and tail in the main struct
//Returns number of new samples obtained //Returns number of new samples obtained
uint16_t max86150_check(void) uint16_t max86150_check(void)
{ {
//Read register FIDO_DATA in (3-byte * number of active LED) chunks //Read register FIDO_DATA in (3-byte * number of active LED) chunks
//Until FIFO_RD_PTR = FIFO_WR_PTR //Until FIFO_RD_PTR = FIFO_WR_PTR
byte readPointer = max86150_getReadPointer(); byte readPointer = max86150_get_fifo_read_pointer();
byte writePointer = max86150_getWritePointer(); byte writePointer = max86150_get_fifo_write_pointer();
int numberOfSamples = 0; int numberOfSamples = 0;
//Do we have new data? //Do we have new data?
if (readPointer != writePointer) if (readPointer != writePointer) {
{ //Calculate the number of readings we need to get from sensor
//Calculate the number of readings we need to get from sensor numberOfSamples = writePointer - readPointer;
numberOfSamples = writePointer - readPointer; if (numberOfSamples < 0)
if (numberOfSamples < 0) numberOfSamples += 32; //Wrap condition numberOfSamples += 32; //Wrap condition
//We now have the number of readings, now calc bytes to read //We now have the number of readings, now calc bytes to read
//For this example we are just doing Red and IR (3 bytes each) //For this example we are just doing Red and IR (3 bytes each)
int bytesLeftToRead = numberOfSamples * activeDevices * 3; int bytesLeftToRead = numberOfSamples * activeDevices * 3;
//Get ready to read a burst of data from the FIFO register //Get ready to read a burst of data from the FIFO register
uint8_t command[] = {MAX86150_FIFODATA}; uint8_t command[] = { MAX86150_FIFODATA };
I2C_MasterWrite(MXC_I2C1_BUS0, _i2caddr << 1, command, 1, 0); I2C_MasterWrite(
MXC_I2C1_BUS0, MAX86150_ADDRESS << 1, command, 1, 0
//We may need to read as many as 288 bytes so we read in blocks no larger than I2C_BUFFER_LENGTH );
//I2C_BUFFER_LENGTH changes based on the platform. 64 bytes for SAMD21, 32 bytes for Uno.
//Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno //We may need to read as many as 288 bytes so we read in blocks no larger than I2C_BUFFER_LENGTH
while (bytesLeftToRead > 0) //I2C_BUFFER_LENGTH changes based on the platform. 64 bytes for SAMD21, 32 bytes for Uno.
{ //Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno
int toGet = bytesLeftToRead; while (bytesLeftToRead > 0) {
if (toGet > I2C_BUFFER_LENGTH) int toGet = bytesLeftToRead;
{ if (toGet > I2C_BUFFER_LENGTH) {
//If toGet is 32 this is bad because we read 6 bytes (Red+IR * 3 = 6) at a time //If toGet is 32 this is bad because we read 9 bytes (Red+IR+ECG * 3 = 9) at a time
//32 % 6 = 2 left over. We don't want to request 32 bytes, we want to request 30. //32 % 9 = 5 left over. We don't want to request 32 bytes, we want to request 27.
//32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27. //32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27.
toGet = I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH % (activeDevices * 3)); //Trim toGet to be a multiple of the samples we need to read toGet = I2C_BUFFER_LENGTH -
} (I2C_BUFFER_LENGTH %
(activeDevices *
bytesLeftToRead -= toGet; 3)); //Trim toGet to be a multiple of the samples we need to read
}
//Request toGet number of bytes from sensor
//_i2cPort->requestFrom(_i2caddr, toGet); bytesLeftToRead -= toGet;
uint8_t data[toGet];
uint8_t *p = data; //Request toGet number of bytes from sensor
I2C_MasterRead(MXC_I2C1_BUS0, _i2caddr << 1, data, toGet, 0); //_i2cPort->requestFrom(MAX86150_ADDRESS, toGet);
uint8_t data[bytesLeftToRead];
while (toGet > 0) uint8_t *p = data;
{ I2C_MasterRead(
sense.head++; //Advance the head of the storage struct MXC_I2C1_BUS0,
sense.head %= STORAGE_SIZE; //Wrap condition MAX86150_ADDRESS << 1,
data,
byte temp[sizeof(uint32_t)]; //Array of 4 bytes that we will convert into long bytesLeftToRead,
uint32_t tempLong; 0
);
//Burst read three bytes - RED
temp[3] = 0; while (bytesLeftToRead > 0) {
temp[2] = *p++; sense.head++; //Advance the head of the storage struct
temp[1] = *p++; sense.head %= STORAGE_SIZE; //Wrap condition
temp[0] = *p++;
byte temp[sizeof(
//Convert array to long uint32_t)]; //Array of 4 bytes that we will convert into long
memcpy(&tempLong, temp, sizeof(tempLong)); uint32_t tempLong;
//Burst read three bytes - RED
temp[3] = 0;
temp[2] = *p++;
temp[1] = *p++;
temp[0] = *p++;
//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));
tempLong &= 0x7FFFF; //Zero out all but 18 bits tempLong &= 0x7FFFF; //Zero out all but 18 bits
sense.red[sense.head] = tempLong; //Store this reading into the sense array sense.red[sense.head] =
tempLong; //Store this reading into the sense array
if (activeDevices > 1) if (activeDevices > 1) {
{ //Burst read three more bytes - IR
//Burst read three more bytes - IR temp[3] = 0;
temp[3] = 0; temp[2] = *p++;
temp[2] = *p++; temp[1] = *p++;
temp[1] = *p++; temp[0] = *p++;
temp[0] = *p++;
//Convert array to long //Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong)); memcpy(&tempLong,
temp,
sizeof(tempLong));
//Serial.println(tempLong); //Serial.println(tempLong);
tempLong &= 0x7FFFF; //Zero out all but 18 bits tempLong &=
0x7FFFF; //Zero out all but 18 bits
sense.IR[sense.head] = tempLong; sense.IR[sense.head] = tempLong;
} }
if (activeDevices > 2) if (activeDevices > 2) {
{ //Burst read three more bytes - ECG
//Burst read three more bytes - ECG
int32_t tempLongSigned; int32_t tempLongSigned;
temp[3] = 0;
temp[3] = 0; temp[2] = *p++;
temp[2] = *p++; temp[1] = *p++;
temp[1] = *p++; temp[0] = *p++;
temp[0] = *p++;
//Serial.println(tempLong); //Serial.println(tempLong);
//Convert array to long //Convert array to long
memcpy(&tempLongSigned, temp, sizeof(tempLongSigned)); memcpy(&tempLongSigned,
temp,
//tempLong &= 0x3FFFF; //Zero out all but 18 bits sizeof(tempLongSigned));
//tempLong &= 0x3FFFF; //Zero out all but 18 bits
sense.ecg[sense.head] = tempLongSigned;
}
sense.ecg[sense.head] = tempLongSigned; bytesLeftToRead -= activeDevices * 3;
} }
} //End while (bytesLeftToRead > 0)
toGet -= activeDevices * 3; } //End readPtr != writePtr
} return (numberOfSamples); //Let the world know how much new data we found
} //End while (bytesLeftToRead > 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 //Check for new data but give up after a certain amount of time
//Returns true if new data was found //Returns true if new data was found
//Returns false if new data was not found //Returns false if new data was not found
bool max86150_safeCheck(uint8_t maxTimeToCheck) bool max86150_safe_check(uint8_t max_tries)
{ {
// TODO uint8_t tries = 0;
//uint32_t markTime = millis();
while(1)
{
//if(millis() - markTime > maxTimeToCheck) return(false);
if(max86150_check() == true) //We found new data! while (tries < max_tries) {
return(true); if (max86150_check() == true) { //We found new data!
return (true);
}
delay(1); tries++;
} delay(1);
}
return false;
} }
//Given a register, read it, mask it, and then set the thing //Given a register, read it, mask it, and then set the thing
void max86150_bitMask(uint8_t reg, uint8_t mask, uint8_t thing) void max86150_bit_mask(uint8_t reg, uint8_t mask, uint8_t thing)
{ {
// Grab current register context // Grab current register context
uint8_t originalContents = max86150_readRegister8(_i2caddr, reg); uint8_t originalContents =
max86150_read_register(MAX86150_ADDRESS, reg);
// Zero-out the portions of the register we're interested in // Zero-out the portions of the register we're interested in
originalContents = originalContents & mask; originalContents = originalContents & mask;
// Change contents // Change contents
max86150_writeRegister8(_i2caddr, reg, originalContents | thing); max86150_write_register(
MAX86150_ADDRESS, reg, originalContents | thing
);
} }
uint8_t max86150_readRegister8(uint8_t address, uint8_t reg) { uint8_t max86150_read_register(uint8_t address, uint8_t reg)
{
uint8_t tempData = 0; uint8_t tempData = 0;
uint8_t command[] = {reg}; uint8_t command[] = { reg };
I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 1, 0); // Important! true is for repeated start (since we are not reading complete fifo)
// See https://os.mbed.com/users/laserdad/code/MAX86150_ECG_PPG//file/3c728f3d1f10/main.cpp/
I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 1, true);
I2C_MasterRead(MXC_I2C1_BUS0, address << 1, &tempData, 1, 0); I2C_MasterRead(MXC_I2C1_BUS0, address << 1, &tempData, 1, 0);
return tempData; return tempData;
} }
void max86150_writeRegister8(uint8_t address, uint8_t reg, uint8_t value) { void max86150_write_register(uint8_t address, uint8_t reg, uint8_t value)
uint8_t command[] = {reg, value}; {
I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 2, 0); uint8_t command[] = { reg, value };
I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 2, 0);
} }
...@@ -18,86 +18,115 @@ ...@@ -18,86 +18,115 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
typedef uint8_t byte;
bool max86150_begin(void); static const uint8_t MAX86150_SAMPLEAVG_1 = 0b000;
static const uint8_t MAX86150_SAMPLEAVG_2 = 0b001;
uint32_t max86150_getRed(void); //Returns immediate red value static const uint8_t MAX86150_SAMPLEAVG_4 = 0b010;
uint32_t max86150_getIR(void); //Returns immediate IR value static const uint8_t MAX86150_SAMPLEAVG_8 = 0b011;
int32_t max86150_getECG(void); //Returns immediate ECG value static const uint8_t MAX86150_SAMPLEAVG_16 = 0b100;
bool max86150_safeCheck(uint8_t maxTimeToCheck); //Given a max amount of time, check for new data static const uint8_t MAX86150_SAMPLEAVG_32 = 0b101;
// Configuration static const uint8_t MAX86150_ADCRANGE_4096 = 0b00000000;
void max86150_softReset(); static const uint8_t MAX86150_ADCRANGE_8192 = 0b01000000;
void max86150_shutDown(); static const uint8_t MAX86150_ADCRANGE_16384 = 0b10000000;
void max86150_wakeUp(); static const uint8_t MAX86150_ADCRANGE_32768 = 0b11000000;
static const uint8_t MAX86150_PPG_SAMPLERATE_10 = 0b00000000;
static const uint8_t MAX86150_PPG_SAMPLERATE_20 = 0b00000100;
static const uint8_t MAX86150_PPG_SAMPLERATE_50 = 0b00001000;
static const uint8_t MAX86150_PPG_SAMPLERATE_84 = 0b00001100;
static const uint8_t MAX86150_PPG_SAMPLERATE_100 = 0b00010000;
static const uint8_t MAX86150_PPG_SAMPLERATE_200 = 0b00010100;
static const uint8_t MAX86150_PPG_SAMPLERATE_400 = 0b00011000;
static const uint8_t MAX86150_PPG_SAMPLERATE_800 = 0b00011100;
static const uint8_t MAX86150_PPG_SAMPLERATE_1000 = 0b00100000;
static const uint8_t MAX86150_PPG_SAMPLERATE_1600 = 0b00100100;
static const uint8_t MAX86150_PPG_SAMPLERATE_3200 = 0b00101000;
static const uint8_t MAX86150_PPG_PULSEWIDTH_50 = 0b00;
static const uint8_t MAX86150_PPG_PULSEWIDTH_100 = 0b01;
static const uint8_t MAX86150_PPG_PULSEWIDTH_200 = 0b10;
static const uint8_t MAX86150_PPG_PULSEWIDTH_400 = 0b11;
static const uint8_t MAX86150_SLOT_NONE = 0b0000;
static const uint8_t MAX86150_SLOT_RED_LED = 0b0010;
static const uint8_t MAX86150_SLOT_IR_LED = 0b0001;
static const uint8_t MAX86150_SLOT_RED_PILOT = 0b0110;
static const uint8_t MAX86150_SLOT_IR_PILOT = 0b0101;
static const uint8_t MAX86150_SLOT_ECG = 0b1001;
static const uint8_t MAX86150_LED1_RANGE_50 = 0b00;
static const uint8_t MAX86150_LED1_RANGE_100 = 0b01;
static const uint8_t MAX86150_LED2_RANGE_50 = 0b0000;
static const uint8_t MAX86150_LED2_RANGE_100 = 0b0100;
static const uint8_t MAX86150_ECG_SAMPLERATE_200 = 0b011;
static const uint8_t MAX86150_ECG_SAMPLERATE_400 = 0b010;
static const uint8_t MAX86150_ECG_SAMPLERATE_800 = 0b001;
static const uint8_t MAX86150_ECG_SAMPLERATE_1600 = 0b000;
static const uint8_t MAX86150_ECG_SAMPLERATE_3200 = 0b100;
static const uint8_t MAX86150_ECG_PGA_GAIN_1 = 0b0000;
static const uint8_t MAX86150_ECG_PGA_GAIN_2 = 0b0100;
static const uint8_t MAX86150_ECG_PGA_GAIN_4 = 0b1000;
static const uint8_t MAX86150_ECG_PGA_GAIN_8 = 0b1100;
static const uint8_t MAX86150_ECG_IA_GAIN_5 = 0b00;
static const uint8_t MAX86150_ECG_IA_GAIN_9_5 = 0b01;
static const uint8_t MAX86150_ECG_IA_GAIN_20 = 0b10;
static const uint8_t MAX86150_ECG_IA_GAIN_50 = 0b11;
void max86150_setLEDMode(uint8_t mode);
void max86150_setADCRange(uint8_t adcRange);
void max86150_setSampleRate(uint8_t sampleRate);
void max86150_setPulseWidth(uint8_t pulseWidth);
void max86150_setPulseAmplitudeRed(uint8_t value); bool max86150_begin(void);
void max86150_setPulseAmplitudeIR(uint8_t value);
void max86150_setPulseAmplitudeProximity(uint8_t value);
void max86150_setProximityThreshold(uint8_t threshMSB);
//Multi-led configuration mode (page 22) uint8_t max86150_get_int1(void);
void max86150_enableSlot(uint8_t slotNumber, uint8_t device); //Given slot number, assign a device to slot uint8_t max86150_get_int2(void);
void max86150_set_int_full(bool enabled);
void max86150_set_int_datardy(bool enabled);
void max86150_set_int_ambient_light_overflow(bool enabled);
void max86150_set_int_proximity(bool enabled);
void max86150_soft_reset(void);
void max86150_shut_down(void);
void max86150_wake_up(void);
void max86150_set_ppg_adc_range(uint8_t adcRange);
void max86150_set_ppg_sample_rate(uint8_t sampleRate);
void max86150_set_ppg_pulse_width(uint8_t pulseWidth);
void max86150_set_led_red_amplitude(uint8_t amplitude);
void max86150_set_led_ir_amplitude(uint8_t amplitude);
void max86150_set_led_proximity_amplitude(uint8_t amplitude);
void max86150_set_proximity_threshold(uint8_t threshMSB);
void max86150_fifo_enable_slot(uint8_t slotNumber, uint8_t device);
void max86150_disableSlots(void); void max86150_disableSlots(void);
void max86150_set_ppg_averaging(uint8_t numberOfSamples);
// Data Collection void max86150_clear_fifo(void);
void max86150_set_fifo_rollover(bool enabled);
//Interrupts (page 13, 14) void max86150_set_fifo_almost_full_clear(bool enabled);
uint8_t max86150_getINT1(void); //Returns the main interrupt group void max86150_set_fifo_almost_full_repeat(bool enabled);
uint8_t max86150_getINT2(void); //Returns the temp ready interrupt void max86150_set_fifo_almost_full(uint8_t numberOfSamples);
void max86150_enableAFULL(void); //Enable/disable individual interrupts uint8_t max86150_get_fifo_write_pointer(void);
void max86150_disableAFULL(void); uint8_t max86150_get_fifo_read_pointer(void);
void max86150_enableDATARDY(void); uint8_t max86150_read_part_id();
void max86150_disableDATARDY(void); void max86150_set_ecg_sample_rate(uint8_t sampleRate);
void max86150_enableALCOVF(void); void max86150_set_ecg_pga_gain(uint8_t gain);
void max86150_disableALCOVF(void); void max86150_set_ecg_instrumentation_amplifier_gain(uint8_t gain);
void max86150_enablePROXINT(void);
void max86150_disablePROXINT(void); void max86150_setup(const uint8_t ppg_sample_rate);
void max86150_enableDIETEMPRDY(void);
void max86150_disableDIETEMPRDY(void); uint8_t max86150_available(void);
uint32_t max86150_get_red(void);
//FIFO Configuration (page 18) uint32_t max86150_get_ir(void);
void max86150_setFIFOAverage(uint8_t samples); int32_t max86150_get_ecg(void);
void max86150_enableFIFORollover(); uint32_t max86150_get_fifo_red(void);
void max86150_disableFIFORollover(); uint32_t max86150_get_fifo_ir(void);
void max86150_setFIFOAlmostFull(uint8_t samples); int32_t max86150_get_fifo_ecg(void);
void max86150_next_sample(void);
//FIFO Reading uint8_t max86150_get_sample(uint32_t *red, uint32_t *ir, int32_t *ecg);
uint16_t max86150_check(void); //Checks for new data and fills FIFO uint16_t max86150_check(void);
uint8_t max86150_available(void); //Tells caller how many new samples are available (head - tail) bool max86150_safe_check(uint8_t max_tries);
void max86150_nextSample(void); //Advances the tail of the sense array
uint32_t max86150_getFIFORed(void); //Returns the FIFO sample pointed to by tail void max86150_bit_mask(uint8_t reg, uint8_t mask, uint8_t thing);
uint32_t max86150_getFIFOIR(void); //Returns the FIFO sample pointed to by tail uint8_t max86150_read_register(uint8_t address, uint8_t reg);
int32_t max86150_getFIFOECG(void); //Returns the FIFO sample pointed to by tail void max86150_write_register(uint8_t address, uint8_t reg, uint8_t value);
uint8_t max86150_getWritePointer(void);
uint8_t max86150_getReadPointer(void);
void max86150_clearFIFO(void); //Sets the read/write pointers to zero
//Proximity Mode Interrupt Threshold
void max86150_setPROXINTTHRESH(uint8_t val);
// Die Temperature
float max86150_readTemperature();
float max86150_readTemperatureF();
// Detecting ID/Revision
uint8_t max86150_getRevisionID();
uint8_t max86150_readPartID();
uint8_t max86150_readRegLED();
// Setup the IC with user selectable settings
//void max86150_setup(byte powerLevel = 0x1F, byte sampleAverage = 4, byte ledMode = 3, int sampleRate = 400, int pulseWidth = 411, int adcRange = 4096);
void max86150_setup(byte powerLevel, byte sampleAverage, byte ledMode, int sampleRate, int pulseWidth, int adcRange);
// Low-level I2C communication
uint8_t max86150_readRegister8(uint8_t address, uint8_t reg);
void max86150_writeRegister8(uint8_t address, uint8_t reg, uint8_t value);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment