ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
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"
50 #include "ruuvi_nrf5_sdk15_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 
76 static 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 
88 static nrf_saadc_channel_config_t channel_configs[NRF_SAADC_CHANNEL_COUNT];
89 static nrf_saadc_channel_config_t * p_channel_configs[NRF_SAADC_CHANNEL_COUNT] =
90 {
91  NULL, NULL, NULL, NULL,
92  NULL, NULL, NULL, NULL
93 };
94 static bool m_adc_is_init = false;
95 static nrf_drv_saadc_config_t adc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
96 
97 static 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 
106 static 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 
115 static 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 
124 static inline uint8_t nrf_to_bits_resolution (const nrf_saadc_resolution_t resolution)
125 {
126  return bits_resolution[resolution];
127 }
128 
132 static 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 
140 static 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 
164 static 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 
172 static 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 
180 static 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 
190 static 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 
199 static 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 
209 static 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 
218 static 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 
249 static 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 
283 static 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 
290 bool 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 
321 rd_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 
346 rd_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 
378 rd_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 
476 rd_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 
494 static 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 
527 rd_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 
543 rd_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 
559 bool 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