Skip to content
Snippets Groups Projects
Commit 36a774a8 authored by schneider's avatar schneider
Browse files

Merge branch 'max86150' into 'master'

Stable max86150 epicardium API

See merge request card10/firmware!371
parents f17417e1 d92965df
No related branches found
No related tags found
No related merge requests found
Pipeline #4509 passed
......@@ -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
......@@ -203,9 +202,11 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
#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.
......
......@@ -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,
......
......@@ -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;
}
#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));
}
}
......@@ -13,6 +13,7 @@ module_sources = files(
'lifecycle.c',
'light_sensor.c',
'log.c',
'max86150.c',
'max30001.c',
'mutex.c',
'panic.c',
......
......@@ -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);
......
......@@ -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();
......
......@@ -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,
};
......
......@@ -65,78 +65,61 @@ 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_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 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 MAX86150_LED1_RANGE_MASK = (byte)~0b00000011;
static const uint8_t MAX86150_LED2_RANGE_MASK = (byte)~0b00001100;
static const uint8_t MAX_30105_EXPECTEDPARTID = 0x1E;
static const uint8_t MAX86150_ECG_SAMPLERATE_MASK = (byte)~0b00000111;
static uint8_t _i2caddr;
static const uint8_t MAX86150_ECG_PGA_GAIN_MASK = (byte)~0b00001100;
//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 const uint8_t MAX86150_ECG_IA_GAIN_MASK = (byte)~0b00000011;
static void max86150_bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
static const uint8_t MAX86150_EXPECTEDPARTID = 0x1E;
#define STORAGE_SIZE 128 //Each long is 4 bytes so limit this to fit on your micro
typedef struct Record
{
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];
......@@ -153,11 +136,9 @@ static void delay(int ms)
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) {
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;
......@@ -170,133 +151,234 @@ 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));
return (max86150_read_register(MAX86150_ADDRESS, MAX86150_INTSTAT1));
}
void max86150_enableAFULL(void) {
max86150_bitMask(MAX86150_INTENABLE1, MAX86150_INT_A_FULL_MASK, MAX86150_INT_A_FULL_ENABLE);
}
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_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
);
}
void max86150_disablePROXINT(void) {
max86150_bitMask(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);
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 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!
// 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_shutDown(void) {
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_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN);
max86150_bit_mask(
MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_SHUTDOWN
);
}
void max86150_wakeUp(void) {
void max86150_wake_up(void)
{
// Pull IC out of low power mode (datasheet pg. 19)
max86150_bitMask(MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP);
max86150_bit_mask(
MAX86150_SYSCONTROL, MAX86150_SHUTDOWN_MASK, MAX86150_WAKEUP
);
}
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_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);
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)
void max86150_fifo_enable_slot(uint8_t slotNumber, uint8_t device)
{
//uint8_t originalContents;
switch (slotNumber) {
case (1):
max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device);
max86150_bit_mask(
MAX86150_FIFOCONTROL1, MAX86150_SLOT1_MASK, device
);
break;
case (2):
max86150_bitMask(MAX86150_FIFOCONTROL1, MAX86150_SLOT2_MASK, device << 4);
max86150_bit_mask(
MAX86150_FIFOCONTROL1,
MAX86150_SLOT2_MASK,
device << 4
);
break;
case (3):
max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device);
max86150_bit_mask(
MAX86150_FIFOCONTROL2, MAX86150_SLOT3_MASK, device
);
break;
case (4):
max86150_bitMask(MAX86150_FIFOCONTROL2, MAX86150_SLOT4_MASK, device << 4);
max86150_bit_mask(
MAX86150_FIFOCONTROL2,
MAX86150_SLOT4_MASK,
device << 4
);
break;
default:
//Shouldn't be here!
......@@ -307,146 +389,189 @@ void max86150_enableSlot(uint8_t slotNumber, uint8_t device)
//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);
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
);
}
//Disable roll over if FIFO over flows
void max86150_disableFIFORollover(void) {
max86150_bitMask(MAX86150_FIFOCONFIG, MAX86150_ROLLOVER_MASK, MAX86150_ROLLOVER_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;
if (numberOfSamples < 0)
numberOfSamples += STORAGE_SIZE;
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))
......@@ -456,7 +581,7 @@ uint32_t max86150_getRed(void)
}
//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))
......@@ -466,7 +591,7 @@ uint32_t max86150_getIR(void)
}
//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))
......@@ -476,28 +601,27 @@ int32_t max86150_getECG(void)
}
//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]);
}
//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]);
}
//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]);
}
//Advance the tail
void max86150_nextSample(void)
{
if(max86150_available()) //Only advance the tail if new data is max86150_available
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
}
......@@ -507,22 +631,70 @@ void max86150_nextSample(void)
//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)
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_getReadPointer();
byte writePointer = max86150_getWritePointer();
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)
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_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
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)
......@@ -530,37 +702,46 @@ uint16_t max86150_check(void)
//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);
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)
{
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.
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
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];
//_i2cPort->requestFrom(MAX86150_ADDRESS, toGet);
uint8_t data[bytesLeftToRead];
uint8_t *p = data;
I2C_MasterRead(MXC_I2C1_BUS0, _i2caddr << 1, data, toGet, 0);
while (toGet > 0)
{
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
byte temp[sizeof(
uint32_t)]; //Array of 4 bytes that we will convert into long
uint32_t tempLong;
//Burst read three bytes - RED
......@@ -574,10 +755,10 @@ uint16_t max86150_check(void)
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
temp[3] = 0;
temp[2] = *p++;
......@@ -585,18 +766,19 @@ uint16_t max86150_check(void)
temp[0] = *p++;
//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));
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;
}
if (activeDevices > 2)
{
if (activeDevices > 2) {
//Burst read three more bytes - ECG
int32_t tempLongSigned;
temp[3] = 0;
temp[2] = *p++;
temp[1] = *p++;
......@@ -604,14 +786,14 @@ uint16_t max86150_check(void)
//Serial.println(tempLong);
//Convert array to long
memcpy(&tempLongSigned, temp, sizeof(tempLongSigned));
memcpy(&tempLongSigned,
temp,
sizeof(tempLongSigned));
//tempLong &= 0x3FFFF; //Zero out all but 18 bits
sense.ecg[sense.head] = tempLongSigned;
}
toGet -= activeDevices * 3;
bytesLeftToRead -= activeDevices * 3;
}
} //End while (bytesLeftToRead > 0)
} //End readPtr != writePtr
......@@ -621,47 +803,52 @@ uint16_t max86150_check(void)
//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();
uint8_t tries = 0;
while(1)
{
//if(millis() - markTime > maxTimeToCheck) return(false);
if(max86150_check() == true) //We found new data!
while (tries < max_tries) {
if (max86150_check() == true) { //We found new data!
return (true);
}
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);
uint8_t originalContents =
max86150_read_register(MAX86150_ADDRESS, reg);
// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;
// 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 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);
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);
}
......@@ -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);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment