Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
François Revol
firmware
Commits
4f32a5ce
Commit
4f32a5ce
authored
Aug 23, 2019
by
schneider
Browse files
Merge branch 'schneider/max30001-epicaridum' into 'master'
MAX30001 support for epicardium See merge request
card10/firmware!225
parents
198be023
2c343717
Changes
8
Hide whitespace changes
Inline
Side-by-side
epicardium/epicardium.h
View file @
4f32a5ce
...
...
@@ -124,6 +124,10 @@ typedef _Bool bool;
#define API_BHI160_DISABLE 0xe1
#define API_BHI160_DISABLE_ALL 0xe2
#define API_MAX30001_ENABLE 0xf0
#define API_MAX30001_DISABLE 0xf1
/* clang-format on */
typedef
uint32_t
api_int_id_t
;
...
...
@@ -171,9 +175,11 @@ API_ISR(EPIC_INT_BHI160_ACCELEROMETER, epic_isr_bhi160_accelerometer);
API_ISR
(
EPIC_INT_BHI160_ORIENTATION
,
epic_isr_bhi160_orientation
);
#define EPIC_INT_BHI160_GYROSCOPE 6
API_ISR
(
EPIC_INT_BHI160_GYROSCOPE
,
epic_isr_bhi160_gyroscope
);
#define EPIC_INT_MAX30001_ECG 7
API_ISR
(
EPIC_INT_MAX30001_ECG
,
epic_isr_max30001_ecg
);
/* Number of defined interrupts. */
#define EPIC_INT_NUM
7
#define EPIC_INT_NUM
8
/* clang-format on */
/*
...
...
@@ -1628,4 +1634,68 @@ API_ISR(EPIC_INT_RTC_ALARM, epic_isr_rtc_alarm);
*/
API
(
API_TRNG_READ
,
int
epic_trng_read
(
uint8_t
*
dest
,
size_t
size
));
/**
* MAX30001 API
* ----------
*/
/**
* Configuration for a MAX30001 sensor.
*
* This struct is used when enabling the sensor using
* :c:func:`epic_max30001_enable_sensor`.
*/
struct
max30001_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 the sensor in Hz.
*/
uint16_t
sample_rate
;
/**
* Set to true if the second lead comes from USB-C
*/
bool
usb
;
/**
* Set to true if the interal lead bias of the MAX30001 is to be used.
*/
bool
bias
;
/** Always zero. Reserved for future parameters. */
uint8_t
_padding
[
8
];
};
/**
* Enable a MAX30001 ecg sensor. Calling this funciton 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.
* :returns: A sensor descriptor which can be used with
* :c:func:`epic_stream_read` or a negative error value:
*
* - ``-EBUSY``: The MAX30001 driver is currently busy with other tasks and
* could not be acquired for enabling a sensor.
*
* .. versionadded:: 1.6
*/
API
(
API_MAX30001_ENABLE
,
int
epic_max30001_enable_sensor
(
struct
max30001_sensor_config
*
config
));
/**
* Disable MAX30001
*
* .. versionadded:: 1.6
*/
API
(
API_MAX30001_DISABLE
,
int
epic_max30001_disable_sensor
(
void
));
#endif
/* _EPICARDIUM_H */
epicardium/main.c
View file @
4f32a5ce
...
...
@@ -62,6 +62,17 @@ int main(void)
abort
();
}
/* MAX30001 */
if
(
xTaskCreate
(
vMAX30001Task
,
(
const
char
*
)
"MAX30001 Driver"
,
configMINIMAL_STACK_SIZE
*
2
,
NULL
,
tskIDLE_PRIORITY
+
1
,
NULL
)
!=
pdPASS
)
{
LOG_CRIT
(
"startup"
,
"Failed to create %s task!"
,
"MAX30001"
);
abort
();
}
/* API */
if
(
xTaskCreate
(
vApiDispatcher
,
...
...
epicardium/modules/MAX30003.h
0 → 100644
View file @
4f32a5ce
/* clang-format off */
/*******************************************************************************
* Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
#ifndef _MAX30003_H_
#define _MAX30003_H_
///MAX30003 Registers
enum
Registers_e
{
NO_OP
=
0x00
,
STATUS
=
0x01
,
EN_INT
=
0x02
,
EN_INT2
=
0x03
,
MNGR_INT
=
0x04
,
MNGR_DYN
=
0x05
,
SW_RST
=
0x08
,
SYNCH
=
0x09
,
FIFO_RST
=
0x0A
,
INFO
=
0x0F
,
CNFG_GEN
=
0x10
,
CNFG_ALL
=
0x12
,
CNFG_EMUX
=
0x14
,
CNFG_ECG
=
0x15
,
CNFG_RTOR1
=
0x1D
,
CNFG_RTOR2
=
0x1E
,
ECG_FIFO_BURST
=
0x20
,
ECG_FIFO
=
0x21
,
RTOR
=
0x25
,
NO_OP2
=
0x7F
};
///Status register bits
union
Status_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
loff_nl
:
1
;
uint32_t
loff_nh
:
1
;
uint32_t
loff_pl
:
1
;
uint32_t
loff_ph
:
1
;
uint32_t
reserved1
:
4
;
uint32_t
pllint
:
1
;
uint32_t
samp
:
1
;
uint32_t
rrint
:
1
;
uint32_t
lonint
:
1
;
uint32_t
reserved2
:
8
;
uint32_t
dcloffint
:
1
;
uint32_t
fstint
:
1
;
uint32_t
eovf
:
1
;
uint32_t
eint
:
1
;
uint32_t
reserved3
:
8
;
}
bits
;
};
///Enable Interrupt registers bits
union
EnableInterrupts_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
intb_type
:
2
;
uint32_t
reserved1
:
6
;
uint32_t
en_pllint
:
1
;
uint32_t
en_samp
:
1
;
uint32_t
en_rrint
:
1
;
uint32_t
en_loint
:
1
;
uint32_t
reserved2
:
8
;
uint32_t
en_dcloffint
:
1
;
uint32_t
en_fstint
:
1
;
uint32_t
en_eovf
:
1
;
uint32_t
en_eint
:
1
;
uint32_t
reserved3
:
8
;
}
bits
;
};
///Manage Interrupt register bits
union
ManageInterrupts_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
samp_it
:
4
;
uint32_t
clr_samp
:
1
;
uint32_t
reserved1
:
1
;
uint32_t
clr_rrint
:
2
;
uint32_t
clr_fast
:
1
;
uint32_t
reserved2
:
12
;
uint32_t
efit
:
5
;
uint32_t
reserved3
:
8
;
}
bits
;
};
///Manage Dynamic Modes register bits
union
ManageDynamicModes_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
reserved1
:
16
;
uint32_t
fast_th
:
6
;
uint32_t
fast
:
2
;
uint32_t
reserved2
:
8
;
}
bits
;
};
///General Configuration bits
union
GeneralConfiguration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
rbiasn
:
1
;
uint32_t
rbiasp
:
1
;
uint32_t
rbiasv
:
2
;
uint32_t
en_rbias
:
2
;
uint32_t
vth
:
2
;
uint32_t
imag
:
3
;
uint32_t
ipol
:
1
;
uint32_t
en_dcloff
:
2
;
uint32_t
reserved1
:
5
;
uint32_t
en_ecg
:
1
;
uint32_t
fmstr
:
2
;
uint32_t
en_ulp_lon
:
2
;
uint32_t
reserved2
:
8
;
}
bits
;
};
///Cal Configuration bits
union
CalConfiguration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
thigh
:
11
;
uint32_t
fifty
:
1
;
uint32_t
fcal
:
3
;
uint32_t
reserved1
:
5
;
uint32_t
vmag
:
1
;
uint32_t
vmode
:
1
;
uint32_t
en_vcal
:
1
;
uint32_t
reserved2
:
9
;
}
bits
;
};
///Mux Configuration bits
union
MuxConfiguration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
reserved1
:
16
;
uint32_t
caln_sel
:
2
;
uint32_t
calp_sel
:
2
;
uint32_t
openn
:
1
;
uint32_t
openp
:
1
;
uint32_t
reserved2
:
1
;
uint32_t
pol
:
1
;
uint32_t
reserved3
:
8
;
}
bits
;
};
///ECG Configuration bits
union
ECGConfiguration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
reserved1
:
12
;
uint32_t
dlpf
:
2
;
uint32_t
dhpf
:
1
;
uint32_t
reserved2
:
1
;
uint32_t
gain
:
2
;
uint32_t
reserved3
:
4
;
uint32_t
rate
:
2
;
uint32_t
reserved4
:
8
;
}
bits
;
};
///RtoR1 Configuration bits
union
RtoR1Configuration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
reserved1
:
8
;
uint32_t
ptsf
:
4
;
uint32_t
pavg
:
2
;
uint32_t
reserved2
:
1
;
uint32_t
en_rtor
:
1
;
uint32_t
rgain
:
4
;
uint32_t
wndw
:
4
;
uint32_t
reserved3
:
8
;
}
bits
;
};
///RtoR2 Configuration bits
union
RtoR2Configuration_u
{
///Access all bits
uint32_t
all
;
///Access individual bits
struct
{
uint32_t
reserved1
:
8
;
uint32_t
rhsf
:
3
;
uint32_t
reserved2
:
1
;
uint32_t
ravg
:
2
;
uint32_t
reserved3
:
2
;
uint32_t
hoff
:
6
;
uint32_t
reserved4
:
10
;
}
bits
;
};
#endif
/* _MAX30003_H_ */
/* clang-format on */
epicardium/modules/hardware.c
View file @
4f32a5ce
...
...
@@ -269,5 +269,7 @@ int hardware_reset(void)
*/
epic_bme680_deinit
();
epic_max30001_disable_sensor
();
return
0
;
}
epicardium/modules/max30001.c
0 → 100644
View file @
4f32a5ce
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "pmic.h"
#include "spi.h"
#include "MAX30003.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "api/interrupt-sender.h"
#include "epicardium.h"
#include "modules/log.h"
#include "modules/modules.h"
#include "modules/stream.h"
/* Ticks to wait when trying to acquire lock */
#define LOCK_WAIT pdMS_TO_TICKS(MAX30001_MUTEX_WAIT_MS)
/* Interrupt Pin */
static
const
gpio_cfg_t
max30001_interrupt_pin
=
{
PORT_1
,
PIN_12
,
GPIO_FUNC_IN
,
GPIO_PAD_PULL_UP
};
static
const
gpio_cfg_t
analog_switch
=
{
PORT_0
,
PIN_31
,
GPIO_FUNC_OUT
,
GPIO_PAD_NONE
};
/* clang-format on */
/* MAX30001 Task ID */
static
TaskHandle_t
max30001_task_id
=
NULL
;
/* MAX30001 Mutex */
static
StaticSemaphore_t
max30001_mutex_data
;
static
SemaphoreHandle_t
max30001_mutex
=
NULL
;
/* Stream */
static
struct
stream_info
max30001_stream
;
;
/* Active */
static
bool
max30001_sensor_active
=
false
;
static
int
ecg_enable
(
int
sample_rate
,
bool
enable_internal_pull
);
static
int
ecg_disable
(
void
);
/* -- API -------------------------------------------------------------- {{{ */
int
epic_max30001_enable_sensor
(
struct
max30001_sensor_config
*
config
)
{
int
result
=
0
;
result
=
hwlock_acquire
(
HWLOCK_SPI_ECG
,
pdMS_TO_TICKS
(
100
));
if
(
result
<
0
)
{
return
result
;
}
if
(
xSemaphoreTake
(
max30001_mutex
,
LOCK_WAIT
)
!=
pdTRUE
)
{
result
=
-
EBUSY
;
goto
out_free_spi
;
}
struct
stream_info
*
stream
=
&
max30001_stream
;
;
stream
->
item_size
=
sizeof
(
uint16_t
);
stream
->
queue
=
xQueueCreate
(
config
->
sample_buffer_len
,
stream
->
item_size
);
if
(
stream
->
queue
==
NULL
)
{
result
=
-
ENOMEM
;
goto
out_free_both
;
}
result
=
stream_register
(
SD_MAX30001_ECG
,
stream
);
if
(
result
<
0
)
{
vQueueDelete
(
stream
->
queue
);
goto
out_free_both
;
}
result
=
ecg_enable
(
config
->
sample_rate
,
config
->
bias
);
if
(
result
<
0
)
{
vQueueDelete
(
stream
->
queue
);
goto
out_free_both
;
}
if
(
config
->
usb
)
{
GPIO_OutSet
(
&
analog_switch
);
// USB
}
else
{
GPIO_OutClr
(
&
analog_switch
);
// Wrist
}
max30001_sensor_active
=
true
;
result
=
SD_MAX30001_ECG
;
out_free_both:
xSemaphoreGive
(
max30001_mutex
);
out_free_spi:
hwlock_release
(
HWLOCK_SPI_ECG
);
return
result
;
}
int
epic_max30001_disable_sensor
(
void
)
{
int
result
=
0
;
result
=
hwlock_acquire
(
HWLOCK_SPI_ECG
,
pdMS_TO_TICKS
(
100
));
if
(
result
<
0
)
{
return
result
;
}
if
(
xSemaphoreTake
(
max30001_mutex
,
LOCK_WAIT
)
!=
pdTRUE
)
{
result
=
-
EBUSY
;
goto
out_free_spi
;
}
struct
stream_info
*
stream
=
&
max30001_stream
;
result
=
stream_deregister
(
SD_MAX30001_ECG
,
stream
);
if
(
result
<
0
)
{
goto
out_free_both
;
}
vQueueDelete
(
stream
->
queue
);
stream
->
queue
=
NULL
;
result
=
ecg_disable
();
if
(
result
<
0
)
{
goto
out_free_both
;
}
max30001_sensor_active
=
false
;
result
=
0
;
out_free_both:
xSemaphoreGive
(
max30001_mutex
);
out_free_spi:
hwlock_release
(
HWLOCK_SPI_ECG
);
return
result
;
}
/* }}} */
/* -- Driver ----------------------------------------------------------- {{{ */
/*
* Handle a single packet from the FIFO. For most sensors this means pushing
* the sample into its sample queue.
*/
static
void
max30001_handle_samples
(
int16_t
*
sensor_data
,
int16_t
n
)
{
if
(
max30001_stream
.
queue
==
NULL
)
{
return
;
}
while
(
n
--
)
{
uint16_t
data
=
-*
sensor_data
++
;
if
(
xQueueSend
(
max30001_stream
.
queue
,
&
data
,
MAX30001_MUTEX_WAIT_MS
)
!=
pdTRUE
)
{
LOG_WARN
(
"max30001"
,
"queue full"
);
// TODO; handle queue full
}
}
api_interrupt_trigger
(
EPIC_INT_MAX30001_ECG
);
}
/***** Functions *****/
static
uint32_t
ecg_read_reg
(
uint8_t
reg
)
{
spi_req_t
req
;
uint8_t
tx_data
[]
=
{
(
reg
<<
1
)
|
1
,
0
,
0
,
0
};
uint8_t
rx_data
[]
=
{
0
,
0
,
0
,
0
};
req
.
tx_data
=
tx_data
;
req
.
rx_data
=
rx_data
;
req
.
len
=
4
;
req
.
bits
=
8
;
req
.
width
=
SPI17Y_WIDTH_1
;
req
.
ssel
=
0
;
req
.
deass
=
1
;
req
.
ssel_pol
=
SPI17Y_POL_LOW
;
req
.
tx_num
=
0
;
req
.
rx_num
=
0
;
SPI_MasterTrans
(
SPI0
,
&
req
);
return
(
rx_data
[
1
]
<<
16
)
|
(
rx_data
[
2
]
<<
8
)
|
rx_data
[
3
];
}
static
void
ecg_write_reg
(
uint8_t
reg
,
uint32_t
data
)
{
//printf("write %02x %06lx\n", reg, data);
spi_req_t
req
;
uint8_t
tx_data
[]
=
{
(
reg
<<
1
)
|
0
,
data
>>
16
,
(
data
>>
8
)
&
0xFF
,
data
&
0xFF
};
uint8_t
rx_data
[]
=
{
0
,
0
,
0
,
0
};
req
.
tx_data
=
tx_data
;
req
.
rx_data
=
rx_data
;
req
.
len
=
4
;
req
.
bits
=
8
;
req
.
width
=
SPI17Y_WIDTH_1
;
req
.
ssel
=
0
;
req
.
deass
=
1
;
req
.
ssel_pol
=
SPI17Y_POL_LOW
;
req
.
tx_num
=
0
;
req
.
rx_num
=
0
;
SPI_MasterTrans
(
SPI0
,
&
req
);
}
static
int
ecg_enable
(
int
sample_rate
,
bool
enable_internal_pull
)
{
// Reset ECG to clear registers
ecg_write_reg
(
SW_RST
,
0
);
// General config register setting
union
GeneralConfiguration_u
CNFG_GEN_r
;
CNFG_GEN_r
.
bits
.
en_ecg
=
1
;
// Enable ECG channel
if
(
enable_internal_pull
)
{
CNFG_GEN_r
.
bits
.
rbiasn
=
1
;
// Enable resistive bias on negative input
CNFG_GEN_r
.
bits
.
rbiasp
=
1
;
// Enable resistive bias on positive input
CNFG_GEN_r
.
bits
.
en_rbias
=
1
;
// Enable resistive bias
}
else
{