nrfcxx  0.1.0
C++-17 Framework for Nordic nRF5 Devices
adc.hpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 /* Copyright 2017-2019 Peter A. Bigot */
3 
8 #ifndef NRFCXX_SENSOR_ADC_HPP
9 #define NRFCXX_SENSOR_ADC_HPP
10 #pragma once
11 
12 #include <cstring>
13 #include <initializer_list>
14 
15 #include <nrfcxx/periph.hpp>
16 #include <nrfcxx/lpm.hpp>
17 
18 namespace nrfcxx {
19 namespace sensor {
20 
23 namespace adc {
24 
27 class vdd : public periph::ADCClient
28 {
29  using super = periph::ADCClient;
30 
31 public:
44  using vdd_callback_bi = std::function<void(unsigned int vdd_mV)>;
45 
52  vdd (vdd_callback_bi callback = nullptr) :
53  super{},
54  vdd_callback_{callback},
55  vdd_adc16_{}
56  { }
57 
63  int convert_adc16_mV (unsigned int vdd_adc16) const;
64 
71  unsigned int vdd_mV () const
72  {
73  return convert_adc16_mV(vdd_adc16_);
74  }
75 
76 private:
77  int configure_bi_ () override;
78  void complete_bi_ () override;
79 
80  vdd_callback_bi vdd_callback_;
81  volatile uint16_t vdd_adc16_;
82 };
83 
97 {
99 
100  using packed_type = uint32_t;
101  static constexpr auto AIN_WIDTH = 4U;
102  static constexpr packed_type AIN_MASK = (1U << AIN_WIDTH) - 1;
103 
105  static constexpr auto MAX_INTERNAL = 2;
106 
108  enum flags_enum : flags_type {
109 
112  FL_RAW_EXTERNAL = (FL_SUBCLASS_BASE << 0),
113 
119  FL_MEASURE_RESISTANCE = (FL_SUBCLASS_BASE << 1),
120  };
121 
124  template <typename Iterator>
125  int8_t pack_values (packed_type& dest,
126  Iterator begin,
127  Iterator end)
128  {
129  static constexpr int8_t max_count = (8 * sizeof(dest)) / AIN_WIDTH;
130  int8_t count = 0;
131  unsigned int shift = 0;
132 
133  dest = 0;
134  while ((begin != end) && (count < max_count)) {
135  dest |= (AIN_MASK & *begin) << shift;
136  shift += AIN_WIDTH;
137  ++begin;
138  ++count;
139  }
140  if (begin != end) {
141  count = -1;
142  }
143  return count;
144  }
145 
146  volatile uint16_t* raw_adc16_ ()
147  {
148  if (FL_RAW_EXTERNAL & flags_) {
149  return raw_.ptr;
150  }
151  return raw_.val;
152  }
153 
154  volatile const uint16_t* raw_adc16_ () const
155  {
156  if (FL_RAW_EXTERNAL & flags_) {
157  return raw_.ptr;
158  }
159  return raw_.val;
160  }
161 
162  void configure_instance_ (int8_t channel_count,
163  volatile uint16_t* raw);
164 
165 public:
166 
172  const unsigned int r1_Ohm;
173 
179  const unsigned int r2_Ohm;
180 
196  template <typename Iterator>
197  voltage_divider (unsigned int r1_Ohm,
198  unsigned int r2_Ohm,
199  Iterator ains_begin,
200  Iterator ains_end,
201  volatile uint16_t* raw = nullptr) :
202  r1_Ohm{r1_Ohm},
203  r2_Ohm{r2_Ohm}
204  {
205  configure_instance_(pack_values(channel_ains_, ains_begin, ains_end), raw);
206  }
207 
210  voltage_divider (unsigned int r1_Ohm,
211  unsigned int r2_Ohm,
212  uint8_t ain) :
213  voltage_divider{r1_Ohm, r2_Ohm, &ain, 1 + &ain}
214  { }
215 
217  voltage_divider (unsigned int r1_Ohm,
218  unsigned int r2_Ohm,
219  std::initializer_list<uint8_t> ains,
220  volatile uint16_t* raw = nullptr) :
221  voltage_divider{r1_Ohm, r2_Ohm, ains.begin(), ains.end(), raw}
222  { }
223 
225  size_t count () const
226  {
227  return channel_count_;
228  }
229 
236  int channel_ain (size_t ci) const
237  {
238  int rv = -1;
239  if (ci < channel_count_) {
240  rv = AIN_MASK & (channel_ains_ >> (ci * AIN_WIDTH));
241  }
242  return rv;
243  }
244 
252  int sample_adc16 (size_t ci = 0) const
253  {
254  int rv = -1;
255  if (ci < channel_count_) {
256  rv = raw_adc16_()[ci];
257  }
258  return rv;
259  }
260 
267  int sample_mV (size_t ci = 0,
268  bool truncate = false) const;
269 
277  int input_mV (size_t ci = 0) const;
278 
290  virtual int measurement_mV (uint16_t m16) const
291  {
292 #if (NRF51 - 0)
293  return (3 * peripheral::VBG_mV * m16) >> 16;
294 #else
295  return (6 * peripheral::VBG_mV * m16) >> 16;
296 #endif
297  }
298 
306  int measurement_Ohm (uint16_t m16) const
307  {
308  if (0 == m16) {
309  return -1;
310  }
311  if (0 == r1_Ohm) {
312  return r2_Ohm * ((uint64_t{1} << 16) - m16) / m16;
313  }
314  return r1_Ohm * static_cast<uint64_t>(m16) / ((1U << 16) - m16);
315  }
316 
331  int sample_Ohm (size_t ci = 0,
332  bool filter_high_negative = false) const;
333 
334 private:
335 
337  uint32_t channel_ains_ = 0;
338 
340  uint8_t channel_count_ = 0;
341 
344  uint8_t channel_idx_ = 0;
345 
347  uint32_t config_ = 0;
348 
355  union {
356  volatile uint16_t* ptr;
357  uint16_t volatile val[MAX_INTERNAL];
358  } raw_ = {nullptr};
359 
360  int configure_bi_ () override;
361  int nrf51_next_bi_ (size_t ci) override;
362 };
363 
381 {
382 public:
404  constexpr SteinhartHart (float a = 1.19438315714902408e-03,
405  float b = 2.19852114398042317e-04,
406  float c = 1.72427510143621963e-07,
407  uint16_t open_adc16 = 2281,
408  uint16_t short_adc16 = 65133) :
409  a{a},
410  b{b},
411  c{c},
414  { }
415 
417  const float a;
418 
420  const float b;
421 
423  const float c;
424 
432  const uint16_t open_adc16;
433 
441  const uint16_t short_adc16;
442 
451  int temperature_cK (unsigned int therm_Ohm) const;
452 };
453 
456 namespace steinhartHart {
464 
475 extern const SteinhartHart adafruit372hvac;
476 
488 } // ns steinhartHart
489 
497 {
498  using super = voltage_divider;
499 
500 public:
502  static constexpr int ABSOLUTE_ZERO_cCel = -27315;
503 
516  static constexpr int INVALID_cCel = 30000;
517 
528  static constexpr int SHORT_cCel = INVALID_cCel + 1;
529 
540  static constexpr int OPEN_cCel = - SHORT_cCel;
541 
555  template <typename Iterator>
556  ntcThermistor(Iterator ains_begin,
557  Iterator ains_end,
558  volatile uint16_t*raw = nullptr,
560  unsigned int ref_Ohm = 10000) :
561  super{0, ref_Ohm, ains_begin, ains_end, raw},
562  steinhartHart{coeff}
563  { }
564 
566  ntcThermistor(std::initializer_list<uint8_t> ains,
567  volatile uint16_t*raw = nullptr,
569  unsigned int ref_Ohm = 10000) :
570  ntcThermistor(ains.begin(), ains.end(), raw, coeff, ref_Ohm)
571  { }
572 
583  int convert_adc16_cCel (unsigned int therm_adc16);
584 
590  int temperature_cCel (size_t ci = 0);
591 
593  unsigned int therm_adc16 (size_t ci = 0) const;
594 
596  unsigned int therm_Ohm (size_t ci = 0) const;
597 
601 };
602 
627 {
629 
630  /* State transitions:
631  *
632  * * ENTRY_START transitions to IDLE.
633  *
634  * * ENTRY_CALIBRATE claims the ADC, invokes sample_setup_(), and
635  * transitions to CALIBRATE (after a setup delay if necessary).
636  *
637  * * CALIBRATE queues an ADC calibration and falls into
638  * EXIT_CALIBRATE.
639  *
640  * * EXIT_CALIBRATE loops until calibration resolved, then records
641  * state of calibration and transitions to either IDLE (invoking
642  * sample_teardown_() and releasing the ADC) or ENTRY_SAMPLE
643  * depending on the source of the calibration request.
644  *
645  * * ENTRY_SAMPLE if not calibrated jumps to ENTRY_CALIBRATE.
646  * Otherwise it claims the ADC, invokes sample_setup_(), and
647  * transitions to SAMPLE (after a setup delay if necessary).
648  *
649  * * SAMPLE queues an ADC collection and transitions to EXIT_SAMPLE.
650  *
651  * * EXIT_SAMPLE is blocked until the ADC completes, then it invokes
652  * sample_teardown_() and processes the sample, emitting
653  * #PF_OBSERVATION if the collection was valid. It then release
654  * the ADC and transitions to IDLE.
655  *
656  * Calibration and sampling are queued ADCClient operations.
657  * Failure to successfully initiate the queued operation translates
658  * to a machine error.
659  *
660  * NOTE: The design of this machine is influenced by the inability
661  * to queue multiple operations on the same client. As a result the
662  * calibrate and sample operations cannot be made to occur within
663  * the same claim session. This is a problem only when multiple
664  * clients are managed with distinct LPSM wrappers, and calibration
665  * may be performed with both single-ended and differential sampling
666  * configurations producing different offsets, as documented in the
667  * devzone posting below.
668  *
669  * See: https://devzone.nordicsemi.com/f/nordic-q-a/43316/nrf52832-saadc-configuration-effects-on-calibration */
670  static constexpr auto MS_ENTRY_CALIBRATE = lpm::state_machine::MS_ENTRY_SAMPLE + 1;
671  static constexpr auto MS_CALIBRATE = lpm::state_machine::MS_SAMPLE + 1;
672  static constexpr auto MS_EXIT_CALIBRATE = lpm::state_machine::MS_EXIT_SAMPLE + 1;
673 
675  using flags_type = uint8_t;
676 
678  enum flags_enum : flags_type
679  {
682  FL_CALIBRATING = 0x01,
683 
686  FL_SAMPLING = 0x02,
687 
690  FL_PENDING = 0x08,
691 
694  FL_CALIBRATED = 0x10,
695 
698  FL_THEN_SAMPLE = 0x20,
699  };
700 
701 public:
704 
708  static constexpr auto PF_CALIBRATED = lpm::state_machine::PF_APP_BASE << 0;
709 
714  periph::ADCClient& client) :
715  super_lpsm{notify},
716  client_{client}
717  { }
718 
719  template <typename Client = periph::ADCClient>
720  Client& client () const
721  {
722  return reinterpret_cast<Client&>(client_);
723  }
724 
729  int lpsm_calibrate ();
730 
739 
740 private:
741  void lpsm_reset_ () override
742  {
743  mutex_type mutex;
744  flags_bi_ = 0;
745  }
746 
747  int lpsm_process_ (int& delay,
748  process_flags_type& lpf) override;
749 
750  periph::ADCClient& client_;
751  int queue_rc_ = 0;
752  flags_type flags_bi_ = 0;
753 };
754 
755 } // ns adc
756 } // ns sensor
757 } // ns nrfcxx
758 
759 #endif /* NRFCXX_SENSOR_ADC_HPP */
nrfcxx::sensor::adc::lpsm_wrapper::lpsm_wrapper
lpsm_wrapper(notifier_type notify, periph::ADCClient &client)
Construct an instance.
Definition: adc.hpp:713
nrfcxx::sensor::adc::vdd::convert_adc16_mV
int convert_adc16_mV(unsigned int vdd_adc16) const
Convert a measured voltage to VDD voltage.
nrfcxx::sensor::adc::steinhartHart::adafruit372fullScale
const SteinhartHart adafruit372fullScale
Constants for full-scale measurement with Adafruit thermistor.
nrfcxx::sensor::adc::SteinhartHart::SteinhartHart
constexpr SteinhartHart(float a=1.19438315714902408e-03, float b=2.19852114398042317e-04, float c=1.72427510143621963e-07, uint16_t open_adc16=2281, uint16_t short_adc16=65133)
Store the coefficients and reference values.
Definition: adc.hpp:404
nrfcxx::sensor::adc::ntcThermistor::SHORT_cCel
static constexpr int SHORT_cCel
Measurement returned by temperature_cCel() for a shorted thermistor.
Definition: adc.hpp:528
nrfcxx::sensor::adc::ntcThermistor::steinhartHart
const SteinhartHart *const steinhartHart
The Steinhart-Hart coefficients and extreme values for the thermistor in the circuit.
Definition: adc.hpp:600
nrfcxx::sensor::adc::ntcThermistor::INVALID_cCel
static constexpr int INVALID_cCel
An flag value for invalid temperatures.
Definition: adc.hpp:516
nrfcxx::sensor::adc::voltage_divider::sample_Ohm
int sample_Ohm(size_t ci=0, bool filter_high_negative=false) const
Retrieve the latest estimated resistance for a channel.
nrfcxx::periph::ADCClient::flags_type
unsigned int flags_type
Type for client-specific flags.
Definition: periph.hpp:2127
nrfcxx::sensor::adc::ntcThermistor::temperature_cCel
int temperature_cCel(size_t ci=0)
Convert the stored measured voltage to a thermisor.
nrfcxx::sensor::adc::voltage_divider::sample_mV
int sample_mV(size_t ci=0, bool truncate=false) const
Retrieve the latest output voltage measurement for a channel.
nrfcxx::sensor::adc::lpsm_wrapper::lpsm_bypass_calibration
int lpsm_bypass_calibration()
Set the internal flag that indicates calibration has been performed.
nrfcxx::periph::ADCClient::flags_
flags_type flags_
Flags for use by the core infrastructure and client specifications.
Definition: periph.hpp:2354
nrfcxx::sensor::adc::ntcThermistor::therm_Ohm
unsigned int therm_Ohm(size_t ci=0) const
Return the estimated thermistor resistance, in Ohms.
nrfcxx::sensor::adc::voltage_divider::measurement_mV
virtual int measurement_mV(uint16_t m16) const
Convert a raw ADC measurement to mV.
Definition: adc.hpp:290
nrfcxx::sensor::adc::SteinhartHart
Type holding Steinhart-Hart coefficients and extreme values.
Definition: adc.hpp:380
lpm.hpp
Material supporting low-power-mode operations.
nrfcxx::sensor::adc::lpsm_wrapper::lpsm_calibrate
int lpsm_calibrate()
Initiate a calibration.
nrfcxx::sensor::adc::vdd::vdd
vdd(vdd_callback_bi callback=nullptr)
Construct an instance that measures board Vdd.
Definition: adc.hpp:52
nrfcxx::sensor::adc::voltage_divider::r1_Ohm
const unsigned int r1_Ohm
The resistance of the upper leg of the voltage divider, connected to the input voltage.
Definition: adc.hpp:172
nrfcxx::sensor::adc::ntcThermistor
Class supporting thermistor measurements.
Definition: adc.hpp:496
nrfcxx::mutex_irq
nvic_BlockIRQ as a template type.
Definition: core.hpp:497
nrfcxx::sensor::adc::SteinhartHart::a
const float a
Coefficient of ln(R)^0.
Definition: adc.hpp:417
nrfcxx::sensor::adc::voltage_divider::channel_ain
int channel_ain(size_t ci) const
Extract the channel associated with the provided index.
Definition: adc.hpp:236
nrfcxx::lpm::state_machine::MS_SAMPLE
static constexpr state_type MS_SAMPLE
States available to implement a sample state.
Definition: lpm.hpp:192
nrfcxx::sensor::adc::vdd::vdd_mV
unsigned int vdd_mV() const
Return the result of the most recent measurement.
Definition: adc.hpp:71
nrfcxx::sensor::adc::ntcThermistor::OPEN_cCel
static constexpr int OPEN_cCel
Measurement returned by temperature_cCel() for an open thermistor.
Definition: adc.hpp:540
nrfcxx::sensor::adc::ntcThermistor::therm_adc16
unsigned int therm_adc16(size_t ci=0) const
Return the last raw 16-bit ADC thermistor reading.
nrfcxx::periph::ADCClient::FL_SUBCLASS_BASE
Base for subclass use of flags_.
Definition: periph.hpp:2154
nrfcxx::sensor::adc::ntcThermistor::ABSOLUTE_ZERO_cCel
static constexpr int ABSOLUTE_ZERO_cCel
Constant used to convert between Kelvin and Celsius.
Definition: adc.hpp:502
nrfcxx::sensor::adc::voltage_divider::measurement_Ohm
int measurement_Ohm(uint16_t m16) const
Convert a raw ADC measurement to the estimated resistance.
Definition: adc.hpp:306
nrfcxx::sensor::adc::vdd::vdd_callback_bi
std::function< void(unsigned int vdd_mV)> vdd_callback_bi
Type for a function invoked from the ADC IRQ to provide the calculated VDD.
Definition: adc.hpp:44
nrfcxx::periph::ADCClient
Base class for a client of ADC.
Definition: periph.hpp:2123
nrfcxx::sensor::adc::voltage_divider::voltage_divider
voltage_divider(unsigned int r1_Ohm, unsigned int r2_Ohm, Iterator ains_begin, Iterator ains_end, volatile uint16_t *raw=nullptr)
Generic constructor for an iterable set of channels and optional external storage.
Definition: adc.hpp:197
nrfcxx::sensor::adc::steinhartHart::adafruit372refrigerator
const SteinhartHart adafruit372refrigerator
Constants for full-scale measurement with Adafruit thermistor.
nrfcxx::sensor::adc::SteinhartHart::c
const float c
Coefficient of ln(R)^3.
Definition: adc.hpp:423
nrfcxx::sensor::adc::voltage_divider::r2_Ohm
const unsigned int r2_Ohm
The resistance of the lower leg of the voltage divider, connected to ground.
Definition: adc.hpp:179
nrfcxx::sensor::adc::voltage_divider::voltage_divider
voltage_divider(unsigned int r1_Ohm, unsigned int r2_Ohm, uint8_t ain)
Constructor for common case of a single channel with internal storage.
Definition: adc.hpp:210
nrfcxx::sensor::adc::SteinhartHart::temperature_cK
int temperature_cK(unsigned int therm_Ohm) const
Calculate the temperature corresponding to a measured thermistor resistance.
nrfcxx::sensor::adc::voltage_divider::sample_adc16
int sample_adc16(size_t ci=0) const
Retrieve the latest raw measurement for a channel.
Definition: adc.hpp:252
nrfcxx::sensor::adc::lpsm_wrapper::mutex_type
periph::ADCClient::mutex_type mutex_type
Mutex domain is that of the ADC.
Definition: adc.hpp:703
nrfcxx::sensor::adc::lpsm_wrapper
A class that implements an LPM state machine around a periph::ADCClient.
Definition: adc.hpp:626
nrfcxx::notifier_type
std::function< void()> notifier_type
Type used to hold a notifier.
Definition: core.hpp:514
nrfcxx::sensor::adc::voltage_divider::voltage_divider
voltage_divider(unsigned int r1_Ohm, unsigned int r2_Ohm, std::initializer_list< uint8_t > ains, volatile uint16_t *raw=nullptr)
Constructor for an explicit list of channels.
Definition: adc.hpp:217
nrfcxx::sensor::adc::SteinhartHart::open_adc16
const uint16_t open_adc16
16-bit ADC upper threshold for open thermistor.
Definition: adc.hpp:432
nrfcxx::lpm::state_machine::MS_EXIT_SAMPLE
static constexpr state_type MS_EXIT_SAMPLE
States available to guard exit from a sample state.
Definition: lpm.hpp:201
nrfcxx::nrf5::series::ADC_Peripheral::VBG_mV
static constexpr unsigned int VBG_mV
nRF51 reference voltage is 1.2 V.
Definition: impl.hpp:26
nrfcxx::lpm::lpsm_capable
Base (or mixin) class for anything that supports a state_machine.
Definition: lpm.hpp:426
nrfcxx::sensor::adc::lpsm_wrapper::PF_CALIBRATED
static constexpr auto PF_CALIBRATED
Sensor-specific indication from lpm::lpsm_capable::lpsm_process() that the ADC has been calibrated.
Definition: adc.hpp:708
nrfcxx::lpm::state_machine::MS_ENTRY_SAMPLE
static constexpr state_type MS_ENTRY_SAMPLE
States available to guard entry to a sample state.
Definition: lpm.hpp:183
nrfcxx::lpm::state_machine::PF_APP_BASE
static constexpr process_flags_type PF_APP_BASE
First lpsm_capable::lpsm_process() flag bit available for application-specific result code bits.
Definition: lpm.hpp:255
nrfcxx::sensor::adc::voltage_divider::input_mV
int input_mV(size_t ci=0) const
Retrieve the latest input voltage measurement for a channel.
nrfcxx::periph::ADCClient::mutex_type
peripheral::mutex_type mutex_type
Mutex required to inhibit ADC interrupts.
Definition: periph.hpp:2178
nrfcxx::sensor::adc::vdd
ADC instance to measure board Vdd.
Definition: adc.hpp:27
nrfcxx::sensor::adc::ntcThermistor::ntcThermistor
ntcThermistor(Iterator ains_begin, Iterator ains_end, volatile uint16_t *raw=nullptr, const SteinhartHart *coeff=&steinhartHart::adafruit372fullScale, unsigned int ref_Ohm=10000)
Generic constructor for an iterable set of thermistor channels and optional external storage.
Definition: adc.hpp:556
nrfcxx::sensor::adc::steinhartHart::adafruit372hvac
const SteinhartHart adafruit372hvac
Constants for HVAC measurement with Adafruit thermistor.
nrfcxx::sensor::adc::SteinhartHart::short_adc16
const uint16_t short_adc16
16-bit ADC lower threshold for shorted thermistor.
Definition: adc.hpp:441
nrfcxx::sensor::adc::SteinhartHart::b
const float b
Coefficient of ln(R)^1.
Definition: adc.hpp:420
nrfcxx::sensor::adc::voltage_divider
ADC instance for voltage dividers.
Definition: adc.hpp:96
nrfcxx::sensor::adc::ntcThermistor::ntcThermistor
ntcThermistor(std::initializer_list< uint8_t > ains, volatile uint16_t *raw=nullptr, const SteinhartHart *coeff=&steinhartHart::adafruit372fullScale, unsigned int ref_Ohm=10000)
Constructor for an explicit list of channels.
Definition: adc.hpp:566
periph.hpp
Abstraction of Nordic device peripherals.
nrfcxx
Primary namespace for nrfcxx functionality.
Definition: clock.hpp:17
nrfcxx::sensor::adc::voltage_divider::count
size_t count() const
The number of channels used by this client.
Definition: adc.hpp:225
nrfcxx::sensor::adc::ntcThermistor::convert_adc16_cCel
int convert_adc16_cCel(unsigned int therm_adc16)
Convert a measured voltage to a temperature.