ruuvi.drivers.c ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
Loading...
Searching...
No Matches
ruuvi_nrf5_sdk15_adc_mcu.c
Go to the documentation of this file.
1
48#if RUUVI_NRF5_SDK15_ADC_ENABLED
49#include "ruuvi_driver_error.h"
51
52#include "nrf_drv_saadc.h"
53
54#include <string.h>
55
56#define ADC_REF_VOLTAGE_INVALID 0.0000f
57#define ADC_REF_DIVIDER_INVALID 0.0000f
58#define ADC_REF_VOLTAGE_IN_VOLTS 0.600f // Internal reference voltage.
59#define ADC_REF_EXT_VDD_DIV 4 // ADC divides VDD by 4 for reference.
60#define ADC_PRE_SCALING_COMPENSATION_1_6 6.00f
61#define ADC_PRE_SCALING_COMPENSATION_1_5 5.00f
62#define ADC_PRE_SCALING_COMPENSATION_1_4 4.00f
63#define ADC_PRE_SCALING_COMPENSATION_1_3 3.00f
64#define ADC_PRE_SCALING_COMPENSATION_1_2 2.00f
65#define ADC_PRE_SCALING_COMPENSATION_1 1.00f
66#define ADC_PRE_SCALING_COMPENSATION_2 0.50f
67#define ADC_PRE_SCALING_COMPENSATION_4 0.25f
68#define ADC_PRE_SCALING_NUM 8
69
70#define ADC_BITS_RESOLUTION_8 8
71#define ADC_BITS_RESOLUTION_10 10
72#define ADC_BITS_RESOLUTION_12 12
73#define ADC_BITS_RESOLUTION_14 14
74#define ADC_BITS_RESOLUTION_NUM 4
75
76static float pre_scaling_values[ADC_PRE_SCALING_NUM] =
77{
78 ADC_PRE_SCALING_COMPENSATION_1_6,
79 ADC_PRE_SCALING_COMPENSATION_1_5,
80 ADC_PRE_SCALING_COMPENSATION_1_4,
81 ADC_PRE_SCALING_COMPENSATION_1_3,
82 ADC_PRE_SCALING_COMPENSATION_1_2,
83 ADC_PRE_SCALING_COMPENSATION_1,
84 ADC_PRE_SCALING_COMPENSATION_2,
85 ADC_PRE_SCALING_COMPENSATION_4
86};
87
88static nrf_saadc_channel_config_t channel_configs[NRF_SAADC_CHANNEL_COUNT];
89static nrf_saadc_channel_config_t * p_channel_configs[NRF_SAADC_CHANNEL_COUNT] =
90{
91 NULL, NULL, NULL, NULL,
92 NULL, NULL, NULL, NULL
93};
94static bool m_adc_is_init = false;
95static nrf_drv_saadc_config_t adc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
96
97static uint8_t bits_resolution[ADC_BITS_RESOLUTION_NUM] =
98{
99 ADC_BITS_RESOLUTION_8, ADC_BITS_RESOLUTION_10,
100 ADC_BITS_RESOLUTION_12, ADC_BITS_RESOLUTION_14,
101};
102
106static inline nrf_saadc_oversample_t ruuvi_to_nrf_oversample (const ri_adc_oversample_t
107 oversample)
108{
109 return (nrf_saadc_oversample_t) oversample;
110}
111
115static inline nrf_saadc_resolution_t ruuvi_to_nrf_resolution (const ri_adc_resolution_t
116 resolution)
117{
118 return (nrf_saadc_resolution_t) resolution;
119}
120
124static inline uint8_t nrf_to_bits_resolution (const nrf_saadc_resolution_t resolution)
125{
126 return bits_resolution[resolution];
127}
128
132static inline nrf_saadc_input_t ruuvi_to_nrf_channel (const ri_adc_channel_t channel)
133{
134 return (nrf_saadc_input_t) channel;
135}
136
140static inline nrf_saadc_reference_t ruuvi_to_nrf_vref (const ri_adc_vref_t vref)
141{
142 nrf_saadc_reference_t nrfref = NRF_SAADC_REFERENCE_INTERNAL;
143
144 switch (vref)
145 {
147 nrfref = NRF_SAADC_REFERENCE_VDD4;
148 break;
149
151
152 // Intentional fallthrough
153 default:
154 nrfref = NRF_SAADC_REFERENCE_INTERNAL;
155 break;
156 }
157
158 return nrfref;
159}
160
164static inline nrf_saadc_mode_t ruuvi_to_nrf_mode (const ri_adc_mode_t mode)
165{
166 return (nrf_saadc_mode_t) mode;
167}
168
172static inline ri_adc_gain_t nrf_to_ruuvi_gain (const nrf_saadc_gain_t gain)
173{
174 return (ri_adc_gain_t) gain;
175}
176
180static inline ri_adc_vref_t nrf_to_ruuvi_vref (const nrf_saadc_reference_t gain)
181{
182 return (ri_adc_vref_t) gain;
183}
184
185#ifdef RI_ADC_ADV_CONFIG
186
190static inline nrf_saadc_resistor_t ruuvi_to_nrf_resistor (const nri_adc_resistor_t
191 resistor)
192{
193 return (nrf_saadc_resistor_t) resistor;
194}
195
199static inline nrf_saadc_acqtime_t ruuvi_to_nrf_acqtime (const ri_adc_acqtime_t acqtime)
200{
201 return (nrf_saadc_acqtime_t) acqtime;
202}
203
204#endif
205
209static inline nrf_saadc_gain_t ruuvi_to_nrf_gain (const ri_adc_gain_t gain)
210{
211 return (nrf_saadc_gain_t) gain;
212}
213
214
218static float raw_adc_to_volts (uint8_t channel_num,
219 ri_adc_get_data_t * p_config,
220 int16_t * adc)
221{
222 nrf_saadc_channel_config_t * p_ch_config =
223 p_channel_configs[channel_num];
224 uint16_t counts = 1 << nrf_to_bits_resolution (adc_config.resolution);
225 float result;
226
227 // Only voltages referred to internal VREF are accurate.
228 if (NRF_SAADC_REFERENCE_INTERNAL == p_ch_config->reference)
229 {
230 result = (ADC_REF_VOLTAGE_IN_VOLTS * ( (float) (*adc) / (float) counts) *
231 pre_scaling_values[ (uint8_t) nrf_to_ruuvi_gain (p_ch_config->gain)] *
232 p_config->divider);
233 }
234 // This relies on VDD accuracy and is at best indicative.
235 else
236 {
237 result = (p_config->vdd * ( (float) (*adc) / (float) counts) // Raw ADC ref VDD
238 * pre_scaling_values[ (uint8_t) nrf_to_ruuvi_gain (p_ch_config->gain)] // Prescaling
239 / ADC_REF_EXT_VDD_DIV // ADC ref prescaling
240 * p_config->divider); // External divider
241 }
242
243 return result;
244}
245
249static float raw_adc_to_ratio (uint8_t channel_num,
250 ri_adc_get_data_t * p_config,
251 int16_t * adc)
252{
253 nrf_saadc_channel_config_t * p_ch_config =
254 p_channel_configs[channel_num];
255 uint16_t counts = 1 << nrf_to_bits_resolution (adc_config.resolution);
256 float result;
257
258 // This relies on VDD accuracy and is at best indicative.
259 if (NRF_SAADC_REFERENCE_INTERNAL == p_ch_config->reference)
260 {
261 // Absolute voltage
262 result = (ADC_REF_VOLTAGE_IN_VOLTS * ( (float) (*adc) / (float) counts) *
263 pre_scaling_values[ (uint8_t) nrf_to_ruuvi_gain (p_ch_config->gain)] *
264 p_config->divider);
265 // Divided to a ratio
266 result /= p_config->vdd;
267 }
268 // Measurement referred to VDD.
269 else
270 {
271 result = ( (float) (*adc) / (float) counts);
272 }
273
274 return result;
275}
276
283static void saadc_event_handler (nrf_drv_saadc_evt_t const * p_evt)
284{
285 if (p_evt->type == NRF_DRV_SAADC_EVT_DONE)
286 {
287 }
288}
289
290bool ri_adc_is_init (void)
291{
292 return m_adc_is_init;
293}
294
296{
297 rd_status_t status = RD_SUCCESS;
298
299 if (false == ri_adc_is_init())
300 {
301 if (NULL != p_config)
302 {
303 adc_config.resolution = ruuvi_to_nrf_resolution (p_config->resolution);
304 adc_config.oversample = ruuvi_to_nrf_oversample (p_config->oversample);
305 }
306
307 if (NRF_SUCCESS == nrf_drv_saadc_init (&adc_config, saadc_event_handler))
308 {
309 m_adc_is_init = true;
310 status = RD_SUCCESS;
311 }
312 else
313 {
314 status = RD_ERROR_INVALID_STATE;
315 }
316 }
317
318 return status;
319}
320
321rd_status_t ri_adc_uninit (bool config_default)
322{
323 rd_status_t status = RD_SUCCESS;
324
325 if (true == ri_adc_is_init())
326 {
327 nrf_drv_saadc_uninit();
328
329 if (true == config_default)
330 {
331 nrf_drv_saadc_config_t def_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
332 memcpy (&adc_config, &def_config, sizeof (nrf_drv_saadc_config_t));
333
334 for (uint8_t i = 0; i < NRF_SAADC_CHANNEL_COUNT; i++)
335 {
336 p_channel_configs[i] = NULL;
337 }
338 }
339
340 m_adc_is_init = false;
341 }
342
343 return status;
344}
345
346rd_status_t ri_adc_stop (uint8_t channel_num)
347{
348 rd_status_t status = RD_SUCCESS;
349
350 if (true == ri_adc_is_init())
351 {
352 if (NRF_SAADC_CHANNEL_COUNT > channel_num)
353 {
354 nrf_drv_saadc_config_t def_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
355
356 if (NULL != p_channel_configs[ channel_num])
357 {
358 memcpy (&channel_configs[ channel_num],
359 &def_config,
360 sizeof (nrf_drv_saadc_config_t));
361 p_channel_configs[ channel_num] = NULL;
362
363 if (NRF_SUCCESS != nrf_drv_saadc_channel_uninit (channel_num))
364 {
365 status = RD_ERROR_INVALID_STATE;
366 }
367 }
368 }
369 else
370 {
371 status = RD_ERROR_INVALID_PARAM;
372 }
373 }
374
375 return status;
376}
377
378rd_status_t ri_adc_configure (uint8_t channel_num,
379 ri_adc_pins_config_t * p_pins,
380 ri_adc_channel_config_t * p_config)
381{
382 rd_status_t status = RD_SUCCESS;
383
384 if (true == ri_adc_is_init())
385 {
386 if (true == nrf_drv_saadc_is_busy())
387 {
388 nrf_drv_saadc_abort();
389 }
390
391 if ( (NULL != p_pins) && (NULL != p_config))
392 {
393 if ( (NRF_SAADC_CHANNEL_COUNT > channel_num) &&
394 (p_channel_configs[ channel_num] == NULL))
395 {
396 nrf_saadc_channel_config_t ch_config;
397#ifdef RI_ADC_ADV_MODE_CONFIG
398
399 if (RI_ADC_MODE_DIFFERENTIAL == p_config->mode)
400 {
401 nrf_saadc_channel_config_t def_config = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL (
402 ruuvi_to_nrf_channel (p_pins->p_pin.channel),
403 ruuvi_to_nrf_channel (p_pins->n_pin.channel));
404 memcpy (&ch_config, &def_config, sizeof (nrf_saadc_channel_config_t));
405#ifdef RI_ADC_ADV_CONFIG
406 ch_config.resistor_p = ruuvi_to_nrf_resistor (p_pins->p_pin.resistor);
407 ch_config.resistor_n = ruuvi_to_nrf_resistor (p_pins->n_pin.resistor);
408#endif
409 }
410 else
411 {
412#endif
413 nrf_saadc_channel_config_t def_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE (
414 ruuvi_to_nrf_channel (p_pins->p_pin.channel));
415 memcpy (&ch_config, &def_config, sizeof (nrf_saadc_channel_config_t));
416#ifdef RI_ADC_ADV_CONFIG
417 ch_config.resistor_p = ruuvi_to_nrf_resistor (p_pins->p_pin.resistor);
418#endif
419#ifdef RI_ADC_ADV_MODE_CONFIG
420 }
421
422#endif
423 ch_config.reference = ruuvi_to_nrf_vref (p_config->vref);
424#ifdef RI_ADC_ADV_CONFIG
425 ch_config.acq_time = ruuvi_to_nrf_acqtime (p_config->acqtime);
426#else
427
428 // Use 1/6 gain for internal reference and 1/4 gain for external reference.
429 // This allows ADC to use maximum non-saturated scale.
430 if (NRF_SAADC_REFERENCE_INTERNAL == ch_config.reference)
431 {
432 p_config->gain = RI_ADC_GAIN1_6;
433 }
434 else
435 {
436 p_config->gain = RI_ADC_GAIN1_4;
437 }
438
439#endif
440 ch_config.gain = ruuvi_to_nrf_gain (p_config->gain);
441 memcpy (&channel_configs[ channel_num],
442 &ch_config,
443 sizeof (nrf_saadc_channel_config_t));
444 p_channel_configs[ channel_num] =
445 &channel_configs[ channel_num];
446
447 for (uint8_t i = 0; i < NRF_SAADC_CHANNEL_COUNT; i++)
448 {
449 if (NULL != p_channel_configs[i])
450 {
451 if (NRF_SUCCESS != nrf_drv_saadc_channel_init (i, &channel_configs[i]))
452 {
453 status |= RD_ERROR_INVALID_STATE;
454 }
455 }
456 }
457 }
458 else
459 {
460 status = RD_ERROR_INVALID_PARAM;
461 }
462 }
463 else
464 {
465 status = RD_ERROR_NULL;
466 }
467 }
468 else
469 {
470 status = RD_ERROR_INVALID_STATE;
471 }
472
473 return status;
474}
475
476rd_status_t ri_adc_get_raw_data (uint8_t channel_num,
477 int16_t * p_data)
478{
480 nrf_saadc_value_t adc_buf;
481
482 if (NRF_SUCCESS == nrf_drv_saadc_sample_convert (channel_num, &adc_buf))
483 {
484 (*p_data) = (int16_t) (adc_buf);
485 status = RD_SUCCESS;
486 }
487
488 return status;
489}
490
494static rd_status_t nrf5_adc_get_raw (uint8_t channel_num,
495 ri_adc_get_data_t * p_config,
496 int16_t * const p_data)
497{
498 rd_status_t status = RD_SUCCESS;
499
500 if (NULL == p_config || NULL == p_data)
501 {
502 status |= RD_ERROR_NULL;
503 }
504 else
505 {
506 nrf_saadc_channel_config_t * p_ch_config =
507 p_channel_configs[channel_num];
508
509 if ( (NULL == p_ch_config) ||
510 (p_config->vdd == ADC_REF_VOLTAGE_INVALID) ||
511 (p_config->divider == ADC_REF_DIVIDER_INVALID) ||
512 (isnan (p_config->divider)) ||
513 (isnan (p_config->vdd) && (RI_ADC_VREF_EXTERNAL ==
514 nrf_to_ruuvi_vref (p_ch_config->reference))))
515 {
516 status |= RD_ERROR_INVALID_PARAM;
517 }
518 else
519 {
520 status |= ri_adc_get_raw_data (channel_num, p_data);
521 }
522 }
523
524 return status;
525}
526
527rd_status_t ri_adc_get_data_absolute (uint8_t channel_num,
528 ri_adc_get_data_t * p_config,
529 float * p_data)
530{
531 int16_t data;
532 // Input check in function.
533 rd_status_t status = nrf5_adc_get_raw (channel_num, p_config, &data);
534
535 if (RD_SUCCESS == status)
536 {
537 (*p_data) = raw_adc_to_volts (channel_num, p_config, &data);
538 }
539
540 return status;
541}
542
543rd_status_t ri_adc_get_data_ratio (uint8_t channel_num,
544 ri_adc_get_data_t * p_config,
545 float * p_data)
546{
547 int16_t data;
548 // Input check in function.
549 rd_status_t status = nrf5_adc_get_raw (channel_num, p_config, &data);
550
551 if (RD_SUCCESS == status)
552 {
553 (*p_data) = raw_adc_to_ratio (channel_num, p_config, &data);
554 }
555
556 return status;
557}
558
559bool ri_adc_mcu_is_valid_ch (const uint8_t ch)
560{
561 return ch < NRF_SAADC_CHANNEL_COUNT;
562}
563
564#endif
#define RD_ERROR_INVALID_PARAM
Invalid Parameter.
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
Interface for controlling ADC onboard MCU.
rd_status_t ri_adc_uninit(bool config_default)
Uninitialize ADC.
rd_status_t ri_adc_get_raw_data(uint8_t channel_num, int16_t *p_data)
Get raw ADC data.
@ RI_ADC_VREF_EXTERNAL
External voltage reference.
@ RI_ADC_VREF_INTERNAL
Internal voltage reference.
rd_status_t ri_adc_get_data_ratio(uint8_t channel_num, ri_adc_get_data_t *p_config, float *p_data)
Get ADC data in volts.
rd_status_t ri_adc_get_data_absolute(uint8_t channel_num, ri_adc_get_data_t *p_config, float *p_data)
Get ADC data in volts.
ri_adc_channel_t
Enable implementation selected by application.
bool ri_adc_mcu_is_valid_ch(const uint8_t ch)
Return true if given channel index can be used by underlying implementation.
rd_status_t ri_adc_stop(uint8_t channel_num)
Stop use ADC channel.
rd_status_t ri_adc_configure(uint8_t channel_num, ri_adc_pins_config_t *p_pins, ri_adc_channel_config_t *p_config)
Configure ADC channel.
rd_status_t ri_adc_init(ri_adc_config_t *p_config)
Initialization of ADC.
bool ri_adc_is_init(void)
Check if ADC is initialized.
ri_adc_oversample_t oversample
ri_adc_resolution_t resolution