-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathHealthMonitor.cpp
324 lines (280 loc) · 9.56 KB
/
HealthMonitor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "HealthMonitor.h"
/// Initialization values for ECG_InitStart()
#define EN_ECG 0b1
#define OPENP 0b0
#define OPENN 0b0
#define POL 0b0
#define CALP_SEL 0b0
#define CALN_SEL 0b0
#define E_FIT 0xf
#define RATE 0b10
#define GAIN 0b00
#define DHPF 0b01
#define DLPF 0b01
/// Initialization values for CAL_InitStart()
#define EN_VCAL 0b1
#define VMODE 0b1
#define VMAG 0b1
#define FCAL 0b011
#define THIGH 0x7FF
#define FIFTY 0b0
/// Initializaton values for Rbias_FMSTR_Init()
#define EN_RBIAS 0b01
#define RBIASV 0b10
#define RBIASP 0b1
#define RBIASN 0b1
#define FMSTR 0b00
#define RTOR1 0x3fa300
HealthMonitor::HealthMonitor()
: i2c2(I2C2_SDA, I2C2_SCL),
spi(SPI0_MOSI, SPI0_MISO, SPI0_SCK, SPI0_SS),
max14720(&i2c2, MAX14720_I2C_SLAVE_ADDR),
max30001(&spi),
max30001_InterruptB(P3_6),
max30001_Interrupt2B(P4_5),
max30101(&i2c2),
max30101_Interrupt(P4_0),
pwmout(P1_7)
{
n_ir_buffer_length = 500; // buffer length of 100 stores 5 seconds of
// samples running at 100sps
spo2_init = false;
}
HealthMonitor::~HealthMonitor(void)
{
}
int HealthMonitor::init()
{
int result = init_pmic();
// set NVIC priorities for GPIO to prevent priority inversion
NVIC_SetPriority(GPIO_P0_IRQn, 5);
NVIC_SetPriority(GPIO_P1_IRQn, 5);
NVIC_SetPriority(GPIO_P2_IRQn, 5);
NVIC_SetPriority(GPIO_P3_IRQn, 5);
NVIC_SetPriority(GPIO_P4_IRQn, 5);
NVIC_SetPriority(GPIO_P5_IRQn, 5);
NVIC_SetPriority(GPIO_P6_IRQn, 5);
// used by the MAX30001
NVIC_SetPriority(SPI1_IRQn, 0);
result += init_pulse_ox();
result += init_ecg();
return result;
}
int HealthMonitor::init_pulse_ox()
{
MAX30101::ModeConfiguration_u mode_config;
mode_config.all = 0;
mode_config.bits.reset = 1;
int res = max30101.setModeConfiguration(mode_config);
wait_ms(100);
MAX30101::FIFO_Configuration_u fifo_config;
fifo_config.bits.sample_average = 1;
fifo_config.bits.fifo_a_full = 17; // fifo almost full = 17
fifo_config.bits.fifo_roll_over_en = 0; // no roll over
res = res + max30101.setFIFOConfiguration(fifo_config);
MAX30101::SpO2Configuration_u spo2_config;
spo2_config.bits.led_pw = 3; // 411 us
spo2_config.bits.spo2_sr = 1; // 100 samples per second
spo2_config.bits.spo2_adc_range = 1; // 4096 nA
res = res + max30101.setSpO2Configuration(spo2_config);
MAX30101::InterruptBitField_u interrupt_config;
interrupt_config.bits.a_full = 1; // Almost full flag
interrupt_config.bits.ppg_rdy = 1; // New FIFO Data Ready
max30101.enableInterrupts(interrupt_config);
// ~7 ma for both LED
res += max30101.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24);
res += max30101.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24);
mode_config.all = 0;
mode_config.bits.mode = 0x03;
res += max30101.setModeConfiguration(mode_config);
return res;
}
void HealthMonitor::spo2_range()
{
// read the first 500 samples, and determine the signal range
for (i = 0; i < n_ir_buffer_length; i++) {
while (max30101_Interrupt.read() == 1)
; // wait until the interrupt pin asserts
max30101.read_spo2_fifo((aun_red_buffer + i),
(aun_ir_buffer + i)); // read from MAX30102 FIFO
}
// calculate heart rate and SpO2 after first 500 samples (first 5 seconds of
// samples)
maxim_heart_rate_and_oxygen_saturation(
aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02,
&ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
spo2_init = true;
}
bool HealthMonitor::read_spo2(uint32_t *spo2)
{
if (!spo2_init)
spo2_range();
i = 0;
// dumping the first 100 sets of samples in the memory and shift the last
// 400 sets of samples to the top
for (i = 100; i < 500; i++) {
aun_red_buffer[i - 100] = aun_red_buffer[i];
aun_ir_buffer[i - 100] = aun_ir_buffer[i];
}
// take 100 sets of samples before calculating the heart rate.
for (i = 400; i < 500; i++) {
while (max30101_Interrupt.read() == 1)
;
max30101.read_spo2_fifo((aun_red_buffer + i), (aun_ir_buffer + i));
}
maxim_heart_rate_and_oxygen_saturation(
aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02,
&ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
// spo2_range();
/**
printf("red=");
printf("%i", aun_red_buffer[i]);
printf(", ir=");
printf("%i", aun_ir_buffer[i]);
printf(", HR=%i, ", n_heart_rate);
printf("HRvalid=%i, ", ch_hr_valid);
printf("SpO2=%i, ", n_sp02);
printf("SPO2Valid=%i\n\r", ch_spo2_valid);
**/
*spo2 = n_sp02;
return (ch_spo2_valid == 1);
}
int HealthMonitor::init_pmic()
{
// initialize HVOUT on the MAX14720 PMIC
int result = max14720.init();
if (result == MAX14720_ERROR) {
return -1;
}
max14720.boostEn = MAX14720::BOOST_ENABLED;
max14720.boostSetVoltage(HVOUT_VOLTAGE);
return result;
}
int HealthMonitor::start_ecg()
{
uint32_t all;
max30001.reg_write(MAX30001::EN_INT, 3);
max30001.reg_write(MAX30001::EN_INT2, 3);
max30001.ECG_InitStart(0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0f, 0x02, 0x00, 0x01, 0x01);
max30001.RtoR_InitStart(0x01, 0x03, 0x0f, 0x02, 0x03,
0x20, 0x02, 0x04, 0x01);
max30001.INT_assignment(
MAX30001::MAX30001_INT_B, // en_enint_loc
MAX30001::MAX30001_NO_INT, // en_eovf_loc
MAX30001::MAX30001_NO_INT, // en_fstint_loc
MAX30001::MAX30001_INT_2B, // en_dcloffint_loc
MAX30001::MAX30001_INT_B, // en_bint_loc
MAX30001::MAX30001_NO_INT, // en_bovf_loc
MAX30001::MAX30001_INT_2B, // en_bover_loc
MAX30001::MAX30001_INT_2B, // en_bundr_loc
MAX30001::MAX30001_NO_INT, // en_bcgmon_loc
MAX30001::MAX30001_INT_B, // en_pint_loc
MAX30001::MAX30001_NO_INT, // en_povf_loc,
MAX30001::MAX30001_NO_INT, // en_pedge_loc
MAX30001::MAX30001_INT_2B, // en_lonint_loc
MAX30001::MAX30001_INT_B, // en_rrint_loc
MAX30001::MAX30001_NO_INT, // en_samp_loc
MAX30001::MAX30001_INT_ODNR, // intb_Type
MAX30001::MAX30001_INT_ODNR); // int2b_Type
//fifo_clear();
max30001.synch();
max30001.reg_read(MAX30001::STATUS, &all);
return 0;
}
int HealthMonitor::stop_ecg()
{
uint32_t val;
max30001.reg_write(MAX30001::CNFG_GEN, 0x80004);
max30001.reg_read(MAX30001::CNFG_RTOR1, &val);
max30001.reg_read(MAX30001::CNFG_RTOR1, &val);
if (RTOR1 != val) {
max30001.reg_write(MAX30001::CNFG_RTOR1, RTOR1);
}
max30001.reg_write(MAX30001::EN_INT, 3);
max30001.reg_write(MAX30001::EN_INT2, 3);
return 0;
}
int HealthMonitor::init_ecg()
{
uint32_t val;
uint32_t id;
int part_version;
printf("init ecg\r\n");
/* the example app specifically states the id has to be read twice */
max30001.reg_read(MAX30001::INFO, &id);
max30001.reg_read(MAX30001::INFO, &id);
part_version = (id >> 12) & 0x3;
if (part_version == 0) {
printf("Device: MAX30004\r\n");
} else if (part_version == 1) {
printf("Device: MAX30001\r\n");
} else if (part_version == 2) {
printf("Device: MAX30002\r\n");
} else if (part_version == 3) {
printf("Device: MAX30003\r\n");
}
max30001_InterruptB.disable_irq();
max30001_Interrupt2B.disable_irq();
max30001_InterruptB.mode(PullUp);
max30001_InterruptB.fall(&MAX30001::Mid_IntB_Handler);
max30001_Interrupt2B.mode(PullUp);
max30001_Interrupt2B.fall(&MAX30001::Mid_Int2B_Handler);
max30001_InterruptB.enable_irq();
max30001_Interrupt2B.enable_irq();
max30001.AllowInterrupts(1);
// Configuring the FCLK for the ECG, set to 32.768KHZ
// mbed does not provide the resolution necessary, so for now we have a
// specific solution...
max30001.FCLK_MaximOnly();
max30001.sw_rst(); // Do a software reset of the MAX30001
max30001.reg_write(MAX30001::CNFG_EMUX, 0x0);
max30001.reg_write(MAX30001::CNFG_GEN, 0x80004);
max30001.reg_read(MAX30001::CNFG_RTOR1, &val);
max30001.reg_read(MAX30001::CNFG_RTOR1, &val);
if (RTOR1 != val) {
max30001.reg_write(MAX30001::CNFG_RTOR1, RTOR1);
}
return 0;
}
void HealthMonitor::read_ecg(uint8_t *data)
{
unsigned int i;
uint8_t *bytePtr;
MAX30001::max30001_bledata_t heartrateData;
max30001.ReadHeartrateData(&heartrateData);
bytePtr = reinterpret_cast<uint8_t *>(&heartrateData);
for (i = 0; i < sizeof(MAX30001::max30001_bledata_t); i++) {
data[i] = bytePtr[i];
}
}
float HealthMonitor::read_hr()
{
uint8_t data[4];
read_ecg(data);
float t = 8.0f;
float value = 0;
float RtoR = (float)((int)data[1] << 8) + (float)data[0];
float fmStr = (float)((int)data[3] << 8) + (float)data[2];
if (fmStr == 0.0f)
t = 7.813f;
if (RtoR > 0.0f) {
value = 60000.0f / (RtoR * t);
}
return value;
}