API guide¶
How to do the common jobs with the driver: initialise, configure, read, program alarms and GPIOs, and handle errors.
Every snippet on this page is compiled by the host CI from
tests/docs/api_tasks.cpp, with Adc standing for ads7952::ADS7952<MySpi>.
The API reference lists every member.
The shape of the API¶
One ADS7952<SpiType> object per chip. It owns no hardware: it holds a reference to your port and mirrors what it
has programmed into the chip.
| Group | Calls | Returns |
|---|---|---|
| Initialise | EnsureInitialized(force), IsInitialized() |
bool |
| Read | ReadChannel(ch), ReadAllChannels(), RawManualFrames(ch, out, n) |
ReadResult, ChannelReadings, frame count |
| Modes | EnterManualMode(ch), EnterAuto1Mode(reset), EnterAuto2Mode(reset), GetMode() |
bool |
| Sequences | ProgramAuto1Channels(mask), ProgramAuto2LastChannel(ch) |
bool |
| Alarms and GPIOs | ProgramAlarm(), ProgramAlarmVoltage(), ProgramGPIO(config), SetGPIOOutputs(bits) |
bool |
| Range and power | SetRange(range), SetPowerDown(pd) |
bool |
| Conversion | CountToVoltage(), VoltageToCount(), SetVref(), SetVA(), GetActiveVref() |
volts, counts |
Reads return a struct with an error field and an ok() helper (Error::SpiError when the port's transfer()
reported a bus error). The other calls return false for an argument out of range (a channel above 11) or a
failed transfer; see Porting.
Initialise¶
#include "ads7952.hpp"
#include <cstdio>
int read_adc(MySpi& spi) {
ads7952::ADS7952<MySpi> adc(spi, 2.5f, 5.0f); // REFP = 2.5 V, +VA = 5 V
if (!adc.EnsureInitialized()) { // 1. discard power-up frame, program sequences
return -1;
}
const ads7952::ReadResult ch3 = adc.ReadChannel(3); // 2. one channel (Manual mode)
if (!ch3.ok()) {
return -1; // Timeout: wiring, SPI mode or CS framing
}
std::printf("CH3 = %.3f V (%u counts)\n", ch3.voltage, static_cast<unsigned>(ch3.count));
const ads7952::ChannelReadings all = adc.ReadAllChannels(); // 3. every channel (Auto-1 mode)
for (uint8_t ch = 0; ch < 12; ++ch) {
if (all.hasChannel(ch)) {
std::printf("CH%u = %.3f V\n", static_cast<unsigned>(ch), all.voltage[ch]);
}
}
return all.ok() ? 0 : -1;
}
EnsureInitialized() discards the invalid first conversion, programs the Auto-1 mask and the Auto-2 last channel
from ADS7952_CFG and enters Auto-1. A second call returns true without SPI traffic; pass force = true after
the chip was reset or lost power. Every read returns Error::NotInitialized until it has succeeded.
Configure¶
// Auto-1 scans any subset; Auto-2 scans CH0..last. Program them once after EnsureInitialized().
if (!adc.ProgramAuto1Channels(ads7952::ChannelMask(0, 1, 4, 5))) {
return false;
}
if (!adc.ProgramAuto2LastChannel(7)) { // CH0..CH7
return false;
}
// 0..2 x VREF: only valid while 2 x VREF <= +VA (2.5 V reference, 5 V supply).
if (!adc.SetRange(ads7952::Range::TwoVref)) { // in effect on the chip when it returns
return false;
}
Build masks with ads7952::ChannelMask(0, 2, 4), ChannelRangeMask(first, last) or the constants
kAllChannels, kEvenChannels, kOddChannels, kFirstFour, kSecondFour, kThirdFour. Bit N is channel N.
SetRange() is in effect on the chip when it returns, in whatever mode the chip is in, and the driver's full scale
changes with it. Modes and the frame pipeline shows the
two frames it sends.
Read¶
// One channel: Manual mode, three frames, keyed on the channel address in the reply.
const ads7952::ReadResult r = adc.ReadChannel(4);
if (r.ok()) {
std::printf("CH%u: %u counts, %.3f V\n", static_cast<unsigned>(r.channel),
static_cast<unsigned>(r.count), r.voltage);
}
// Every channel of the Auto-1 mask: one frame per channel plus two.
const ads7952::ChannelReadings all = adc.ReadAllChannels();
for (uint8_t ch = 0; ch < ads7952::ChannelReadings::MAX_CHANNELS; ++ch) {
if (all.hasChannel(ch)) {
std::printf("CH%u: %.3f V\n", static_cast<unsigned>(ch), all.voltage[ch]);
}
}
if (!all.ok()) {
std::printf("%u of the programmed channels arrived\n", static_cast<unsigned>(all.validChannelCount()));
}
| Call | Frames (healthy) | Frames (most) | Leaves the chip in |
|---|---|---|---|
ReadChannel(ch) |
3 | 1 + MANUAL_READ_MAX_FRAMES |
Manual, on ch |
ReadAllChannels() |
N + 2 | 1 + N + READ_ALL_MAX_EXTRA_FRAMES |
Auto-1 |
N is the number of channels in the Auto-1 mask.
ReadAllChannels() always uses the Auto-1 mask. ChannelReadings::valid_mask says which entries of count[] and
voltage[] were filled; on Error::Timeout the channels that did arrive are still valid.
Program alarms and GPIOs¶
// GPIO0 goes high while any channel is outside its window (active-high output).
ads7952::GPIOConfig gpio;
gpio.alarm_mode = ads7952::GPIO01AlarmMode::GPIO0_HighAndLowAlarm;
gpio.direction_mask = ads7952::gpio::kGPIO2 | ads7952::gpio::kGPIO3; // GPIO2, GPIO3: outputs
if (!adc.ProgramGPIO(gpio)) {
return false;
}
// A window of 0.5 V .. 2.0 V on CH0, from volts with the active reference ...
if (!adc.ProgramAlarmVoltage(0, ads7952::AlarmBound::Low, 0.5f) ||
!adc.ProgramAlarmVoltage(0, ads7952::AlarmBound::High, 2.0f)) {
return false;
}
// ... or from counts. The chip keeps the top 10 bits (4-count steps).
if (!adc.ProgramAlarm(1, ads7952::AlarmBound::High, 3000)) {
return false;
}
Each channel has a high and a low threshold. The chip compares the top 10 bits of each conversion with them, so a
threshold has a resolution of 4 counts. ProgramAlarmVoltage() converts with the reference that is active when you
call it: program the thresholds again after SetRange() or SetVref().
GPIO01AlarmMode |
DI06:04 | GPIO0 | GPIO1 |
|---|---|---|---|
GPIO |
000 |
GPIO | GPIO |
GPIO0_HighAndLowAlarm |
001 |
High or low alarm | GPIO |
GPIO0_HighAlarm |
010 |
High alarm | GPIO |
GPIO1_LowAlarm |
100 |
GPIO | Low alarm |
GPIO1_LowAlarm_GPIO0_HighAlarm |
110 |
High alarm | Low alarm |
The chip has no code that makes GPIO1 a high-alarm output (SLAS605C Table 11). GPIO1_HighAlarm, the old, misleading
name of GPIO1_LowAlarm, is kept as a deprecated alias with the same code.
GPIOConfig::gpio2_as_range_input and gpio3_as_powerdown_input hand the range and power-down control to those
pins; reset_all_registers resets every chip register in the next frame. On the VQFN package only GPIO0 exists.
Write outputs and power down¶
// Each call is in effect on the chip when it returns.
if (!adc.SetGPIOOutputs(ads7952::gpio::kGPIO2)) { // GPIO2 high, GPIO3 low
return false;
}
if (!adc.SetPowerDown(ads7952::PowerDown::PowerDown)) { // the chip is powered down
return false;
}
if (!adc.SetPowerDown(ads7952::PowerDown::Normal)) { // awake again
return false;
}
SetGPIOOutputs() and SetPowerDown() work like SetRange(): when they return, the pins show the new levels, or
the chip is powered down (about 1 µA) or awake again. Waking takes 1 µs and costs one invalid conversion, which
falls in the frame the driver drops.
Read raw frames¶
// Bring-up: clock frames by hand and see the address pipeline (CH in DO15:12, count in DO11:0).
uint16_t words[4] = {};
const uint8_t n = adc.RawManualFrames(6, words, 4);
for (uint8_t i = 0; i < n; ++i) {
std::printf("frame %u: CH%u = %u\n", static_cast<unsigned>(i),
static_cast<unsigned>(words[i] >> 12), static_cast<unsigned>(words[i] & 0x0FFF));
}
RawManualFrames() sends one Manual frame and n - 1 continue frames and returns every reply untouched. Use it
on the bench to see the address pipeline advance; it leaves the chip in Manual mode.
Handle errors¶
const ads7952::ReadResult r = adc.ReadChannel(2);
switch (r.error) {
case ads7952::Error::Ok:
break;
case ads7952::Error::NotInitialized: // call EnsureInitialized() first
adc.EnsureInitialized();
break;
case ads7952::Error::InvalidChannel: // channel > 11
break;
case ads7952::Error::Timeout: // the address never came back: check the bus
adc.EnsureInitialized(true); // after a reset or brown-out, program again
break;
case ads7952::Error::SpiError: // the port's transfer() returned false
break;
default:
break;
}
| Signal | Meaning | What to do |
|---|---|---|
Error::NotInitialized |
EnsureInitialized() has not succeeded |
Call it |
Error::InvalidChannel |
Channel above 11 | Fix the caller |
Error::Timeout |
The requested channel address never came back in the frame budget | Check SPI mode, CS framing and MISO; re-initialise after a chip reset |
Error::SpiError |
The port's transfer() returned false |
Check the port and the bus; the read stopped at that frame |
A bool call returns false |
Channel or last channel above 11, or the port's transfer() returned false |
Fix the caller, or check the bus |
ProgrammingFailed and ModeChangeFailed exist in the enum but no call returns them today.
Troubleshooting maps symptoms to causes.
Calibrate the conversion¶
// Software only: the scale used by CountToVoltage(), VoltageToCount() and the results.
adc.SetVref(measured_vref); // e.g. 2.497 V measured at REFP
const float v = adc.CountToVoltage(2048); // with the active reference (range applied)
const uint16_t c = ads7952::VoltageToCount(1.25f, adc.GetActiveVref());
std::printf("2048 counts = %.4f V, 1.25 V = %u counts\n", v, static_cast<unsigned>(c));
SetVref() and SetVA() only change the numbers the driver converts with; they send nothing. Measure REFP once on
your board and pass the real value for the best absolute accuracy. Configuration covers the
limits these values are clamped to.