Skip to content

Quickstart

Read all 16 inputs of an ADS9324 from your MCU: wire seven signals, write two small port classes, call three driver functions.

1. Wire it

MCU ADS9324 (pin) Notes
SPI SCLK SCLK (37) SPI mode 0; up to 60 MHz, start at 1 to 8 MHz
SPI MOSI SDI (36) Register frames, zeros during a conversion read
SPI MISO SDOUT (38) Register read-back and, in the driver's mode, conversion data
SPI CS CS (35) Active low; one CS-low window per transfer
GPIO out CONVST (50) The falling edge samples all channels
GPIO in DRDY/ALARM (49) Optional; active high when a conversion is ready
IOVDD RESET (32), REFSEL (31) RESET is active low: hold it high. REFSEL high selects the internal reference
GND GND Common ground; also the thermal pad

The ADS9324 needs three supplies (5 V, 1.8 V and the 1.7 V to 3.6 V I/O rail) and its reference capacitors before it answers. The Hardware page has the full pin list, the supplies and the timing.

2. Add the driver

Add the repository as a submodule and link the hf::ads9324 CMake target (or add inc/ and src/ to your include path and build with C++20). Installation covers CMake and ESP-IDF.

git submodule add https://github.com/hardfoc/hf-ads9324-driver.git external/hf-ads9324-driver

3. Write the port

The port is the only platform code: one SPI transfer, the CONVST and DRDY pins, and two delays. Replace the platform_* calls with your SDK's.

my_port.hpp
// SPI: one CS-low window per call, SPI mode 0, MSB first.
class MySpi : public ads9324::SpiInterface<MySpi> {
public:
  // tx == nullptr: clock out zeros. rx == nullptr: discard what arrives on SDOUT.
  // Return false on a bus error: the driver then fails the call that sent the frame.
  bool transfer(const uint8_t* tx, uint8_t* rx, std::size_t len) {
    return platform_spi_transfer(tx, rx, len);
  }
};

// Host: the CONVST output, the optional DRDY input and two delays.
class MyHost : public ads9324::HostInterface<MyHost> {
public:
  static constexpr int kConvst = 3;   // GPIO wired to CONVST (pin 50)
  static constexpr int kDrdy = 11;    // GPIO wired to DRDY/ALARM (pin 49)
  static constexpr int kReset = 12;   // GPIO wired to RESET (pin 32); optional

  void ConvstWrite(bool high) { platform_gpio_write(kConvst, high); }
  bool DrdyRead() { return platform_gpio_read(kDrdy); }   // high = conversion done
  bool HasDrdy() const { return true; }                   // false: fixed wait instead
  void DelayUs(uint32_t us) { platform_delay_us(us); }
  void DelayMs(uint32_t ms) { platform_delay_us(ms * 1000U); }
  void ResetWrite(bool high) {
    platform_gpio_write(kReset, high);
  } // optional: HardwareReset()
};

HasDrdy() returning false makes the driver wait a fixed time after CONVST instead of polling DRDY. Porting explains each method.

4. Read the channels

main.cpp
#include "ads9324.hpp"

int read_all_16_channels(MySpi& spi, MyHost& host) {
  ads9324::ADS9324<MySpi, MyHost> adc(spi, host);

  if (!adc.EnsureInitialized()) {                    // 1. reset, 1-lane SDOUT, DEVICE_ID
    return -1;                                       // wiring, supplies or SPI mode
  }

  ads9324::ChannelConfig cfg;                        // 2. every input: differential, ±10 V
  cfg.type = ads9324::InputType::Differential;
  cfg.range = ads9324::InputRange::PlusMinus10V;
  if (!adc.ConfigureAllChannels(cfg)) {
    return -2;
  }

  const ads9324::Snapshot snap = adc.ReadSnapshot();  // 3. CONVST, wait DRDY, read 32 bytes
  if (!snap.ok()) {
    return -3;                                       // snap.error says why
  }
  std::printf("AIN1 = %.4f V\n", static_cast<double>(snap.voltage[0]));  // 4. channel 0 is AIN1
  return 0;
}

What each step does:

  1. EnsureInitialized() waits 30 ms for the supplies and reference, resets the chip, writes GEN_CFG3 = 0x0032 (1 lane, 16-bit words, data on SDOUT), checks DEVICE_ID = 0x0004 and writes the compile-time PGA defaults (±5 V differential unless configured) to every channel. Nothing else works until it returns true.
  2. ConfigureAllChannels() writes each channel's PGA nibble and filter bit. The ranges are bipolar; the driver remembers them to convert counts to volts.
  3. ReadSnapshot() pulses CONVST, waits for DRDY, then clocks 32 bytes out of SDOUT in one CS-low window.
  4. Channel 0 in the API is AIN1 on the chip; channel 15 is AIN16.

This example is built by the host CI, so it compiles against the current headers.

If it does not work

Symptom First thing to check
EnsureInitialized() returns false ReadDeviceId(): 0x0000 means no SDOUT data (wiring, SPI mode, supplies); anything else but 0x0004 means a framing problem
Snapshot error Timeout CONVST wiring, DRDY polarity, or set HasDrdy() to false
All counts near zero Input type and range, a floating input in open-wire-safe mode, or the AINxM pin not connected
Voltages off by a fixed factor The range you configured is not the range you assumed (PGA and calibration)

Next