ADS7952 Driver
API reference
Loading...
Searching...
No Matches
ads7952.ipp
Go to the documentation of this file.
1
31#pragma once
32
33namespace ads7952 {
34
40// =============================================================================
41// Construction
42// =============================================================================
43
44template <typename SpiType>
46ADS7952<SpiType>::ADS7952(SpiType &spi, float vref, float va, Range initial_range) noexcept
47 : spi_(spi),
48 vref_((std::max)(ADS7952_CFG::MIN_VREF, (std::min)(vref, ADS7952_CFG::MAX_VREF))),
49 va_((std::max)(ADS7952_CFG::MIN_VA, (std::min)(va, ADS7952_CFG::MAX_VA))),
50 two_vref_(2.0f * vref_),
51 active_vref_((initial_range == Range::TwoVref) ? two_vref_ : vref_),
52 range_(initial_range) {}
53
54// =============================================================================
55// Runtime Calibration
56// =============================================================================
57
58template <typename SpiType>
59void ADS7952<SpiType>::SetVref(float vref) noexcept {
60 vref_ = (std::max)(ADS7952_CFG::MIN_VREF, (std::min)(vref, ADS7952_CFG::MAX_VREF));
61 two_vref_ = 2.0f * vref_;
62 // Update active_vref_ based on current range setting
63 active_vref_ = (range_ == Range::TwoVref) ? two_vref_ : vref_;
64}
65
66template <typename SpiType>
67void ADS7952<SpiType>::SetVA(float va) noexcept {
68 va_ = (std::max)(ADS7952_CFG::MIN_VA, (std::min)(va, ADS7952_CFG::MAX_VA));
69}
70
71// =============================================================================
72// Initialization
73// =============================================================================
74
75template <typename SpiType>
77bool ADS7952<SpiType>::EnsureInitialized(bool force) noexcept {
78 if (initialized_ && !force) {
79 return true;
80 }
81 initialized_ = false;
82 return Initialize();
83}
84
85template <typename SpiType>
86bool ADS7952<SpiType>::Initialize() noexcept {
87 // Datasheet: first conversion after power-up is invalid — discard it
88 if (!spiTransfer16(reg::Mode::CONTINUE)) {
89 return false;
90 }
91
92 // Program Auto-1 with default channel mask
93 if (!ProgramAuto1Channels(auto1_mask_)) {
94 return false;
95 }
96
97 // Program Auto-2 with default last channel
98 if (!ProgramAuto2LastChannel(auto2_last_ch_)) {
99 return false;
100 }
101
102 // Enter Auto-1 mode as default operating mode
103 if (!EnterAuto1Mode(true)) {
104 return false;
105 }
106
107 initialized_ = true;
108 return true;
109}
110
111// =============================================================================
112// Channel Reading
113// =============================================================================
114
115template <typename SpiType>
117ReadResult ADS7952<SpiType>::ReadChannel(uint8_t channel) noexcept {
118 ReadResult result{};
119
120 if (!initialized_) {
122 return result;
123 }
124 if (channel >= reg::NUM_CHANNELS) {
125 result.error = Error::InvalidChannel;
126 return result;
127 }
128
129 const uint16_t ctrl = commonControlBits();
130
131 // Frame N: MANUAL mode with the target channel. SLAS605C Figure 51: the
132 // command written in frame N is parsed on the rising edge of CS ending
133 // frame N, the MUX switches on the 2nd SCLK of frame N+1, the input is
134 // acquired through frame N+1 and sampled on the CS falling edge that
135 // opens frame N+2 — so the conversion for this channel is clocked out in
136 // frame N+2, not N+1. Frame N+1 still carries the channel selected two
137 // frames ago. Rather than count frames, clock CONTINUE until the 4-bit
138 // channel address in DO15:12 is the one requested (the Airseal/Moonshine
139 // port's ProcessFrame contract); anything else is attributed to the wrong
140 // tap.
142 | reg::ChannelSelect(channel) | ctrl;
143 result.channel = channel;
144 if (!spiTransfer16(cmd)) {
145 result.error = Error::SpiError;
146 return result;
147 }
148 mode_ = Mode::Manual;
149 manual_channel_ = channel;
150
151 const uint16_t cont = reg::Mode::CONTINUE | ctrl;
152 for (uint8_t i = 0; i < ADS7952_CFG::MANUAL_READ_MAX_FRAMES; ++i) {
153 uint16_t resp = 0;
154 if (!spiTransfer16(cont, &resp)) {
155 result.error = Error::SpiError;
156 return result;
157 }
158 const uint8_t ch = reg::Response::GetChannel(resp);
159 if (ch == channel) {
160 result.channel = ch;
161 result.count = reg::Response::GetData(resp);
162 result.voltage = CountToVoltage(result.count);
163 result.error = Error::Ok;
164 return result;
165 }
166 }
167
168 result.channel = channel;
169 result.error = Error::Timeout;
170 return result;
171}
172
173template <typename SpiType>
176 ChannelReadings result{};
177
178 if (!initialized_) {
180 return result;
181 }
182
183 const uint16_t ctrl = commonControlBits();
184 const uint16_t target_mask = auto1_mask_ & 0x0FFF;
185 const uint8_t num_enabled = popcount16(target_mask);
186
187 if (num_enabled == 0) {
188 result.error = Error::Ok;
189 return result;
190 }
191
192 // Enter Auto-1 with the channel counter reset. Table 2: in Auto-1 the
193 // DI11 programming bit gates DI10..DI00 as a block, so RESET_COUNTER
194 // (DI10) is ignored unless DI11 = PROGRAM_ENABLE — the earlier
195 // PROGRAM_RETAIN | RESET_COUNTER frame never reset the sequence. The
196 // range / power / GPIO bits in `ctrl` are what the device already runs
197 // with, so re-programming them here is a no-op. Response from this frame
198 // is stale — discard.
200 | reg::RESET_COUNTER | ctrl;
201 if (!spiTransfer16(cmd)) {
202 result.error = Error::SpiError;
203 return result;
204 }
205 mode_ = Mode::Auto1;
206
207 // Read until all programmed channels are received (with safety margin).
208 const uint16_t cont = reg::Mode::CONTINUE | ctrl;
209 const uint8_t max_frames =
211
212 for (uint8_t i = 0; i < max_frames; ++i) {
213 uint16_t resp = 0;
214 if (!spiTransfer16(cont, &resp)) {
215 result.error = Error::SpiError;
216 return result;
217 }
218 uint8_t ch = reg::Response::GetChannel(resp);
219 uint16_t data = reg::Response::GetData(resp);
220
221 if (ch < ChannelReadings::MAX_CHANNELS && (target_mask & (1U << ch))) {
222 result.count[ch] = data;
223 result.voltage[ch] = CountToVoltage(data);
224 result.valid_mask |= static_cast<uint16_t>(1U << ch);
225 }
226
227 if (result.valid_mask == target_mask) {
228 result.error = Error::Ok;
229 return result;
230 }
231 }
232
233 // Not all channels arrived within the allowed frame budget.
234 result.error = Error::Timeout;
235 return result;
236}
237
238// =============================================================================
239// Voltage Conversion
240// =============================================================================
241
242template <typename SpiType>
244float ADS7952<SpiType>::CountToVoltage(uint16_t count) const noexcept {
245 return (static_cast<float>(count) * active_vref_)
246 / static_cast<float>(reg::MAX_COUNT);
247}
248
249template <typename SpiType>
251uint16_t ADS7952<SpiType>::VoltageToCount(float voltage) const noexcept {
252 if (voltage <= 0.0f) return 0;
253 float raw = (voltage / active_vref_) * static_cast<float>(reg::MAX_COUNT);
254 if (raw > static_cast<float>(reg::MAX_COUNT)) return reg::MAX_COUNT;
255 return static_cast<uint16_t>(raw);
256}
257
258// =============================================================================
259// Mode Control
260// =============================================================================
261
262template <typename SpiType>
264bool ADS7952<SpiType>::EnterManualMode(uint8_t channel) noexcept {
265 if (channel >= reg::NUM_CHANNELS) return false;
266
268 | reg::ChannelSelect(channel) | commonControlBits();
269 if (!spiTransfer16(cmd)) {
270 return false;
271 }
272 mode_ = Mode::Manual;
273 manual_channel_ = channel;
274 return true;
275}
276
277template <typename SpiType>
279bool ADS7952<SpiType>::EnterAuto1Mode(bool reset_counter) noexcept {
281 | (reset_counter ? reg::RESET_COUNTER : reg::NO_RESET_COUNTER)
282 | commonControlBits();
283 if (!spiTransfer16(cmd)) {
284 return false;
285 }
286 mode_ = Mode::Auto1;
287 return true;
288}
289
290template <typename SpiType>
292bool ADS7952<SpiType>::EnterAuto2Mode(bool reset_counter) noexcept {
294 | (reset_counter ? reg::RESET_COUNTER : reg::NO_RESET_COUNTER)
295 | commonControlBits();
296 if (!spiTransfer16(cmd)) {
297 return false;
298 }
299 mode_ = Mode::Auto2;
300 return true;
301}
302
303// =============================================================================
304// Programming — Auto-1 Channel Mask
305// =============================================================================
306
307template <typename SpiType>
309bool ADS7952<SpiType>::ProgramAuto1Channels(uint16_t channel_mask) noexcept {
310 // Frame 1: Enter Auto-1 programming mode
311 if (!spiTransfer16(reg::Mode::AUTO_1_PROG)) {
312 return false;
313 }
314
315 // Frame 2: Channel enable mask in bits [11:0]
316 // (bits [15:12] are "don't care" per datasheet Table 3)
317 if (!spiTransfer16(channel_mask & 0x0FFF)) {
318 return false;
319 }
320
321 auto1_mask_ = channel_mask & 0x0FFF;
322 return true;
323}
324
325// =============================================================================
326// Programming — Auto-2 Last Channel
327// =============================================================================
328
329template <typename SpiType>
331bool ADS7952<SpiType>::ProgramAuto2LastChannel(uint8_t last_channel) noexcept {
332 if (last_channel >= reg::NUM_CHANNELS) return false;
333
334 // Single frame: mode + last channel address in bits [9:6]
335 if (!spiTransfer16(reg::Mode::AUTO_2_PROG | reg::Auto2LastChannel(last_channel))) {
336 return false;
337 }
338
339 auto2_last_ch_ = last_channel;
340 return true;
341}
342
343// =============================================================================
344// Programming — GPIO Configuration
345// =============================================================================
346
347template <typename SpiType>
349bool ADS7952<SpiType>::ProgramGPIO(const GPIOConfig &config) noexcept {
350 uint16_t frame = reg::Mode::GPIO_PROG;
351
352 // Reset control (bit 9)
353 if (config.reset_all_registers) {
355 }
356
357 // GPIO3 function (bit 8)
358 if (config.gpio3_as_powerdown_input) {
360 }
361
362 // GPIO2 function (bit 7)
363 if (config.gpio2_as_range_input) {
365 }
366
367 // GPIO0/1 alarm mode (bits [6:4])
368 switch (config.alarm_mode) {
370 break;
373 break;
376 break;
379 break;
382 break;
383 }
384
385 // Direction mask (bits [3:0])
386 frame |= static_cast<uint16_t>(config.direction_mask & 0x0F);
387
388 return spiTransfer16(frame);
389}
390
391// =============================================================================
392// Programming — Alarm Thresholds
393// =============================================================================
394
395template <typename SpiType>
397bool ADS7952<SpiType>::ProgramAlarm(uint8_t channel, AlarmBound bound,
398 uint16_t threshold_12bit) noexcept {
399 if (channel >= reg::NUM_CHANNELS) return false;
400
401 // Determine alarm group (3 groups × 4 channels)
402 const uint8_t group = channel / reg::CHANNELS_PER_ALARM_GROUP;
403 const uint8_t ch_in_grp = channel % reg::CHANNELS_PER_ALARM_GROUP;
404
405 // Select the correct alarm group mode code
406 uint16_t group_mode;
407 switch (group) {
408 case 0: group_mode = reg::Mode::ALARM_GROUP_0; break;
409 case 1: group_mode = reg::Mode::ALARM_GROUP_1; break;
410 case 2: group_mode = reg::Mode::ALARM_GROUP_2; break;
411 default: return false;
412 }
413
414 // Frame 1: Enter alarm group programming mode
415 if (!spiTransfer16(group_mode)) {
416 return false;
417 }
418
419 // Frame 2: Alarm data — channel in group, hi/lo select, exit, threshold
420 uint16_t alarm_frame = reg::Alarm::ChannelInGroup(ch_in_grp);
421
422 if (bound == AlarmBound::High) {
423 alarm_frame |= reg::Alarm::HIGH_REGISTER;
424 }
425
426 // Exit programming after this alarm (single-alarm API)
427 alarm_frame |= reg::Alarm::EXIT_NEXT_FRAME;
428 alarm_frame |= reg::Alarm::Threshold12To10(threshold_12bit);
429
430 if (!spiTransfer16(alarm_frame)) {
431 return false;
432 }
433
434 // Frame 3: Any frame to complete the exit (continue mode)
435 return spiTransfer16(reg::Mode::CONTINUE | commonControlBits());
436}
437
438template <typename SpiType>
441 float voltage) noexcept {
442 return ProgramAlarm(channel, bound, VoltageToCount(voltage));
443}
444
445// =============================================================================
446// Range & Power
447// =============================================================================
448
449template <typename SpiType>
450template <typename Commit>
451bool ADS7952<SpiType>::programControlBits(uint16_t ctrl, Commit commit) noexcept {
452 // SLAS605C Tables 1, 2, 5: DI06:00 are taken only from a mode frame with
453 // DI11 = 1 (DI11 = 0 retains the previous values). A CONTINUE frame
454 // ignores DI11:00 entirely (Table 7), so it cannot carry a new setting.
455 // Re-issue the current mode: Manual re-selects the current channel,
456 // Auto-1/Auto-2 keep counting (DI10 = 0, no counter reset).
457 uint16_t cmd = reg::PROGRAM_ENABLE | ctrl;
458 switch (mode_) {
459 case Mode::Auto1:
461 break;
462 case Mode::Auto2:
464 break;
465 case Mode::Manual:
466 default:
467 cmd |= reg::Mode::MANUAL | reg::ChannelSelect(manual_channel_);
468 break;
469 }
470 if (!spiTransfer16(cmd)) {
471 return false; // not sent: chip and driver keep the old setting
472 }
473 commit(); // the chip latched DI06:00 at the CS rising edge of that frame
474
475 // One more frame makes the setting effective before returning
476 // (Figure 50: the range written in frame N applies to the input acquired
477 // in frame N+1; section 8.1: GPO pins update on the CS falling edge of
478 // frame N+1; 8.3.2: DI05 = 1 powers down at the 16th SCLK of frame N+1).
479 // Its reply is the last conversion made with the old range: discarded.
480 return spiTransfer16(reg::Mode::CONTINUE);
481}
482
483template <typename SpiType>
485bool ADS7952<SpiType>::SetRange(Range range) noexcept {
486 const uint16_t ctrl = (commonControlBits() & ~reg::RANGE_2VREF) |
488 return programControlBits(ctrl, [&] {
489 range_ = range;
490 active_vref_ = (range == Range::TwoVref) ? two_vref_ : vref_;
491 });
492}
493
494template <typename SpiType>
497 const uint16_t ctrl = (commonControlBits() & ~reg::POWER_DOWN) |
499 return programControlBits(ctrl, [&] { power_down_ = pd; });
500}
501
502// =============================================================================
503// GPIO Output Control
504// =============================================================================
505
506template <typename SpiType>
508bool ADS7952<SpiType>::SetGPIOOutputs(uint8_t gpio_state) noexcept {
509 const uint8_t state = gpio_state & 0x0F;
510 const uint16_t ctrl = static_cast<uint16_t>((commonControlBits() & ~0x000Fu) | state);
511 return programControlBits(ctrl, [&] { gpio_output_state_ = state; });
512}
513
514// =============================================================================
515// Raw frame trace
516// =============================================================================
517
518template <typename SpiType>
520uint8_t ADS7952<SpiType>::RawManualFrames(uint8_t channel, uint16_t* out,
521 uint8_t n) noexcept {
522 if (!initialized_ || out == nullptr || n == 0 ||
523 channel >= reg::NUM_CHANNELS) {
524 return 0;
525 }
526 const uint16_t ctrl = commonControlBits();
527 if (!spiTransfer16(reg::Mode::MANUAL | reg::PROGRAM_RETAIN | reg::ChannelSelect(channel) | ctrl,
528 &out[0])) {
529 return 0;
530 }
531 mode_ = Mode::Manual;
532 manual_channel_ = channel;
533 const uint16_t cont = reg::Mode::CONTINUE | ctrl;
534 for (uint8_t i = 1; i < n; ++i) {
535 if (!spiTransfer16(cont, &out[i])) {
536 return i; // words 0..i-1 are valid
537 }
538 }
539 return n;
540}
541
542// =============================================================================
543// Internal Helpers
544// =============================================================================
545
546template <typename SpiType>
548bool ADS7952<SpiType>::spiTransfer16(uint16_t command, uint16_t* response) noexcept {
549 uint8_t tx[2] = {
550 static_cast<uint8_t>((command >> 8) & 0xFF), // MSB first
551 static_cast<uint8_t>(command & 0xFF)};
552 uint8_t rx[2] = {0, 0};
553
554 // detail::Transfer: a bool-returning port reports bus errors; a legacy
555 // void-returning port is treated as always successful.
556 const bool ok = detail::Transfer(spi_, tx, rx, 2);
557
558 if (response != nullptr) {
559 *response = ok ? static_cast<uint16_t>((static_cast<uint16_t>(rx[0]) << 8) | rx[1]) : 0;
560 }
561 return ok;
562}
563
564template <typename SpiType>
566uint16_t ADS7952<SpiType>::commonControlBits() const noexcept {
567 uint16_t bits = 0;
568
569 if (range_ == Range::TwoVref) {
570 bits |= reg::RANGE_2VREF;
571 }
572
573 if (power_down_ == PowerDown::PowerDown) {
574 bits |= reg::POWER_DOWN;
575 }
576
577 bits |= static_cast<uint16_t>(gpio_output_state_ & 0x0F);
578
579 return bits;
580}
581
582template <typename SpiType>
584constexpr uint8_t ADS7952<SpiType>::popcount16(uint16_t x) noexcept {
585 x = x - ((x >> 1) & 0x5555);
586 x = (x & 0x3333) + ((x >> 2) & 0x3333);
587 return static_cast<uint8_t>((((x + (x >> 4)) & 0x0F0F) * 0x0101) >> 8);
588}
589
590} // namespace ads7952
591
// end of ads7952_core
Main driver class for the ADS7952 ADC.
Definition ads7952.hpp:81
constexpr float MIN_VA
constexpr uint8_t READ_ALL_MAX_EXTRA_FRAMES
constexpr float MIN_VREF
constexpr float MAX_VREF
constexpr uint8_t MANUAL_READ_MAX_FRAMES
constexpr float MAX_VA
constexpr uint16_t VoltageToCount(float voltage, float vref) noexcept
Convert a voltage to a 12-bit ADC count.
float CountToVoltage(uint16_t count) const noexcept
Convert raw count to voltage using the currently active reference.
Definition ads7952.ipp:244
bool SetGPIOOutputs(uint8_t gpio_state) noexcept
Set GPIO output pin levels (for pins configured as outputs).
Definition ads7952.ipp:508
ChannelReadings ReadAllChannels() noexcept
Read all channels in the current Auto-1 sequence.
Definition ads7952.ipp:175
void SetVref(float vref) noexcept
Update the Vref value for voltage calculations.
Definition ads7952.ipp:59
bool ProgramAuto2LastChannel(uint8_t last_channel) noexcept
Program the Auto-2 last channel.
Definition ads7952.ipp:331
uint8_t RawManualFrames(uint8_t channel, uint16_t *out, uint8_t n) noexcept
Clock raw 16-bit frames and return every response word unparsed.
Definition ads7952.ipp:520
bool ProgramGPIO(const GPIOConfig &config) noexcept
Program GPIO pin functions, direction, and alarm routing.
Definition ads7952.ipp:349
bool ProgramAlarm(uint8_t channel, AlarmBound bound, uint16_t threshold_12bit) noexcept
Program an alarm threshold for a single channel (count-based).
Definition ads7952.ipp:397
bool ProgramAlarmVoltage(uint8_t channel, AlarmBound bound, float voltage) noexcept
Program an alarm threshold for a single channel (voltage-based).
Definition ads7952.ipp:440
void SetVA(float va) noexcept
Update the VA supply voltage value.
Definition ads7952.ipp:67
uint16_t VoltageToCount(float voltage) const noexcept
Convert a voltage to a 12-bit ADC count using the active reference.
Definition ads7952.ipp:251
bool SetPowerDown(PowerDown pd) noexcept
Set the power-down mode; in effect on the chip when this returns.
Definition ads7952.ipp:496
ReadResult ReadChannel(uint8_t channel) noexcept
Read a single ADC channel (switches to Manual mode).
Definition ads7952.ipp:117
bool EnterManualMode(uint8_t channel=0) noexcept
Enter Manual mode, selecting the given channel for conversion.
Definition ads7952.ipp:264
bool EnterAuto2Mode(bool reset_counter=true) noexcept
Enter Auto-2 mode (sequences channels 0 through last_channel).
Definition ads7952.ipp:292
bool EnterAuto1Mode(bool reset_counter=true) noexcept
Enter Auto-1 mode (sequences through programmed channel mask).
Definition ads7952.ipp:279
bool ProgramAuto1Channels(uint16_t channel_mask) noexcept
Program the Auto-1 channel sequence.
Definition ads7952.ipp:309
bool SetRange(Range range) noexcept
Set the input voltage range; in effect on the chip when this returns.
Definition ads7952.ipp:485
bool EnsureInitialized(bool force=false) noexcept
Ensure the driver is initialized — idempotent, safe to call repeatedly.
Definition ads7952.ipp:77
ADS7952(SpiType &spi, float vref=ADS7952_CFG::DEFAULT_VREF, float va=ADS7952_CFG::DEFAULT_VA, Range initial_range=ADS7952_CFG::DEFAULT_RANGE) noexcept
Construct a new ADS7952 driver instance.
Definition ads7952.ipp:46
constexpr uint16_t ChannelSelect(uint8_t ch) noexcept
Encode manual-mode channel selection bits DI[10:7].
constexpr uint16_t Auto2LastChannel(uint8_t ch) noexcept
Encode Auto-2 last-channel field DI[9:6].
@ GPIO0_HighAndLowAlarm
xx1: GPIO0 = combined high-or-low alarm output
@ GPIO0_HighAlarm
010: GPIO0 = high alarm output
@ GPIO
000: both as general-purpose I/O
@ GPIO1_LowAlarm
100: GPIO1 = low alarm output
@ PowerDown
Device enters power-down.
@ Auto2
Device sequences channels 0 to last_channel.
@ Auto1
Device sequences through programmed channel mask.
@ Manual
Host selects channel each frame.
@ TwoVref
0 to 2*Vref, clamped to VA (RANGE bit = 1)
@ High
High alarm threshold register.
@ SpiError
SPI transfer failure.
@ InvalidChannel
Channel number out of range (0-11)
@ NotInitialized
Driver not initialized.
@ Ok
Operation succeeded.
@ Timeout
Operation timed out waiting for data.
bool Transfer(Port &port, const uint8_t *tx, uint8_t *rx, std::size_t len)
Call the port's transfer() and return its status.
constexpr uint16_t EXIT_NEXT_FRAME
constexpr uint16_t Threshold12To10(uint16_t adc_12bit) noexcept
Convert 12-bit ADC threshold to 10-bit alarm register format.
constexpr uint16_t HIGH_REGISTER
constexpr uint16_t ChannelInGroup(uint8_t ch_in_group) noexcept
Encode alarm channel index within a 4-channel group.
constexpr uint16_t GPIO0_HI_ALARM
constexpr uint16_t RESET_ALL_REGS
constexpr uint16_t GPIO1_LO_GPIO0_HI_ALARM
constexpr uint16_t GPIO1_LO_ALARM
constexpr uint16_t GPIO3_PWRDOWN_IN
constexpr uint16_t GPIO0_HI_LO_ALARM
constexpr uint16_t GPIO2_RANGE_IN
constexpr uint16_t AUTO_1
constexpr uint16_t AUTO_1_PROG
constexpr uint16_t ALARM_GROUP_0
constexpr uint16_t ALARM_GROUP_1
constexpr uint16_t CONTINUE
constexpr uint16_t GPIO_PROG
constexpr uint16_t AUTO_2_PROG
constexpr uint16_t ALARM_GROUP_2
constexpr uint16_t MANUAL
constexpr uint16_t AUTO_2
constexpr uint8_t GetChannel(uint16_t frame) noexcept
Extract channel descriptor from response frame DO[15:12].
constexpr uint16_t GetData(uint16_t frame) noexcept
Extract conversion data from response frame DO[11:0].
constexpr uint16_t POWER_NORMAL
constexpr uint16_t PROGRAM_RETAIN
constexpr uint16_t RANGE_2VREF
constexpr uint16_t NO_RESET_COUNTER
constexpr uint16_t PROGRAM_ENABLE
constexpr uint16_t RANGE_VREF
constexpr uint8_t NUM_CHANNELS
Number of ADC input channels.
constexpr uint16_t POWER_DOWN
constexpr uint16_t MAX_COUNT
Maximum 12-bit conversion value.
constexpr uint16_t RESET_COUNTER
constexpr uint8_t CHANNELS_PER_ALARM_GROUP
Channels per alarm group.
static constexpr uint8_t MAX_CHANNELS