diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 01709785540c27f72ca8a85bf08412978470029b..84071d042c85643dfc181549f038e46c9c4b354a 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -133,9 +133,8 @@ typedef _Bool bool; #define API_MAX30001_ENABLE 0xf0 #define API_MAX30001_DISABLE 0xf1 -#define API_MAX86150_INIT 0x0100 -#define API_MAX86150_GET_DATA 0x0101 -#define API_MAX86150_SET_LED_AMPLITUDE 0x0102 +#define API_MAX86150_ENABLE 0x0100 +#define API_MAX86150_DISABLE 0x0101 #define API_USB_SHUTDOWN 0x110 #define API_USB_STORAGE 0x111 @@ -194,18 +193,20 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id)); /** RTC Alarm interrupt. See :c:func:`epic_isr_rtc_alarm`. */ #define EPIC_INT_RTC_ALARM 3 /** BHI160 Accelerometer. See :c:func:`epic_isr_bhi160_accelerometer`. */ -#define EPIC_INT_BHI160_ACCELEROMETER 4 +#define EPIC_INT_BHI160_ACCELEROMETER 4 /** BHI160 Orientation Sensor. See :c:func:`epic_isr_bhi160_orientation`. */ -#define EPIC_INT_BHI160_ORIENTATION 5 +#define EPIC_INT_BHI160_ORIENTATION 5 /** BHI160 Gyroscope. See :c:func:`epic_isr_bhi160_gyroscope`. */ -#define EPIC_INT_BHI160_GYROSCOPE 6 +#define EPIC_INT_BHI160_GYROSCOPE 6 /** MAX30001 ECG. See :c:func:`epic_isr_max30001_ecg`. */ -#define EPIC_INT_MAX30001_ECG 7 +#define EPIC_INT_MAX30001_ECG 7 /** BHI160 Magnetometer. See :c:func:`epic_isr_bhi160_magnetometer`. */ #define EPIC_INT_BHI160_MAGNETOMETER 8 +/** MAX86150 ECG and PPG sensor. See :c:func:`epic_isr_max86150`. */ +#define EPIC_INT_MAX86150 9 /* Number of defined interrupts. */ -#define EPIC_INT_NUM 9 +#define EPIC_INT_NUM 10 /* clang-format on */ /* @@ -871,6 +872,84 @@ API(API_BME680_GET_DATA, int epic_bme680_read_sensors( struct bme680_sensor_data *data )); +/** + * MAX86150 + * ====== + */ + +/** + * Configuration for a MAX86150 sensor. + * + * This struct is used when enabling a sensor using + * :c:func:`epic_max86150_enable_sensor`. + */ +struct max86150_sensor_config { + /** + * Number of samples Epicardium should keep for this sensor. Do not set + * this number too high as the sample buffer will eat RAM. + */ + size_t sample_buffer_len; + /** + * Sample rate for PPG from the sensor in Hz. Maximum data rate is limited + * to 200 Hz for all sensors though some might be limited at a lower + * rate. + * + * Possible values are 10, 20, 50, 84, 100, 200. + */ + uint16_t ppg_sample_rate; +}; + +/** + * MAX86150 Sensor Data + */ +struct max86150_sensor_data { + /** Red LED data */ + uint32_t red; + /** IR LED data */ + uint32_t ir; + /** ECG data */ + int32_t ecg; +}; + +/** + * Enable a MAX86150 PPG and ECG sensor. + * + * Calling this function will instruct the MAX86150 to collect a + * data from the sensor. You can then retrieve the samples using + * :c:func:`epic_stream_read`. + * + * :param max86150_sensor_config* config: Configuration for this sensor. + * :param size_t config_size: Size of ``config``. + * :returns: A sensor descriptor which can be used with + * :c:func:`epic_stream_read` or a negative error value: + * + * - ``-ENOMEM``: The MAX86150 driver failed to create a stream queue. + * - ``-ENODEV``: The MAX86150 driver failed due to physical connectivity problem + * (broken wire, unpowered, etc). + * - ``-EINVAL``: config->ppg_sample_rate is not one of 10, 20, 50, 84, 100, 200 + * or config_size is not size of config. + * + * .. versionadded:: 1.13 + */ +API(API_MAX86150_ENABLE, int epic_max86150_enable_sensor(struct max86150_sensor_config *config, size_t config_size)); + +/** + * Disable the MAX86150 sensor. + * + * :returns: 0 in case of success or forward negative error value from stream_deregister. + * + * .. versionadded:: 1.13 + */ +API(API_MAX86150_DISABLE, int epic_max86150_disable_sensor()); + +/** + * **Interrupt Service Routine** for :c:data:`EPIC_INT_MAX86150` + * + * :c:func:`epic_isr_max86150` is called whenever the MAX86150 + * PPG sensor has new data available. + */ +API_ISR(EPIC_INT_MAX86150, epic_isr_max86150); + /** * Personal State * ============== @@ -1139,7 +1218,7 @@ struct bhi160_sensor_config { }; /** - * Enable a BHI160 virtual sensor. Calling this funciton will instruct the + * Enable a BHI160 virtual sensor. Calling this function will instruct the * BHI160 to collect data for this specific virtual sensor. You can then * retrieve the samples using :c:func:`epic_stream_read`. * @@ -1854,9 +1933,9 @@ struct max30001_sensor_config { }; /** - * Enable a MAX30001 ecg sensor. + * Enable a MAX30001 ECG sensor. * - * Calling this funciton will instruct the MAX30001 to collect data for this + * Calling this function will instruct the MAX30001 to collect data for this * sensor. You can then retrieve the samples using :c:func:`epic_stream_read`. * * :param max30001_sensor_config* config: Configuration for this sensor. diff --git a/epicardium/main.c b/epicardium/main.c index 831a3e12f9fe3c3dbf391663612e6368487bbfb5..c5e2768c37177f97f6a2388bdfdd8b71f10cd591 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -113,6 +113,16 @@ int main(void) NULL) != pdPASS) { panic("Failed to create %s task!", "MAX30001"); } + /* MAX86150 */ + if (xTaskCreate( + vMAX86150Task, + (const char *)"MAX86150 Driver", + configMINIMAL_STACK_SIZE * 2, + NULL, + tskIDLE_PRIORITY + 1, + NULL) != pdPASS) { + panic("Failed to create %s task!", "MAX86150"); + } /* API */ if (xTaskCreate( vApiDispatcher, diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index afc44f64b7e241b0603b8239fd0aa5b8cdd0c2ff..358154334f1b7f3a2d508e7348e70999aa67257c 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -194,6 +194,11 @@ int hardware_early_init(void) */ max30001_mutex_init(); + /* + * max86150 mutex init + */ + max86150_mutex_init(); + /* Allow user space to trigger interrupts. * Used for BLE, not sure if needed. */ SCB->CCR |= SCB_CCR_USERSETMPEND_Msk; @@ -283,5 +288,7 @@ int hardware_reset(void) epic_max30001_disable_sensor(); + epic_max86150_disable_sensor(); + return 0; } diff --git a/epicardium/modules/max86150.c b/epicardium/modules/max86150.c new file mode 100644 index 0000000000000000000000000000000000000000..428d0caf22f8f78511e1e7a0792602af092605e1 --- /dev/null +++ b/epicardium/modules/max86150.c @@ -0,0 +1,236 @@ +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include "max86150.h" +#include "epicardium.h" +#include "modules.h" +#include "modules/log.h" +#include "modules/stream.h" +#include "gpio.h" +#include "pmic.h" + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "api/interrupt-sender.h" +#include "modules/modules.h" + +static const gpio_cfg_t max86150_interrupt_pin = { + PORT_1, PIN_13, GPIO_FUNC_IN, GPIO_PAD_PULL_UP +}; + +/* MAX86150 Task ID */ +static TaskHandle_t max86150_task_id = NULL; + +/* MAX86150 Mutex */ +static struct mutex max86150_mutex = { 0 }; + +/* Stream */ +static struct stream_info max86150_stream; + +/* Active */ +static bool max86150_sensor_active = false; + +int epic_max86150_enable_sensor( + struct max86150_sensor_config *config, size_t config_size +) { + int result = 0; + + if (sizeof(struct max86150_sensor_config) != config_size) { + return -EINVAL; + } + + mutex_lock(&max86150_mutex); + hwlock_acquire(HWLOCK_I2C); + + struct stream_info *stream = &max86150_stream; + stream->item_size = sizeof(struct max86150_sensor_data); + stream->queue = + xQueueCreate(config->sample_buffer_len, stream->item_size); + if (stream->queue == NULL) { + result = -ENOMEM; + goto out_free; + } + + uint8_t ppg_sample_rate; + + if (config->ppg_sample_rate == 10) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_10; + } else if (config->ppg_sample_rate == 20) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_20; + } else if (config->ppg_sample_rate == 50) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_50; + } else if (config->ppg_sample_rate == 84) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_84; + } else if (config->ppg_sample_rate == 100) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_100; + } else if (config->ppg_sample_rate == 200) { + ppg_sample_rate = MAX86150_PPG_SAMPLERATE_200; + } else { + result = -EINVAL; + goto out_free; + } + + result = stream_register(SD_MAX86150, stream); + if (result < 0) { + vQueueDelete(stream->queue); + goto out_free; + } + + bool begin_result = max86150_begin(); + if (!begin_result) { + result = -ENODEV; + vQueueDelete(stream->queue); + goto out_free; + } + + max86150_setup(ppg_sample_rate); + max86150_get_int1(); + max86150_get_int2(); + + max86150_sensor_active = true; + result = SD_MAX86150; + +out_free: + hwlock_release(HWLOCK_I2C); + mutex_unlock(&max86150_mutex); + return result; +} + +int epic_max86150_disable_sensor(void) +{ + int result = 0; + + mutex_lock(&max86150_mutex); + hwlock_acquire(HWLOCK_I2C); + + struct stream_info *stream = &max86150_stream; + result = stream_deregister(SD_MAX86150, stream); + if (result < 0) { + goto out_free; + } + + vQueueDelete(stream->queue); + stream->queue = NULL; + + // disable max86150 leds + max86150_set_led_red_amplitude(0); + max86150_set_led_ir_amplitude(0); + + max86150_sensor_active = false; + + result = 0; +out_free: + hwlock_release(HWLOCK_I2C); + mutex_unlock(&max86150_mutex); + return result; +} + +static int max86150_handle_sample(struct max86150_sensor_data *data) +{ + //LOG_INFO("max86150", "Sample! %ld, %ld, %ld", data->red, data->ir, data->ecg); + + if (max86150_stream.queue == NULL) { + return -ESRCH; + } + + /* Discard overflow. See discussion in !316. */ + if (xQueueSend(max86150_stream.queue, data, 0) != pdTRUE) { + LOG_WARN("max86150", "queue full"); + return -EIO; + } + return api_interrupt_trigger(EPIC_INT_MAX86150); +} + +static int max86150_fetch_fifo(void) +{ + int result = 0; + + mutex_lock(&max86150_mutex); + hwlock_acquire(HWLOCK_I2C); + + struct max86150_sensor_data sample; + + // There is 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/ + // So we should not use max86150_check() but max86150_get_sample(). + while (max86150_get_sample(&sample.red, &sample.ir, &sample.ecg) > 0) { + result = max86150_handle_sample(&sample); + // stop in case of errors + if (result < 0) { + break; + } + } + hwlock_release(HWLOCK_I2C); + mutex_unlock(&max86150_mutex); + return result; +} + +/* + * Callback for the MAX86150 interrupt pin. This callback is called from the + * SDK's GPIO interrupt driver, in interrupt context. + */ +static void max86150_interrupt_callback(void *_) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if (max86150_task_id != NULL) { + vTaskNotifyGiveFromISR( + max86150_task_id, &xHigherPriorityTaskWoken + ); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} +/* }}} */ + +void max86150_mutex_init(void) +{ + mutex_create(&max86150_mutex); +} + +void vMAX86150Task(void *pvParameters) +{ + max86150_task_id = xTaskGetCurrentTaskHandle(); + + mutex_lock(&max86150_mutex); + hwlock_acquire(HWLOCK_I2C); + + /* Install interrupt callback */ + GPIO_Config(&max86150_interrupt_pin); + GPIO_RegisterCallback( + &max86150_interrupt_pin, max86150_interrupt_callback, NULL + ); + GPIO_IntConfig( + &max86150_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_FALLING + ); + GPIO_IntEnable(&max86150_interrupt_pin); + NVIC_SetPriority( + (IRQn_Type)MXC_GPIO_GET_IRQ(max86150_interrupt_pin.port), 2 + ); + NVIC_EnableIRQ( + (IRQn_Type)MXC_GPIO_GET_IRQ(max86150_interrupt_pin.port) + ); + + hwlock_release(HWLOCK_I2C); + mutex_unlock(&max86150_mutex); + + /* ----------------------------------------- */ + + while (1) { + if (max86150_sensor_active) { + //LOG_INFO("max86150", "Interrupt!"); + int ret = max86150_fetch_fifo(); + if (ret < 0) { + LOG_ERR("max86150", "Unknown error: %d", -ret); + } + } + /* + * Wait for interrupt. After two seconds, fetch FIFO anyway + * + * In the future, reads using epic_stream_read() might also + * trigger a FIFO fetch, from outside this task. + */ + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)); + } +} diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 8ead5e72c4cc80e3fe0601d713784028cc16645f..474b32930f9b4970fdde4cf65c6891f5eead8a1f 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -13,6 +13,7 @@ module_sources = files( 'lifecycle.c', 'light_sensor.c', 'log.c', + 'max86150.c', 'max30001.c', 'mutex.c', 'panic.c', diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 567a2a82bb98341451c5ea3c5b847a05e63b1951..0444b688f33cb879e3cca9f59cb1eb365999eb17 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -111,6 +111,12 @@ void disp_forcelock(); #define BHI160_MUTEX_WAIT_MS 50 void vBhi160Task(void *pvParameters); +/* ---------- MAX86150 ----------------------------------------------------- */ +#define MAX86150_MUTEX_WAIT_MS 50 +void vMAX86150Task(void *pvParameters); +void max86150_mutex_init(void); + + /* ---------- MAX30001 ----------------------------------------------------- */ void vMAX30001Task(void *pvParameters); void max30001_mutex_init(void); diff --git a/epicardium/modules/sleep.c b/epicardium/modules/sleep.c index 4b94fa7977ac5533247cd1b5ce60009f8ded42af..c7c060e14a2094d68945710a97c3b2a4983a7c31 100644 --- a/epicardium/modules/sleep.c +++ b/epicardium/modules/sleep.c @@ -176,9 +176,9 @@ void sleep_deepsleep(void) /* This will fail if there is no * harmonic board attached */ max86150_begin(); - max86150_getINT1(); - max86150_getINT2(); - max86150_shutDown(); + max86150_get_int1(); + max86150_get_int2(); + max86150_shut_down(); #endif epic_bhi160_disable_all_sensors(); epic_bme680_deinit(); diff --git a/epicardium/modules/stream.h b/epicardium/modules/stream.h index f6f939708d4aaa91d3ca9fc02f03e21bf4e69339..a953537aea4d1bb6a3aed755d0ec236098366781 100644 --- a/epicardium/modules/stream.h +++ b/epicardium/modules/stream.h @@ -36,6 +36,7 @@ enum stream_descriptor { SD_BHI160_GYROSCOPE, /** MAX30001 ECG */ SD_MAX30001_ECG, + SD_MAX86150, /** Highest descriptor must always be ``SD_MAX``. */ SD_MAX, }; diff --git a/lib/vendor/Maxim/MAX86150/max86150.c b/lib/vendor/Maxim/MAX86150/max86150.c index ab14bb1086a56f4af34ae046dbcc07f602f20f18..203df693a4f8cbbbc06d992afee7803ec0693197 100644 --- a/lib/vendor/Maxim/MAX86150/max86150.c +++ b/lib/vendor/Maxim/MAX86150/max86150.c @@ -18,151 +18,132 @@ typedef uint8_t byte; -static const uint8_t MAX86150_INTSTAT1 = 0x00; -static const uint8_t MAX86150_INTSTAT2 = 0x01; -static const uint8_t MAX86150_INTENABLE1 = 0x02; -static const uint8_t MAX86150_INTENABLE2 = 0x03; +static const uint8_t MAX86150_INTSTAT1 = 0x00; +static const uint8_t MAX86150_INTSTAT2 = 0x01; +static const uint8_t MAX86150_INTENABLE1 = 0x02; +static const uint8_t MAX86150_INTENABLE2 = 0x03; -static const uint8_t MAX86150_FIFOWRITEPTR = 0x04; -static const uint8_t MAX86150_FIFOOVERFLOW = 0x05; -static const uint8_t MAX86150_FIFOREADPTR = 0x06; -static const uint8_t MAX86150_FIFODATA = 0x07; +static const uint8_t MAX86150_FIFOWRITEPTR = 0x04; +static const uint8_t MAX86150_FIFOOVERFLOW = 0x05; +static const uint8_t MAX86150_FIFOREADPTR = 0x06; +static const uint8_t MAX86150_FIFODATA = 0x07; -static const uint8_t MAX86150_FIFOCONFIG = 0x08; -static const uint8_t MAX86150_FIFOCONTROL1= 0x09; -static const uint8_t MAX86150_FIFOCONTROL2 = 0x0A; +static const uint8_t MAX86150_FIFOCONFIG = 0x08; +static const uint8_t MAX86150_FIFOCONTROL1 = 0x09; +static const uint8_t MAX86150_FIFOCONTROL2 = 0x0A; -static const uint8_t MAX86150_SYSCONTROL = 0x0D; -static const uint8_t MAX86150_PPGCONFIG1 = 0x0E; -static const uint8_t MAX86150_PPGCONFIG2 = 0x0F; -static const uint8_t MAX86150_LED_PROX_AMP = 0x10; +static const uint8_t MAX86150_SYSCONTROL = 0x0D; +static const uint8_t MAX86150_PPGCONFIG1 = 0x0E; +static const uint8_t MAX86150_PPGCONFIG2 = 0x0F; +static const uint8_t MAX86150_LED_PROX_AMP = 0x10; -static const uint8_t MAX86150_LED1_PULSEAMP = 0x11; -static const uint8_t MAX86150_LED2_PULSEAMP = 0x12; -static const uint8_t MAX86150_LED_RANGE = 0x14; -static const uint8_t MAX86150_LED_PILOT_PA = 0x15; +static const uint8_t MAX86150_LED1_PULSEAMP = 0x11; +static const uint8_t MAX86150_LED2_PULSEAMP = 0x12; +static const uint8_t MAX86150_LED_RANGE = 0x14; +static const uint8_t MAX86150_LED_PILOT_PA = 0x15; -static const uint8_t MAX86150_ECG_CONFIG1 = 0x3C; -static const uint8_t MAX86150_ECG_CONFIG3 = 0x3E; -static const uint8_t MAX86150_PROXINTTHRESH = 0x10; +static const uint8_t MAX86150_ECG_CONFIG1 = 0x3C; +static const uint8_t MAX86150_ECG_CONFIG3 = 0x3E; +static const uint8_t MAX86150_PROXINTTHRESH = 0x10; -static const uint8_t MAX86150_PARTID = 0xFF; +static const uint8_t MAX86150_PARTID = 0xFF; // MAX86150 Commands -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_DISABLE = 0x00; +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_DISABLE = 0x00; -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_MASK = (byte)~0b01000000; +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_ALC_OVF_MASK = (byte)~0b00100000; -static const uint8_t MAX86150_INT_ALC_OVF_ENABLE = 0x20; +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_DISABLE = 0x00; -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_MASK = (byte)~0b00010000; +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_SAMPLEAVG_MASK = (byte)~0b11100000; -static const uint8_t MAX86150_SAMPLEAVG_1 = 0x00; -static const uint8_t MAX86150_SAMPLEAVG_2 = 0x20; -static const uint8_t MAX86150_SAMPLEAVG_4 = 0x40; -static const uint8_t MAX86150_SAMPLEAVG_8 = 0x60; -static const uint8_t MAX86150_SAMPLEAVG_16 = 0x80; -static const uint8_t MAX86150_SAMPLEAVG_32 = 0xA0; - -static const uint8_t MAX86150_ROLLOVER_MASK = 0xEF; -static const uint8_t MAX86150_ROLLOVER_ENABLE = 0x10; -static const uint8_t MAX86150_ROLLOVER_DISABLE = 0x00; - -static const uint8_t MAX86150_A_FULL_MASK = 0xF0; - -static const uint8_t MAX86150_SHUTDOWN_MASK = 0x7F; -static const uint8_t MAX86150_SHUTDOWN = 0x80; -static const uint8_t MAX86150_WAKEUP = 0x00; - -static const uint8_t MAX86150_RESET_MASK = 0xFE; -static const uint8_t MAX86150_RESET = 0x01; - -static const uint8_t MAX86150_MODE_MASK = 0xF8; -static const uint8_t MAX86150_MODE_REDONLY = 0x02; -static const uint8_t MAX86150_MODE_REDIRONLY = 0x03; -static const uint8_t MAX86150_MODE_MULTILED = 0x07; - -static const uint8_t MAX86150_ADCRANGE_MASK = 0x9F; -static const uint8_t MAX86150_ADCRANGE_2048 = 0x00; -static const uint8_t MAX86150_ADCRANGE_4096 = 0x20; -static const uint8_t MAX86150_ADCRANGE_8192 = 0x40; -static const uint8_t MAX86150_ADCRANGE_16384 = 0x60; - -static const uint8_t MAX86150_SAMPLERATE_MASK = 0xE3; -static const uint8_t MAX86150_SAMPLERATE_50 = 0x00; -static const uint8_t MAX86150_SAMPLERATE_100 = 0x04; -static const uint8_t MAX86150_SAMPLERATE_200 = 0x08; -static const uint8_t MAX86150_SAMPLERATE_400 = 0x0C; -static const uint8_t MAX86150_SAMPLERATE_800 = 0x10; -static const uint8_t MAX86150_SAMPLERATE_1000 = 0x14; -static const uint8_t MAX86150_SAMPLERATE_1600 = 0x18; -static const uint8_t MAX86150_SAMPLERATE_3200 = 0x1C; - -static const uint8_t MAX86150_PULSEWIDTH_MASK = 0xFC; -static const uint8_t MAX86150_PULSEWIDTH_69 = 0x00; -static const uint8_t MAX86150_PULSEWIDTH_118 = 0x01; -static const uint8_t MAX86150_PULSEWIDTH_215 = 0x02; -static const uint8_t MAX86150_PULSEWIDTH_411 = 0x03; - -static const uint8_t MAX86150_SLOT1_MASK = 0xF0; -static const uint8_t MAX86150_SLOT2_MASK = 0x0F; -static const uint8_t MAX86150_SLOT3_MASK = 0xF0; -static const uint8_t MAX86150_SLOT4_MASK = 0x0F; - -static const uint8_t SLOT_NONE = 0x00; -static const uint8_t SLOT_RED_LED = 0x01; -static const uint8_t SLOT_IR_LED = 0x02; -static const uint8_t SLOT_RED_PILOT = 0x09; -static const uint8_t SLOT_IR_PILOT = 0x0A; -static const uint8_t SLOT_ECG = 0x0D; - -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; +static const uint8_t MAX86150_SAMPLEAVG_MASK = (byte)~0b00000111; + +static const uint8_t MAX86150_ROLLOVER_MASK = (byte)~0b00010000; +static const uint8_t MAX86150_ROLLOVER_ENABLE = 0b00010000; +static const uint8_t MAX86150_ROLLOVER_DISABLE = 0b00000000; + +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_ALMOST_FULL_CLEAR_DISABLE = 0b00000000; + +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_ALMOST_FULL_REPEAT_DISABLE = 0b00000000; + +static const uint8_t MAX86150_A_FULL_MASK = (byte)~0b00001111; + +static const uint8_t MAX86150_SHUTDOWN_MASK = (byte)~0b00000010; +static const uint8_t MAX86150_SHUTDOWN = 0b10; +static const uint8_t MAX86150_WAKEUP = 0b00; + +static const uint8_t MAX86150_FIFO_ENABLE_MASK = (byte)~0b00000100; +static const uint8_t MAX86150_FIFO_ENABLE = 0b100; +static const uint8_t MAX86150_FIFO_DISABLE = 0b000; + +static const uint8_t MAX86150_RESET_MASK = (byte)~0b00000001; +static const uint8_t MAX86150_RESET = 0b1; + +static const uint8_t MAX86150_ADCRANGE_MASK = (byte)~0b11000000; + +static const uint8_t MAX86150_PPG_SAMPLERATE_MASK = (byte)~0b00111100; + +static const uint8_t MAX86150_PPG_PULSEWIDTH_MASK = (byte)~0b00000011; + +static const uint8_t MAX86150_SLOT1_MASK = 0xF0; +static const uint8_t MAX86150_SLOT2_MASK = 0x0F; +static const uint8_t MAX86150_SLOT3_MASK = 0xF0; +static const uint8_t MAX86150_SLOT4_MASK = 0x0F; + +static const uint8_t MAX86150_LED1_RANGE_MASK = (byte)~0b00000011; +static const uint8_t MAX86150_LED2_RANGE_MASK = (byte)~0b00001100; + +static const uint8_t MAX86150_ECG_SAMPLERATE_MASK = (byte)~0b00000111; + +static const uint8_t MAX86150_ECG_PGA_GAIN_MASK = (byte)~0b00001100; + +static const uint8_t MAX86150_ECG_IA_GAIN_MASK = (byte)~0b00000011; + +static const uint8_t MAX86150_EXPECTEDPARTID = 0x1E; + +static byte activeDevices = + 3; //Gets set during max86150_setup. Allows max86150_check() to calculate how many bytes to read from FIFO + +#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 static sense_struct sense; static void delay(int ms) { - TMR_Delay(MXC_TMR0, MSEC(ms), 0); + TMR_Delay(MXC_TMR0, MSEC(ms), 0); } bool max86150_begin(void) { - _i2caddr = MAX86150_ADDRESS; - - // Step 1: Initial Communication and Verification - // Check that a MAX86150 is connected - if (max86150_readPartID() != MAX_30105_EXPECTEDPARTID) { - // Error -- Part ID read from MAX86150 does not match expected part ID. - // This may mean there is a physical connectivity problem (broken wire, unpowered, etc). - return false; - } - return true; + // Step 1: Initial Communication and Verification + // Check that a MAX86150 is connected + if (max86150_read_part_id() != MAX86150_EXPECTEDPARTID) { + // Error -- Part ID read from MAX86150 does not match expected part ID. + // This may mean there is a physical connectivity problem (broken wire, unpowered, etc). + return false; + } + return true; } // @@ -170,498 +151,704 @@ bool max86150_begin(void) // //Begin Interrupt configuration -uint8_t max86150_getINT1(void) +uint8_t max86150_get_int1(void) { - return (max86150_readRegister8(_i2caddr, 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); + return (max86150_read_register(MAX86150_ADDRESS, MAX86150_INTSTAT1)); } -void max86150_disableAFULL(void) { - max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_A_FULL_MASK, MAX86150_INT_A_FULL_DISABLE); -} - -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); +uint8_t max86150_get_int2(void) +{ + return (max86150_read_register(MAX86150_ADDRESS, MAX86150_INTSTAT2)); } -void max86150_enablePROXINT(void) { - max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_PROX_INT_MASK, MAX86150_INT_PROX_INT_ENABLE); -} -void max86150_disablePROXINT(void) { - max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_PROX_INT_MASK, MAX86150_INT_PROX_INT_DISABLE); +void max86150_set_int_full(bool enabled) +{ + if (enabled) { + max86150_bit_mask( + 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 -void max86150_softReset(void) { - max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_RESET_MASK, MAX86150_RESET); - - // Poll for bit to clear, reset is then complete - // Timeout after 100ms - //TODO - //unsigned long startTime = millis(); - //while (millis() - startTime < 100) - { - //uint8_t response = max86150_readRegister8(_i2caddr, MAX86150_SYSCONTROL); - //if ((response & MAX86150_RESET) == 0) break; //We're done! - delay(1); //Let's not over burden the I2C bus - } -} - -void max86150_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) - max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN); -} - -void max86150_wakeUp(void) { - // Pull IC out of low power mode (datasheet pg. 19) - max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP); +void max86150_soft_reset(void) +{ + max86150_bit_mask( + MAX86150_SYSCONTROL, MAX86150_RESET_MASK, MAX86150_RESET + ); + + // Poll for bit to clear, reset is then complete + // Timeout after 100 tries + uint8_t tries = 0; + while (tries < 100) { + uint8_t response = max86150_read_register( + MAX86150_ADDRESS, MAX86150_SYSCONTROL + ); + if ((response & MAX86150_RESET) == 0) + break; //We're done! + tries++; + delay(1); //Let's not over burden the I2C bus + } +} + +void max86150_shut_down(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) + max86150_bit_mask( + MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN + ); } -void max86150_setLEDMode(uint8_t mode) { - // Set which LEDs are used for sampling -- Red only, RED+IR only, or custom. - // See datasheet, page 19 - //max86150_bitMask(MAX86150_PPGCONFIG1, MAX86150_MODE_MASK, mode); +void max86150_wake_up(void) +{ + // Pull IC out of low power mode (datasheet pg. 19) + max86150_bit_mask( + MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP + ); } -void max86150_setADCRange(uint8_t adcRange) { - // adcRange: one of MAX86150_ADCRANGE_2048, _4096, _8192, _16384 - //max86150_bitMask(MAX86150_PARTICLECONFIG, MAX86150_ADCRANGE_MASK, adcRange); +void max86150_set_fifo_enable(bool enabled) +{ + 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) { - // sampleRate: one of MAX86150_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200 - //max86150_bitMask(MAX86150_PARTICLECONFIG, MAX86150_SAMPLERATE_MASK, sampleRate); +void max86150_set_ppg_sample_rate(uint8_t sampleRate) +{ + // sampleRate: one of MAX86150_PPG_SAMPLERATE_* + max86150_bit_mask( + MAX86150_PPGCONFIG1, MAX86150_PPG_SAMPLERATE_MASK, sampleRate + ); } -void max86150_setPulseWidth(uint8_t pulseWidth) { - // pulseWidth: one of MAX86150_PULSEWIDTH_69, _188, _215, _411 - //max86150_bitMask(MAX86150_PPGCONFIG1, MAX86150_PULSEWIDTH_MASK, pulseWidth); +void max86150_set_ppg_pulse_width(uint8_t 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) // 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) { - max86150_writeRegister8(_i2caddr, MAX86150_LED_PROX_AMP, amplitude); +void max86150_set_led_proximity_amplitude(uint8_t 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. - max86150_writeRegister8(_i2caddr, MAX86150_PROXINTTHRESH, threshMSB); + // The threshMSB signifies only the 8 most significant-bits of the ADC count. + max86150_write_register( + MAX86150_ADDRESS, MAX86150_PROXINTTHRESH, threshMSB + ); } //Given a slot number assign a thing to it //Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity) //Assigning a SLOT_RED_LED will pulse LED //Assigning a SLOT_RED_PILOT will ?? -void max86150_enableSlot(uint8_t slotNumber, uint8_t device) -{ - //uint8_t originalContents; - - switch (slotNumber) { - case (1): - max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device); - break; - case (2): - max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT2_MASK, device << 4); - break; - case (3): - max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device); - break; - case (4): - max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT4_MASK, device << 4); - break; - default: - //Shouldn't be here! - break; - } +void max86150_fifo_enable_slot(uint8_t slotNumber, uint8_t device) +{ + switch (slotNumber) { + case (1): + max86150_bit_mask( + MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device + ); + break; + case (2): + max86150_bit_mask( + MAX86150_FIFOCONTROL1, + MAX86150_SLOT2_MASK, + device << 4 + ); + break; + case (3): + max86150_bit_mask( + MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device + ); + break; + case (4): + max86150_bit_mask( + MAX86150_FIFOCONTROL2, + MAX86150_SLOT4_MASK, + device << 4 + ); + break; + default: + //Shouldn't be here! + break; + } } //Clears all slot assignments void max86150_disableSlots(void) { - max86150_writeRegister8(_i2caddr, MAX86150_FIFOCONTROL1, 0); - max86150_writeRegister8(_i2caddr, MAX86150_FIFOCONTROL2, 0); + max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOCONTROL1, 0); + max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOCONTROL2, 0); } // // 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 -void max86150_clearFIFO(void) { - max86150_writeRegister8(_i2caddr, MAX86150_FIFOWRITEPTR, 0); - max86150_writeRegister8(_i2caddr, MAX86150_FIFOOVERFLOW, 0); - max86150_writeRegister8(_i2caddr, MAX86150_FIFOREADPTR, 0); +void max86150_clear_fifo(void) +{ + max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOWRITEPTR, 0); + max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOOVERFLOW, 0); + max86150_write_register(MAX86150_ADDRESS, MAX86150_FIFOREADPTR, 0); } //Enable roll over if FIFO over flows -void max86150_enableFIFORollover(void) { - max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_ROLLOVER_MASK, MAX86150_ROLLOVER_ENABLE); -} - -//Disable roll over if FIFO over flows -void max86150_disableFIFORollover(void) { - max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_ROLLOVER_MASK, MAX86150_ROLLOVER_DISABLE); +void max86150_set_fifo_rollover(bool enabled) +{ + if (enabled) { + max86150_bit_mask( + MAX86150_FIFOCONFIG, + MAX86150_ROLLOVER_MASK, + 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 //Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples -void max86150_setFIFOAlmostFull(uint8_t numberOfSamples) { - max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_A_FULL_MASK, numberOfSamples); +void max86150_set_fifo_almost_full(uint8_t numberOfSamples) +{ + max86150_bit_mask( + MAX86150_FIFOCONFIG, MAX86150_A_FULL_MASK, numberOfSamples + ); } //Read the FIFO Write Pointer -uint8_t max86150_getWritePointer(void) { - return (max86150_readRegister8(_i2caddr, MAX86150_FIFOWRITEPTR)); +uint8_t max86150_get_fifo_write_pointer(void) +{ + return (max86150_read_register(MAX86150_ADDRESS, MAX86150_FIFOWRITEPTR)); } //Read the FIFO Read Pointer -uint8_t max86150_getReadPointer(void) { - return (max86150_readRegister8(_i2caddr, MAX86150_FIFOREADPTR)); -} - -// Set the PROX_INT_THRESHold -void max86150_setPROXINTTHRESH(uint8_t val) { - max86150_writeRegister8(_i2caddr, MAX86150_PROXINTTHRESH, val); +uint8_t max86150_get_fifo_read_pointer(void) +{ + return (max86150_read_register(MAX86150_ADDRESS, MAX86150_FIFOREADPTR)); } // // Device ID and Revision // -uint8_t max86150_readPartID() { - return max86150_readRegister8(_i2caddr, MAX86150_PARTID); +uint8_t max86150_read_part_id() +{ + return max86150_read_register(MAX86150_ADDRESS, MAX86150_PARTID); } -//Setup the sensor -//The MAX86150 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 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) +//Set ecg sample rate +void max86150_set_ecg_sample_rate(uint8_t sampleRate) { - activeDevices=3; - max86150_writeRegister8(_i2caddr,MAX86150_SYSCONTROL,0x01); - delay(100); - 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 + // sampleRate: one of MAX86150_ECG_SAMPLERATE_* + max86150_bit_mask( + MAX86150_ECG_CONFIG1, MAX86150_ECG_SAMPLERATE_MASK, sampleRate + ); +} +//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)); - //max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1,(0b00001001)); - //max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1,(0b00000010)); - max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2,(0b00001001)); - //max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2,(0b00000000)); - //max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL1, (char)(FIFOCode & 0x00FF) ); - //max86150_writeRegister8(_i2caddr,MAX86150_FIFOCONTROL2, (char)(FIFOCode >>8) ); +//Setup the sensor +//The MAX86150 has many settings. +//Use the default max86150_setup if you are just getting started with the MAX86150 sensor +void max86150_setup(const uint8_t ppg_sample_rate) +{ + max86150_soft_reset(); + 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_writeRegister8(_i2caddr,MAX86150_PPGCONFIG1,0b11100111); + max86150_fifo_enable_slot(1, MAX86150_SLOT_RED_LED); + 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_writeRegister8(_i2caddr,MAX86150_LED_RANGE, 0x00 ); // PPG_ADC_RGE: 32768nA + max86150_set_ppg_adc_range(MAX86150_ADCRANGE_16384); + 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_writeRegister8(_i2caddr,MAX86150_ECG_CONFIG1,0b00000001); - max86150_writeRegister8(_i2caddr,MAX86150_ECG_CONFIG3,0b00001101); + max86150_set_ecg_sample_rate(MAX86150_ECG_SAMPLERATE_200); + max86150_set_ecg_pga_gain(MAX86150_ECG_PGA_GAIN_8); + max86150_set_ecg_instrumentation_amplifier_gain( + MAX86150_ECG_IA_GAIN_9_5 + ); - max86150_setPulseAmplitudeRed(0xFF); - max86150_setPulseAmplitudeIR(0xFF); + max86150_set_int_datardy(false); + max86150_set_int_full(true); - //Multi-LED Mode Configuration, Enable the reading of the three LEDs - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - //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_clear_fifo(); //Reset the FIFO before we begin checking the sensor - max86150_clearFIFO(); //Reset the FIFO before we begin checking the sensor + max86150_set_fifo_enable(true); } //Tell caller how many samples are max86150_available uint8_t max86150_available(void) { - int8_t numberOfSamples = sense.head - sense.tail; - if (numberOfSamples < 0) numberOfSamples += STORAGE_SIZE; + int8_t numberOfSamples = sense.head - sense.tail; + if (numberOfSamples < 0) + numberOfSamples += STORAGE_SIZE; - return (numberOfSamples); + return (numberOfSamples); } //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 - if(max86150_safeCheck(250)) - return (sense.red[sense.head]); - else - return(0); //Sensor failed to find new data + //Check the sensor for new data for 250ms + if (max86150_safeCheck(250)) + return (sense.red[sense.head]); + else + return (0); //Sensor failed to find new data } //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 - if(max86150_safeCheck(250)) - return (sense.IR[sense.head]); - else - return(0); //Sensor failed to find new data + //Check the sensor for new data for 250ms + if (max86150_safeCheck(250)) + return (sense.IR[sense.head]); + else + return (0); //Sensor failed to find new data } //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 - if(max86150_safeCheck(250)) - return (sense.ecg[sense.head]); - else - return(0); //Sensor failed to find new data + //Check the sensor for new data for 250ms + if (max86150_safeCheck(250)) + return (sense.ecg[sense.head]); + else + return (0); //Sensor failed to find new data } //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 -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 -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 -void max86150_nextSample(void) +void max86150_next_sample(void) { - if(max86150_available()) //Only advance the tail if new data is max86150_available - { - sense.tail++; - sense.tail %= STORAGE_SIZE; //Wrap condition - } + if (max86150_available()) { //Only advance the tail if new data is max86150_available + sense.tail++; + 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 //Call regularly //If new data is max86150_available, it updates the head and tail in the main struct //Returns number of new samples obtained uint16_t max86150_check(void) { - //Read register FIDO_DATA in (3-byte * number of active LED) chunks - //Until FIFO_RD_PTR = FIFO_WR_PTR - - byte readPointer = max86150_getReadPointer(); - byte writePointer = max86150_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 bytes to read - //For this example we are just doing Red and IR (3 bytes each) - int bytesLeftToRead = numberOfSamples * activeDevices * 3; - - //Get ready to read a burst of data from the FIFO register - uint8_t command[] = {MAX86150_FIFODATA}; - I2C_MasterWrite(MXC_I2C1_BUS0, _i2caddr << 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 - while (bytesLeftToRead > 0) - { - 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 - //32 % 6 = 2 left over. We don't want to request 32 bytes, 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 % (activeDevices * 3)); //Trim toGet to be a multiple of the samples we need to read - } - - bytesLeftToRead -= toGet; - - //Request toGet number of bytes from sensor - //_i2cPort->requestFrom(_i2caddr, toGet); - uint8_t data[toGet]; - uint8_t *p = data; - I2C_MasterRead(MXC_I2C1_BUS0, _i2caddr << 1, data, toGet, 0); - - while (toGet > 0) - { - sense.head++; //Advance the head of the storage struct - sense.head %= STORAGE_SIZE; //Wrap condition - - byte temp[sizeof(uint32_t)]; //Array of 4 bytes that we will convert into long - 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)); + //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 + + //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) + int bytesLeftToRead = numberOfSamples * activeDevices * 3; + + //Get ready to read a burst of data from the FIFO register + uint8_t command[] = { MAX86150_FIFODATA }; + 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 + while (bytesLeftToRead > 0) { + int toGet = bytesLeftToRead; + if (toGet > I2C_BUFFER_LENGTH) { + //If toGet is 32 this is bad because we read 9 bytes (Red+IR+ECG * 3 = 9) at a time + //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. + + toGet = I2C_BUFFER_LENGTH - + (I2C_BUFFER_LENGTH % + (activeDevices * + 3)); //Trim toGet to be a multiple of the samples we need to read + } + + bytesLeftToRead -= toGet; + + //Request toGet number of bytes from sensor + //_i2cPort->requestFrom(MAX86150_ADDRESS, toGet); + uint8_t data[bytesLeftToRead]; + uint8_t *p = data; + I2C_MasterRead( + MXC_I2C1_BUS0, + MAX86150_ADDRESS << 1, + data, + bytesLeftToRead, + 0 + ); + + while (bytesLeftToRead > 0) { + sense.head++; //Advance the head of the storage struct + sense.head %= STORAGE_SIZE; //Wrap condition + + byte temp[sizeof( + uint32_t)]; //Array of 4 bytes that we will convert into long + 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 - 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) - { - //Burst read three more bytes - IR - temp[3] = 0; - temp[2] = *p++; - temp[1] = *p++; - temp[0] = *p++; + if (activeDevices > 1) { + //Burst read three more bytes - IR + temp[3] = 0; + temp[2] = *p++; + temp[1] = *p++; + temp[0] = *p++; - //Convert array to long - memcpy(&tempLong, temp, sizeof(tempLong)); + //Convert array to long + memcpy(&tempLong, + temp, + sizeof(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) - { - //Burst read three more bytes - ECG + if (activeDevices > 2) { + //Burst read three more bytes - ECG int32_t tempLongSigned; - - temp[3] = 0; - temp[2] = *p++; - temp[1] = *p++; - temp[0] = *p++; + temp[3] = 0; + temp[2] = *p++; + temp[1] = *p++; + temp[0] = *p++; //Serial.println(tempLong); - //Convert array to long - memcpy(&tempLongSigned, temp, sizeof(tempLongSigned)); - - //tempLong &= 0x3FFFF; //Zero out all but 18 bits + //Convert array to long + memcpy(&tempLongSigned, + temp, + sizeof(tempLongSigned)); + //tempLong &= 0x3FFFF; //Zero out all but 18 bits + sense.ecg[sense.head] = tempLongSigned; + } - sense.ecg[sense.head] = tempLongSigned; - } - - toGet -= activeDevices * 3; - } - } //End while (bytesLeftToRead > 0) - } //End readPtr != writePtr - return (numberOfSamples); //Let the world know how much new data we found + bytesLeftToRead -= activeDevices * 3; + } + } //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 //Returns true if new data was found //Returns false if new data was not found -bool max86150_safeCheck(uint8_t maxTimeToCheck) +bool max86150_safe_check(uint8_t max_tries) { - // TODO - //uint32_t markTime = millis(); - - while(1) - { - //if(millis() - markTime > maxTimeToCheck) return(false); + uint8_t tries = 0; - if(max86150_check() == true) //We found new data! - return(true); + while (tries < max_tries) { + 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 -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 - uint8_t originalContents = max86150_readRegister8(_i2caddr, reg); + // Grab current register context + uint8_t originalContents = + max86150_read_register(MAX86150_ADDRESS, reg); - // Zero-out the portions of the register we're interested in - originalContents = originalContents & mask; + // Zero-out the portions of the register we're interested in + originalContents = originalContents & mask; - // Change contents - max86150_writeRegister8(_i2caddr, reg, originalContents | thing); + // Change contents + max86150_write_register( + MAX86150_ADDRESS, reg, originalContents | thing + ); } -uint8_t max86150_readRegister8(uint8_t address, uint8_t reg) { - - uint8_t tempData = 0; - uint8_t command[] = {reg}; - I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 1, 0); +uint8_t max86150_read_register(uint8_t address, uint8_t reg) +{ + uint8_t tempData = 0; + uint8_t command[] = { reg }; + // 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) { - uint8_t command[] = {reg, value}; - I2C_MasterWrite(MXC_I2C1_BUS0, address << 1, command, 2, 0); +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); } diff --git a/lib/vendor/Maxim/MAX86150/max86150.h b/lib/vendor/Maxim/MAX86150/max86150.h index c9454ea51b32d591824e14ea2db2b7735b28735b..66b102183186fe2626aa17b7f62ad33589aa936a 100644 --- a/lib/vendor/Maxim/MAX86150/max86150.h +++ b/lib/vendor/Maxim/MAX86150/max86150.h @@ -18,86 +18,115 @@ #include <stdint.h> #include <stdbool.h> -typedef uint8_t byte; -bool max86150_begin(void); - -uint32_t max86150_getRed(void); //Returns immediate red value -uint32_t max86150_getIR(void); //Returns immediate IR value -int32_t max86150_getECG(void); //Returns immediate ECG value -bool max86150_safeCheck(uint8_t maxTimeToCheck); //Given a max amount of time, check for new data - -// Configuration -void max86150_softReset(); -void max86150_shutDown(); -void max86150_wakeUp(); +static const uint8_t MAX86150_SAMPLEAVG_1 = 0b000; +static const uint8_t MAX86150_SAMPLEAVG_2 = 0b001; +static const uint8_t MAX86150_SAMPLEAVG_4 = 0b010; +static const uint8_t MAX86150_SAMPLEAVG_8 = 0b011; +static const uint8_t MAX86150_SAMPLEAVG_16 = 0b100; +static const uint8_t MAX86150_SAMPLEAVG_32 = 0b101; + +static const uint8_t MAX86150_ADCRANGE_4096 = 0b00000000; +static const uint8_t MAX86150_ADCRANGE_8192 = 0b01000000; +static const uint8_t MAX86150_ADCRANGE_16384 = 0b10000000; +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); -void max86150_setPulseAmplitudeIR(uint8_t value); -void max86150_setPulseAmplitudeProximity(uint8_t value); - -void max86150_setProximityThreshold(uint8_t threshMSB); +bool max86150_begin(void); -//Multi-led configuration mode (page 22) -void max86150_enableSlot(uint8_t slotNumber, uint8_t device); //Given slot number, assign a device to slot +uint8_t max86150_get_int1(void); +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); - -// Data Collection - -//Interrupts (page 13, 14) -uint8_t max86150_getINT1(void); //Returns the main interrupt group -uint8_t max86150_getINT2(void); //Returns the temp ready interrupt -void max86150_enableAFULL(void); //Enable/disable individual interrupts -void max86150_disableAFULL(void); -void max86150_enableDATARDY(void); -void max86150_disableDATARDY(void); -void max86150_enableALCOVF(void); -void max86150_disableALCOVF(void); -void max86150_enablePROXINT(void); -void max86150_disablePROXINT(void); -void max86150_enableDIETEMPRDY(void); -void max86150_disableDIETEMPRDY(void); - -//FIFO Configuration (page 18) -void max86150_setFIFOAverage(uint8_t samples); -void max86150_enableFIFORollover(); -void max86150_disableFIFORollover(); -void max86150_setFIFOAlmostFull(uint8_t samples); - -//FIFO Reading -uint16_t max86150_check(void); //Checks for new data and fills FIFO -uint8_t max86150_available(void); //Tells caller how many new samples are available (head - tail) -void max86150_nextSample(void); //Advances the tail of the sense array -uint32_t max86150_getFIFORed(void); //Returns the FIFO sample pointed to by tail -uint32_t max86150_getFIFOIR(void); //Returns the FIFO sample pointed to by tail -int32_t max86150_getFIFOECG(void); //Returns the FIFO sample pointed to by tail - -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); +void max86150_set_ppg_averaging(uint8_t numberOfSamples); +void max86150_clear_fifo(void); +void max86150_set_fifo_rollover(bool enabled); +void max86150_set_fifo_almost_full_clear(bool enabled); +void max86150_set_fifo_almost_full_repeat(bool enabled); +void max86150_set_fifo_almost_full(uint8_t numberOfSamples); +uint8_t max86150_get_fifo_write_pointer(void); +uint8_t max86150_get_fifo_read_pointer(void); +uint8_t max86150_read_part_id(); +void max86150_set_ecg_sample_rate(uint8_t sampleRate); +void max86150_set_ecg_pga_gain(uint8_t gain); +void max86150_set_ecg_instrumentation_amplifier_gain(uint8_t gain); + +void max86150_setup(const uint8_t ppg_sample_rate); + +uint8_t max86150_available(void); +uint32_t max86150_get_red(void); +uint32_t max86150_get_ir(void); +int32_t max86150_get_ecg(void); +uint32_t max86150_get_fifo_red(void); +uint32_t max86150_get_fifo_ir(void); +int32_t max86150_get_fifo_ecg(void); +void max86150_next_sample(void); +uint8_t max86150_get_sample(uint32_t *red, uint32_t *ir, int32_t *ecg); +uint16_t max86150_check(void); +bool max86150_safe_check(uint8_t max_tries); + +void max86150_bit_mask(uint8_t reg, uint8_t mask, uint8_t thing); +uint8_t max86150_read_register(uint8_t address, uint8_t reg); +void max86150_write_register(uint8_t address, uint8_t reg, uint8_t value);