HF-ADS7952 Driver 0.1.0-dev
HF-ADS7952 C++ Driver
Loading...
Searching...
No Matches
ads7952.ipp
Go to the documentation of this file.
1
17#pragma once
18
19namespace ads7952 {
20
26// =============================================================================
27// Construction
28// =============================================================================
29
30template <typename SpiType>
32ADS7952<SpiType>::ADS7952(SpiType &spi, float vref, float va, Range initial_range) noexcept
33 : spi_(spi),
34 vref_((std::max)(ADS7952_CFG::MIN_VREF, (std::min)(vref, ADS7952_CFG::MAX_VREF))),
35 va_((std::max)(ADS7952_CFG::MIN_VA, (std::min)(va, ADS7952_CFG::MAX_VA))),
36 two_vref_(2.0f * vref_),
37 active_vref_((initial_range == Range::TwoVref) ? two_vref_ : vref_),
38 range_(initial_range) {}
39
40// =============================================================================
41// Runtime Calibration
42// =============================================================================
43
44template <typename SpiType>
45void ADS7952<SpiType>::SetVref(float vref) noexcept {
46 vref_ = (std::max)(ADS7952_CFG::MIN_VREF, (std::min)(vref, ADS7952_CFG::MAX_VREF));
47 two_vref_ = 2.0f * vref_;
48 // Update active_vref_ based on current range setting
49 active_vref_ = (range_ == Range::TwoVref) ? two_vref_ : vref_;
50}
51
52template <typename SpiType>
53void ADS7952<SpiType>::SetVA(float va) noexcept {
54 va_ = (std::max)(ADS7952_CFG::MIN_VA, (std::min)(va, ADS7952_CFG::MAX_VA));
55}
56
57// =============================================================================
58// Initialization
59// =============================================================================
60
61template <typename SpiType>
63bool ADS7952<SpiType>::EnsureInitialized(bool force) noexcept {
64 if (initialized_ && !force) {
65 return true;
66 }
67 initialized_ = false;
68 return Initialize();
69}
70
71template <typename SpiType>
72bool ADS7952<SpiType>::Initialize() noexcept {
73 // Datasheet: first conversion after power-up is invalid — discard it
74 spiTransfer16(reg::Mode::CONTINUE);
75
76 // Program Auto-1 with default channel mask
77 if (!ProgramAuto1Channels(auto1_mask_)) {
78 return false;
79 }
80
81 // Program Auto-2 with default last channel
82 if (!ProgramAuto2LastChannel(auto2_last_ch_)) {
83 return false;
84 }
85
86 // Enter Auto-1 mode as default operating mode
87 if (!EnterAuto1Mode(true)) {
88 return false;
89 }
90
91 initialized_ = true;
92 return true;
93}
94
95// =============================================================================
96// Channel Reading
97// =============================================================================
98
99template <typename SpiType>
101ReadResult ADS7952<SpiType>::ReadChannel(uint8_t channel) noexcept {
102 ReadResult result{};
103
104 if (!initialized_) {
106 return result;
107 }
108 if (channel >= reg::NUM_CHANNELS) {
109 result.error = Error::InvalidChannel;
110 return result;
111 }
112
113 const uint16_t ctrl = commonControlBits();
114
115 // Frame 1: MANUAL mode with target channel — triggers conversion,
116 // response contains stale data from previous operation.
118 | reg::ChannelSelect(channel) | ctrl;
119 spiTransfer16(cmd);
120
121 // Frame 2: CONTINUE — response now holds channel N conversion data.
122 uint16_t resp = spiTransfer16(reg::Mode::CONTINUE | ctrl);
123
124 result.channel = reg::Response::GetChannel(resp);
125 result.count = reg::Response::GetData(resp);
126 result.voltage = CountToVoltage(result.count);
127 result.error = Error::Ok;
128
129 mode_ = Mode::Manual;
130 return result;
131}
132
133template <typename SpiType>
136 ChannelReadings result{};
137
138 if (!initialized_) {
140 return result;
141 }
142
143 const uint16_t ctrl = commonControlBits();
144 const uint16_t target_mask = auto1_mask_ & 0x0FFF;
145 const uint8_t num_enabled = popcount16(target_mask);
146
147 if (num_enabled == 0) {
148 result.error = Error::Ok;
149 return result;
150 }
151
152 // Enter Auto-1 with channel counter reset.
153 // Response from this frame is stale — discard.
155 | reg::RESET_COUNTER | ctrl;
156 spiTransfer16(cmd);
157 mode_ = Mode::Auto1;
158
159 // Read until all programmed channels are received (with safety margin).
160 const uint16_t cont = reg::Mode::CONTINUE | ctrl;
161 const uint8_t max_frames =
163
164 for (uint8_t i = 0; i < max_frames; ++i) {
165 uint16_t resp = spiTransfer16(cont);
166 uint8_t ch = reg::Response::GetChannel(resp);
167 uint16_t data = reg::Response::GetData(resp);
168
169 if (ch < ChannelReadings::MAX_CHANNELS && (target_mask & (1U << ch))) {
170 result.count[ch] = data;
171 result.voltage[ch] = CountToVoltage(data);
172 result.valid_mask |= static_cast<uint16_t>(1U << ch);
173 }
174
175 if (result.valid_mask == target_mask) {
176 result.error = Error::Ok;
177 return result;
178 }
179 }
180
181 // Not all channels arrived within the allowed frame budget.
182 result.error = Error::Timeout;
183 return result;
184}
185
186// =============================================================================
187// Voltage Conversion
188// =============================================================================
189
190template <typename SpiType>
192float ADS7952<SpiType>::CountToVoltage(uint16_t count) const noexcept {
193 return (static_cast<float>(count) * active_vref_)
194 / static_cast<float>(reg::MAX_COUNT);
195}
196
197template <typename SpiType>
199uint16_t ADS7952<SpiType>::VoltageToCount(float voltage) const noexcept {
200 if (voltage <= 0.0f) return 0;
201 float raw = (voltage / active_vref_) * static_cast<float>(reg::MAX_COUNT);
202 if (raw > static_cast<float>(reg::MAX_COUNT)) return reg::MAX_COUNT;
203 return static_cast<uint16_t>(raw);
204}
205
206// =============================================================================
207// Mode Control
208// =============================================================================
209
210template <typename SpiType>
212bool ADS7952<SpiType>::EnterManualMode(uint8_t channel) noexcept {
213 if (channel >= reg::NUM_CHANNELS) return false;
214
216 | reg::ChannelSelect(channel) | commonControlBits();
217 spiTransfer16(cmd);
218 mode_ = Mode::Manual;
219 return true;
220}
221
222template <typename SpiType>
224bool ADS7952<SpiType>::EnterAuto1Mode(bool reset_counter) noexcept {
226 | (reset_counter ? reg::RESET_COUNTER : reg::NO_RESET_COUNTER)
227 | commonControlBits();
228 spiTransfer16(cmd);
229 mode_ = Mode::Auto1;
230 return true;
231}
232
233template <typename SpiType>
235bool ADS7952<SpiType>::EnterAuto2Mode(bool reset_counter) noexcept {
237 | (reset_counter ? reg::RESET_COUNTER : reg::NO_RESET_COUNTER)
238 | commonControlBits();
239 spiTransfer16(cmd);
240 mode_ = Mode::Auto2;
241 return true;
242}
243
244// =============================================================================
245// Programming — Auto-1 Channel Mask
246// =============================================================================
247
248template <typename SpiType>
250bool ADS7952<SpiType>::ProgramAuto1Channels(uint16_t channel_mask) noexcept {
251 // Frame 1: Enter Auto-1 programming mode
252 spiTransfer16(reg::Mode::AUTO_1_PROG);
253
254 // Frame 2: Channel enable mask in bits [11:0]
255 // (bits [15:12] are "don't care" per datasheet Table 3)
256 spiTransfer16(channel_mask & 0x0FFF);
257
258 auto1_mask_ = channel_mask & 0x0FFF;
259 return true;
260}
261
262// =============================================================================
263// Programming — Auto-2 Last Channel
264// =============================================================================
265
266template <typename SpiType>
268bool ADS7952<SpiType>::ProgramAuto2LastChannel(uint8_t last_channel) noexcept {
269 if (last_channel >= reg::NUM_CHANNELS) return false;
270
271 // Single frame: mode + last channel address in bits [9:6]
272 spiTransfer16(reg::Mode::AUTO_2_PROG | reg::Auto2LastChannel(last_channel));
273
274 auto2_last_ch_ = last_channel;
275 return true;
276}
277
278// =============================================================================
279// Programming — GPIO Configuration
280// =============================================================================
281
282template <typename SpiType>
284bool ADS7952<SpiType>::ProgramGPIO(const GPIOConfig &config) noexcept {
285 uint16_t frame = reg::Mode::GPIO_PROG;
286
287 // Reset control (bit 9)
288 if (config.reset_all_registers) {
290 }
291
292 // GPIO3 function (bit 8)
293 if (config.gpio3_as_powerdown_input) {
295 }
296
297 // GPIO2 function (bit 7)
298 if (config.gpio2_as_range_input) {
300 }
301
302 // GPIO0/1 alarm mode (bits [6:4])
303 switch (config.alarm_mode) {
305 break;
308 break;
311 break;
314 break;
317 break;
318 }
319
320 // Direction mask (bits [3:0])
321 frame |= static_cast<uint16_t>(config.direction_mask & 0x0F);
322
323 spiTransfer16(frame);
324 return true;
325}
326
327// =============================================================================
328// Programming — Alarm Thresholds
329// =============================================================================
330
331template <typename SpiType>
333bool ADS7952<SpiType>::ProgramAlarm(uint8_t channel, AlarmBound bound,
334 uint16_t threshold_12bit) noexcept {
335 if (channel >= reg::NUM_CHANNELS) return false;
336
337 // Determine alarm group (3 groups × 4 channels)
338 const uint8_t group = channel / reg::CHANNELS_PER_ALARM_GROUP;
339 const uint8_t ch_in_grp = channel % reg::CHANNELS_PER_ALARM_GROUP;
340
341 // Select the correct alarm group mode code
342 uint16_t group_mode;
343 switch (group) {
344 case 0: group_mode = reg::Mode::ALARM_GROUP_0; break;
345 case 1: group_mode = reg::Mode::ALARM_GROUP_1; break;
346 case 2: group_mode = reg::Mode::ALARM_GROUP_2; break;
347 default: return false;
348 }
349
350 // Frame 1: Enter alarm group programming mode
351 spiTransfer16(group_mode);
352
353 // Frame 2: Alarm data — channel in group, hi/lo select, exit, threshold
354 uint16_t alarm_frame = reg::Alarm::ChannelInGroup(ch_in_grp);
355
356 if (bound == AlarmBound::High) {
357 alarm_frame |= reg::Alarm::HIGH_REGISTER;
358 }
359
360 // Exit programming after this alarm (single-alarm API)
361 alarm_frame |= reg::Alarm::EXIT_NEXT_FRAME;
362 alarm_frame |= reg::Alarm::Threshold12To10(threshold_12bit);
363
364 spiTransfer16(alarm_frame);
365
366 // Frame 3: Any frame to complete the exit (continue mode)
367 spiTransfer16(reg::Mode::CONTINUE | commonControlBits());
368
369 return true;
370}
371
372template <typename SpiType>
375 float voltage) noexcept {
376 return ProgramAlarm(channel, bound, VoltageToCount(voltage));
377}
378
379// =============================================================================
380// Range & Power
381// =============================================================================
382
383template <typename SpiType>
385bool ADS7952<SpiType>::SetRange(Range range) noexcept {
386 range_ = range;
387
388 // Update active voltage reference
389 active_vref_ = (range == Range::TwoVref) ? two_vref_ : vref_;
390
391 // Send a mode frame with the updated range bit so the device latches it
392 uint16_t cmd = reg::Mode::CONTINUE | commonControlBits();
393 spiTransfer16(cmd);
394 return true;
395}
396
397template <typename SpiType>
400 power_down_ = pd;
401
402 // Send a frame so the device latches the new power-down setting
403 uint16_t cmd = reg::Mode::CONTINUE | commonControlBits();
404 spiTransfer16(cmd);
405 return true;
406}
407
408// =============================================================================
409// GPIO Output Control
410// =============================================================================
411
412template <typename SpiType>
414void ADS7952<SpiType>::SetGPIOOutputs(uint8_t gpio_state) noexcept {
415 gpio_output_state_ = gpio_state & 0x0F;
416
417 // The GPIO output bits [3:0] are sent with every mode control frame.
418 // Send a continue frame to latch the new output levels.
419 spiTransfer16(reg::Mode::CONTINUE | commonControlBits());
420}
421
422// =============================================================================
423// Internal Helpers
424// =============================================================================
425
426template <typename SpiType>
428uint16_t ADS7952<SpiType>::spiTransfer16(uint16_t command) noexcept {
429 uint8_t tx[2] = {
430 static_cast<uint8_t>((command >> 8) & 0xFF), // MSB first
431 static_cast<uint8_t>(command & 0xFF)};
432 uint8_t rx[2] = {0, 0};
433
434 spi_.transfer(tx, rx, 2);
435
436 return (static_cast<uint16_t>(rx[0]) << 8) | rx[1];
437}
438
439template <typename SpiType>
441uint16_t ADS7952<SpiType>::commonControlBits() const noexcept {
442 uint16_t bits = 0;
443
444 if (range_ == Range::TwoVref) {
445 bits |= reg::RANGE_2VREF;
446 }
447
448 if (power_down_ == PowerDown::PowerDown) {
449 bits |= reg::POWER_DOWN;
450 }
451
452 bits |= static_cast<uint16_t>(gpio_output_state_ & 0x0F);
453
454 return bits;
455}
456
457template <typename SpiType>
459constexpr uint8_t ADS7952<SpiType>::popcount16(uint16_t x) noexcept {
460 x = x - ((x >> 1) & 0x5555);
461 x = (x & 0x3333) + ((x >> 2) & 0x3333);
462 return static_cast<uint8_t>((((x + (x >> 4)) & 0x0F0F) * 0x0101) >> 8);
463}
464
465} // namespace ads7952
466
467 // end of ads7952_core
Main driver class for the ADS7952 ADC.
Definition ads7952.hpp:76
constexpr float MIN_VA
constexpr uint8_t READ_ALL_MAX_EXTRA_FRAMES
constexpr float MIN_VREF
constexpr float MAX_VREF
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:192
ChannelReadings ReadAllChannels() noexcept
Read all channels in the current Auto-1 sequence.
Definition ads7952.ipp:135
void SetVref(float vref) noexcept
Update the Vref value for voltage calculations.
Definition ads7952.ipp:45
bool ProgramAuto2LastChannel(uint8_t last_channel) noexcept
Program the Auto-2 last channel.
Definition ads7952.ipp:268
void SetGPIOOutputs(uint8_t gpio_state) noexcept
Set GPIO output pin levels (for pins configured as outputs).
Definition ads7952.ipp:414
bool ProgramGPIO(const GPIOConfig &config) noexcept
Program GPIO pin functions, direction, and alarm routing.
Definition ads7952.ipp:284
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:333
bool ProgramAlarmVoltage(uint8_t channel, AlarmBound bound, float voltage) noexcept
Program an alarm threshold for a single channel (voltage-based).
Definition ads7952.ipp:374
void SetVA(float va) noexcept
Update the VA supply voltage value.
Definition ads7952.ipp:53
uint16_t VoltageToCount(float voltage) const noexcept
Convert a voltage to a 12-bit ADC count using the active reference.
Definition ads7952.ipp:199
bool SetPowerDown(PowerDown pd) noexcept
Set the power-down mode.
Definition ads7952.ipp:399
ReadResult ReadChannel(uint8_t channel) noexcept
Read a single ADC channel (switches to Manual mode).
Definition ads7952.ipp:101
bool EnterManualMode(uint8_t channel=0) noexcept
Enter Manual mode, selecting the given channel for conversion.
Definition ads7952.ipp:212
bool EnterAuto2Mode(bool reset_counter=true) noexcept
Enter Auto-2 mode (sequences channels 0 through last_channel).
Definition ads7952.ipp:235
bool EnterAuto1Mode(bool reset_counter=true) noexcept
Enter Auto-1 mode (sequences through programmed channel mask).
Definition ads7952.ipp:224
bool ProgramAuto1Channels(uint16_t channel_mask) noexcept
Program the Auto-1 channel sequence.
Definition ads7952.ipp:250
bool SetRange(Range range) noexcept
Set the input voltage range and update active reference.
Definition ads7952.ipp:385
bool EnsureInitialized(bool force=false) noexcept
Ensure the driver is initialized — idempotent, safe to call repeatedly.
Definition ads7952.ipp:63
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:32
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
GPIO0 = combined hi/lo alarm output.
@ GPIO0_HighAlarm
GPIO0 = high alarm output.
@ GPIO
Both as general-purpose I/O.
@ GPIO1_HighAlarm
GPIO1 = high alarm output.
@ GPIO1_LowAlarm_GPIO0_HighAlarm
GPIO1=low, GPIO0=high alarm.
@ 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.
@ InvalidChannel
Channel number out of range (0-11)
@ NotInitialized
Driver not initialized.
@ Ok
Operation succeeded.
@ Timeout
Operation timed out waiting for data.
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 GPIO3_PWRDOWN_IN
constexpr uint16_t GPIO0_HI_LO_ALARM
constexpr uint16_t GPIO1_HI_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 PROGRAM_RETAIN
constexpr uint16_t RANGE_2VREF
constexpr uint16_t NO_RESET_COUNTER
constexpr uint16_t PROGRAM_ENABLE
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