YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
max30102_drv.cpp
1// ---------------------------------------------
2// This file is part of
3// _ _ __ _ _ __ __
4// ( \/ ) /__\ ( )_( ) /__\ ( )
5// \ / /(__)\ ) _ ( /(__)\ )(__
6// (__)(__)(__)(_) (_)(__)(__)(____)
7//
8// Yet Another HW Abstraction Library
9// Copyright (C) Andreas Terstegge
10// BSD Licensed (see file LICENSE)
11//
12// ---------------------------------------------
13
14#include "max30102_drv.h"
15#include "task.h"
16#include <cstdint>
17#include <cassert>
18
19namespace MAX30102 {
20 // MAX30102 Registers
22 // Status Registers
23 uint8_t INT_STAT_1 = 0x00;
24 uint8_t INT_STAT_2 = 0x01;
25 uint8_t INT_ENABLE_1 = 0x02;
26 uint8_t INT_ENABLE_2 = 0x03;
27 // FIFO Registers
28 uint8_t FIFO_WRITE_PTR = 0x04;
29 uint8_t FIFO_OVF_COUNTER = 0x05;
30 uint8_t FIFO_READ_PTR = 0x06;
31 uint8_t FIFO_DATA = 0x07;
32 // Configuration Registers
33 uint8_t FIFO_CONFIG = 0x08;
34 uint8_t MODE_CONFIG = 0x09;
35 uint8_t SPO2_CONFIG = 0x0A;
36 uint8_t LED1_PULSE_AMP = 0x0C;
37 uint8_t LED2_PULSE_AMP = 0x0D;
38 // Multi-LED Mode Control Registers
39 uint8_t MUL_LED_CTRL_1 = 0x11;
40 uint8_t MUL_LED_CTRL_2 = 0x12;
41 // Die Temperature Registers
42 uint8_t DIE_TEMP_INT = 0x1F;
43 uint8_t DIE_TEMP_FRAC = 0x20;
44 uint8_t DIE_TEMP_CFG = 0x21;
45 // Part ID Registers
46 uint8_t REVISION_ID = 0xFE;
47 uint8_t PART_ID = 0xFF;
48
49 // Mask values
50 uint8_t INT_A_FULL_MASK = 0x80;
51 uint8_t INT_DATA_RDY_MASK = 0x40;
52 uint8_t INT_ALC_OVF_MASK = 0x20;
53 uint8_t INT_DIE_TEMP_RDY_MASK = 0x02;
54 uint8_t ROLLOVER_MASK = 0x10;
55 uint8_t A_FULL_MASK = 0x0f;
56 uint8_t SHUTDOWN_MASK = 0x80;
57 uint8_t RESET_MASK = 0x40;
58 uint8_t SAMPLE_AVG_MASK = 0xe0;
59 uint8_t MODE_MASK = 0x07;
60 uint8_t ADC_RANGE_MASK = 0x60;
61 uint8_t SAMPLE_RATE_MASK = 0x1c;
62 uint8_t PULSE_WIDTH_MASK = 0x03;
63 uint8_t SLOT1_MASK = 0x07;
64 uint8_t SLOT2_MASK = 0x70;
65 uint8_t SLOT3_MASK = 0x07;
66 uint8_t SLOT4_MASK = 0x70;
67 uint8_t TEMP_EN = 0x01;
68
69 // The hardcoded part ID for MAX30102
70 uint8_t EXPECTED_PART_ID = 0x15;
71}
72
73max30102_drv::max30102_drv(i2c_interface & i2c, uint8_t i2c_addr)
74: _i2c(i2c), _i2c_addr(i2c_addr), _data {10,10,10,10},
75 _active_slots(0), _pulse_width(0) {
76}
77
78void max30102_drv::setup_sensor(MAX30102::SAMPLE_AVG sample_avg,
79 MAX30102::LED_MODE led_mode,
80 MAX30102::ADC_RANGE adc_range,
81 MAX30102::SAMPLE_RATE sample_rate,
82 MAX30102::PULSE_WIDTH pulse_width,
83 uint8_t pulse_amp) {
84 // Check if the sensor is available
85 assert(check_part_id());
86 reset();
87 set_fifo_average(sample_avg);
88 enable_fifo_rollover(true);
89 set_led_mode(led_mode);
90 set_adc_range(adc_range);
91 set_sample_rate(sample_rate);
92 set_pulse_width(pulse_width);
93 set_pulse_amplitude_led1(pulse_amp);
94 set_pulse_amplitude_led2(pulse_amp);
95 enable_int_die_temp_rdy(true);
96}
97
98void max30102_drv::set_fifo_average(MAX30102::SAMPLE_AVG samples) {
99 set_register_mask_value(MAX30102::FIFO_CONFIG,
100 MAX30102::SAMPLE_AVG_MASK,
101 samples);
102}
103
104void max30102_drv::set_led_mode(MAX30102::LED_MODE mode) {
105 if (mode == MAX30102::LED1_ONLY) {
106 _active_slots = 1;
107 }
108 else if (mode == MAX30102::BOTH_LEDS) {
109 _active_slots = 2;
110 }
111 set_register_mask_value(MAX30102::MODE_CONFIG,
112 MAX30102::MODE_MASK,
113 mode);
114}
115
116void max30102_drv::set_adc_range(MAX30102::ADC_RANGE adc_range) {
117 set_register_mask_value(MAX30102::SPO2_CONFIG,
118 MAX30102::ADC_RANGE_MASK,
119 adc_range);
120}
121
122void max30102_drv::set_sample_rate(MAX30102::SAMPLE_RATE rate) {
123 set_register_mask_value(MAX30102::SPO2_CONFIG,
124 MAX30102::SAMPLE_RATE_MASK,
125 rate);
126}
127
128void max30102_drv::set_pulse_width(MAX30102::PULSE_WIDTH width) {
129 _pulse_width = width;
130 set_register_mask_value(MAX30102::SPO2_CONFIG,
131 MAX30102::PULSE_WIDTH_MASK,
132 width);
133}
134
135uint8_t max30102_drv::slot_data_available(uint8_t slot) {
136 return _data[slot].available_get();
137}
138
139uint32_t max30102_drv::get_slot_data(uint8_t slot) {
140 uint32_t val = 0;
141 _data[slot].get(val);
142 return val;
143}
144
145bool max30102_drv::check() {
146 // Get number of available samples
147 int8_t number_of_samples = get_write_pointer() - get_read_pointer();
148 // Handle wrap-around case
149 if (number_of_samples < 0) number_of_samples += 32;
150 // Do we have new data?
151 if (number_of_samples) {
152 // Loop over all samples
153 uint8_t buf[12];
154 for (uint8_t sample = 0; sample < number_of_samples; ++sample) {
155 // Read data from FIFO for one sample (_active_slots * 3 bytes)
156 buf[0] = MAX30102::FIFO_DATA;
157 _i2c.i2cWrite(_i2c_addr, buf, 1, false);
158 _i2c.i2cRead (_i2c_addr, buf, _active_slots * 3);
159 // Loop of all active slots
160 for(uint8_t slot=0; slot < _active_slots; ++slot) {
161 uint8_t offset = slot * 3;
162 uint32_t value = 0;
163 value |= buf[offset];
164 value <<= 8;
165 value |= buf[offset+1];
166 value <<= 8;
167 value |= buf[offset+2];
168 // Mask away upper bits - they can contain garbage
169 // Shift the result according to the ADC resolution
170 value = (value & 0x3ffff) >> (3 - _pulse_width);
171 _data[slot].put(value);
172 }
173 }
174 return true;
175 } else {
176 return false;
177 }
178}
179
180uint8_t max30102_drv::get_int_status_1() {
181 return read_register(MAX30102::INT_STAT_1);
182}
183
184uint8_t max30102_drv::get_int_status_2() {
185 return read_register(MAX30102::INT_STAT_2);
186}
187
188void max30102_drv::enable_int_a_full(bool on) {
189 if (on) {
190 set_register_mask(MAX30102::INT_ENABLE_1,
191 MAX30102::INT_A_FULL_MASK);
192 } else {
193 clear_register_mask(MAX30102::INT_ENABLE_1,
194 MAX30102::INT_A_FULL_MASK);
195 }
196}
197
198void max30102_drv::enable_int_data_rdy(bool on) {
199 if (on) {
200 set_register_mask(MAX30102::INT_ENABLE_1,
201 MAX30102::INT_DATA_RDY_MASK);
202 } else {
203 clear_register_mask(MAX30102::INT_ENABLE_1,
204 MAX30102::INT_DATA_RDY_MASK);
205 }
206}
207
208void max30102_drv::enable_int_alc_ovf(bool on) {
209 if (on) {
210 set_register_mask(MAX30102::INT_ENABLE_1,
211 MAX30102::INT_ALC_OVF_MASK);
212 } else {
213 clear_register_mask(MAX30102::INT_ENABLE_1,
214 MAX30102::INT_ALC_OVF_MASK);
215 }
216}
217
218void max30102_drv::enable_int_die_temp_rdy(bool on) {
219 if (on) {
220 set_register_mask(MAX30102::INT_ENABLE_2,
221 MAX30102::INT_DIE_TEMP_RDY_MASK);
222 } else {
223 clear_register_mask(MAX30102::INT_ENABLE_2,
224 MAX30102::INT_DIE_TEMP_RDY_MASK);
225 }
226}
227
228void max30102_drv::enable_fifo_rollover(bool on) {
229 if (on) {
230 set_register_mask(MAX30102::FIFO_CONFIG,
231 MAX30102::ROLLOVER_MASK);
232 } else {
233 clear_register_mask(MAX30102::FIFO_CONFIG,
234 MAX30102::ROLLOVER_MASK);
235 }
236}
237
238void max30102_drv::set_fifo_almost_full(uint8_t samples) {
239 assert(samples >= 17 && samples <=32);
240 set_register_mask_value(MAX30102::FIFO_CONFIG,
241 MAX30102::A_FULL_MASK,
242 32-samples);
243}
244
245void max30102_drv::shutdown(bool on) {
246 if (on) {
247 set_register_mask(MAX30102::MODE_CONFIG,
248 MAX30102::SHUTDOWN_MASK);
249
250 } else {
251 clear_register_mask(MAX30102::MODE_CONFIG,
252 MAX30102::SHUTDOWN_MASK);
253 }
254}
255
256void max30102_drv::reset() {
257 set_register_mask(MAX30102::MODE_CONFIG, MAX30102::RESET_MASK);
258 uint8_t reset_bit = get_register_mask(MAX30102::MODE_CONFIG,
259 MAX30102::RESET_MASK);
260 while (reset_bit) {
261 task::sleep_ms(10);
262 reset_bit = get_register_mask(MAX30102::MODE_CONFIG,
263 MAX30102::RESET_MASK);
264 }
265}
266
267void max30102_drv::set_pulse_amplitude_led1(uint8_t amplitude) {
268 write_register(MAX30102::LED1_PULSE_AMP, amplitude);
269}
270
271void max30102_drv::set_pulse_amplitude_led2(uint8_t amplitude) {
272 write_register(MAX30102::LED2_PULSE_AMP, amplitude);
273}
274
275void max30102_drv::config_slots(uint8_t slot1, uint8_t slot2,
276 uint8_t slot3, uint8_t slot4) {
277 _active_slots = 0;
278 if (slot1 != MAX30102::SLOT_NONE) {
279 set_register_mask_value(MAX30102::MUL_LED_CTRL_1,
280 MAX30102::SLOT1_MASK, slot1);
281 _active_slots += 1;
282 }
283 if (slot2 != MAX30102::SLOT_NONE) {
284 set_register_mask_value(MAX30102::MUL_LED_CTRL_1,
285 MAX30102::SLOT2_MASK, slot2 << 4);
286 _active_slots += 1;
287 }
288 if (slot3 != MAX30102::SLOT_NONE) {
289 set_register_mask_value(MAX30102::MUL_LED_CTRL_2,
290 MAX30102::SLOT3_MASK, slot3);
291 _active_slots += 1;
292 }
293 if (slot4 != MAX30102::SLOT_NONE) {
294 set_register_mask_value(MAX30102::MUL_LED_CTRL_2,
295 MAX30102::SLOT4_MASK, slot4 << 4);
296 _active_slots += 1;
297 }
298}
299
300float max30102_drv::read_temperature() {
301 // Trigger a new temperature reading
302 write_register(MAX30102::DIE_TEMP_CFG, MAX30102::TEMP_EN);
303 // Wait until rdy-interrupt is raised
304 uint8_t rdy_bit = read_register(MAX30102::INT_STAT_2);
305 while (!rdy_bit) {
306 task::sleep_ms(5);
307 rdy_bit = read_register(MAX30102::INT_STAT_2);
308 }
309 uint8_t tempInt = read_register(MAX30102::DIE_TEMP_INT);
310 // Reading the fractional part will also clear the IRQ flag
311 uint8_t tempFrac = read_register(MAX30102::DIE_TEMP_FRAC);
312 return (float)tempInt + (float)tempFrac * 0.0625;
313}
314
315uint8_t max30102_drv::read_part_id() {
316 return read_register(MAX30102::PART_ID);
317}
318
319bool max30102_drv::check_part_id() {
320 return read_part_id() == MAX30102::EXPECTED_PART_ID;
321}
322
323uint8_t max30102_drv::get_revision_id() {
324 return read_register(MAX30102::REVISION_ID);
325}
326
327uint8_t max30102_drv::get_write_pointer() {
328 return read_register(MAX30102::FIFO_WRITE_PTR);
329}
330
331uint8_t max30102_drv::get_ovf_counter() {
332 return read_register(MAX30102::FIFO_OVF_COUNTER);
333}
334
335uint8_t max30102_drv::get_read_pointer() {
336 return read_register(MAX30102::FIFO_READ_PTR);
337}
338
339uint8_t max30102_drv::read_register(uint8_t reg) {
340 uint8_t buf[1];
341 buf[0] = reg;
342 _i2c.i2cWrite(_i2c_addr, buf, 1, false);
343 _i2c.i2cRead (_i2c_addr, buf, 1);
344 return buf[0];
345}
346
347void max30102_drv::write_register(uint8_t reg, uint8_t data) {
348 uint8_t buf[2];
349 buf[0] = reg;
350 buf[1] = data;
351 _i2c.i2cWrite(_i2c_addr, buf, 2);
352}
353
354uint8_t max30102_drv::get_register_mask(uint8_t reg, uint8_t mask) {
355 return read_register(reg) & mask;
356}
357
358void max30102_drv::set_register_mask(uint8_t reg, uint8_t mask) {
359 // Read-Modify-Write cycle
360 write_register(reg, read_register(reg) | mask);
361}
362
363void max30102_drv::set_register_mask_value(uint8_t reg, uint8_t mask, uint8_t val) {
364 // Read-Modify-Write cycle
365 write_register(reg, (read_register(reg) & ~mask) | (val & mask));
366}
367
368void max30102_drv::clear_register_mask(uint8_t reg, uint8_t mask) {
369 write_register(reg, read_register(reg) & ~mask);
370}
371