ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ad9238.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ad9238.h
6 * @brief SPI configuration-port codec for the AD9238 (and the shared ADI high-speed-ADC SPI
7 * map it belongs to) - PC_ENABLE_AD9238.
8 *
9 * The AD9238 (12-bit, 20/40/65 MSPS dual ADC) has TWO interfaces that must not be confused:
10 * - The **sample data path**: a parallel CMOS/LVDS bus (12 data lines + DCO/output clock per
11 * channel) run far beyond what a microcontroller can bit-bang at 20-65 MSPS. That path is
12 * NOT this file - it is out of scope for direct MCU capture; see reverse_engineering/README.md
13 * for the FPGA/CPLD-buffered burst-drain architecture this project actually uses.
14 * - The **SPI configuration port** (SCLK / SDIO / CSB, 3-wire, MSB first): a low-speed,
15 * low-throughput control channel for power-down, output data format, output test patterns,
16 * and offset trim - register writes only, never the sample stream. This file is that codec:
17 * it builds the 16-bit instruction word (R/W + 2-bit byte-count + 13-bit address) and the
18 * shadow-register "device update" transfer that the whole ADI high-speed-ADC generation of
19 * this era (AD9238 and its close siblings) share, per the AD9238 Rev. D datasheet's SPI
20 * Register Map. Pure codec (builds/parses byte sequences); the SPI clocking is the app's -
21 * same contract as every other codec in this library (see services/instrumentation/scpi,
22 * services/instrumentation/gpib).
23 *
24 * **Confidence note.** The instruction-word framing and the transfer-register mechanism are the
25 * standardized shape used across this ADI ADC generation and are implemented directly from that
26 * convention. The specific per-register bit-field constants below (@ref Ad9238Reg and the test-
27 * pattern / output-mode enums) transcribe the AD9238 Rev. D datasheet's register table as best
28 * documented; **confirm every address and bit position against your part's datasheet revision
29 * before writing to real silicon** - per this project's hardware-verification policy
30 * (docs/KNOWN_LIMITATIONS.md), nothing here has been validated against a physical AD9238 yet.
31 *
32 * Reference: Analog Devices AD9238 Data Sheet Rev. D, "Serial Port Interface (SPI)" section.
33 *
34 * @author Douglas Quigg (dstroy0)
35 * @date 2026
36 */
37
38#ifndef PROTOCORE_AD9238_H
39#define PROTOCORE_AD9238_H
40
41#include "protocore_config.h"
42
43#if PC_ENABLE_AD9238
44
45#include <stddef.h>
46#include <stdint.h>
47
48/** @brief SPI register addresses (13-bit address field). Verify against your datasheet revision. */
49enum class Ad9238Reg : uint16_t
50{
51 AD9238_REG_CHIP_PORT_CONFIG = 0x00, ///< SDIO direction, LSB/MSB-first, soft reset (mirrored bits)
52 AD9238_REG_CHIP_ID = 0x01, ///< read-only device id
53 AD9238_REG_CHIP_GRADE = 0x02, ///< read-only speed-grade id
54 AD9238_REG_CHANNEL_INDEX = 0x08, ///< bit0 = channel A, bit1 = channel B (selects which
55 ///< channel the following per-channel registers target)
56 AD9238_REG_POWER_DOWN = 0x09, ///< per-channel: bit0 power-down, bit1 standby
57 AD9238_REG_OUTPUT_MODE = 0x0A, ///< per-channel: data format + output invert (see Ad9238OutputFormat)
58 AD9238_REG_OUTPUT_PHASE = 0x0D, ///< per-channel: output clock phase adjust
59 AD9238_REG_OUTPUT_DELAY = 0x0F, ///< per-channel: output data valid delay
60 AD9238_REG_VREF = 0x10, ///< per-channel: internal reference full-scale select
61 AD9238_REG_ANALOG_INPUT = 0x14, ///< per-channel: analog input duty-cycle stabilizer enable
62 AD9238_REG_TEST_IO = 0x15, ///< per-channel: output test pattern select (see Ad9238TestPattern)
63 AD9238_REG_OFFSET_ADJUST = 0x18, ///< per-channel: digital offset trim
64 AD9238_REG_DEVICE_UPDATE = 0xFF, ///< bit0 = SW transfer: latch all shadowed writes into effect
65};
66
67/** @brief AD9238_REG_TEST_IO[2:0] - the output test pattern (0x15, bits 2:0). */
68enum class Ad9238TestPattern : uint8_t
69{
70 AD9238_TEST_OFF = 0x00, ///< normal operation
71 AD9238_TEST_MIDSCALE_SHORT = 0x01,
72 AD9238_TEST_POS_FULLSCALE = 0x02,
73 AD9238_TEST_NEG_FULLSCALE = 0x03,
74 AD9238_TEST_CHECKERBOARD = 0x04, ///< alternating 0xAAA/0x555 - the pipeline's self-test pattern
75 AD9238_TEST_PN23 = 0x05,
76 AD9238_TEST_PN9 = 0x06,
77 AD9238_TEST_ONE_ZERO_TOGGLE = 0x07,
78};
79
80/** @brief AD9238_REG_OUTPUT_MODE[1:0] - the output data format (0x0A, bits 1:0). */
81enum class Ad9238OutputFormat : uint8_t
82{
83 AD9238_FORMAT_OFFSET_BINARY = 0x00,
84 AD9238_FORMAT_TWOS_COMPLEMENT = 0x01,
85 AD9238_FORMAT_GRAY_CODE = 0x02,
86};
87
88/** @brief Which channel a per-channel register write targets (AD9238_REG_CHANNEL_INDEX bits). */
89enum class Ad9238Channel : uint8_t
90{
91 AD9238_CHAN_A = 0x01,
92 AD9238_CHAN_B = 0x02,
93 AD9238_CHAN_BOTH = 0x03,
94};
95
96/**
97 * @brief Build the 16-bit SPI instruction word (MSB first on the wire: high byte then low byte).
98 * @param read true for a read transaction, false for a write.
99 * @param reg_addr 13-bit register address (@ref Ad9238Reg or a raw value).
100 * @param nbytes number of data bytes to follow (1-4; encoded as W1:W0 = nbytes-1, so 4 means
101 * "streaming" per the datasheet's byte-count field - pass 1 unless you know you
102 * want streaming mode).
103 * @param out2 receives the 2-byte instruction word.
104 * @return true; false only if @p out2 is null or @p nbytes is 0 or > 4.
105 */
106bool pc_ad9238_build_instruction(bool read, uint16_t reg_addr, uint8_t nbytes, uint8_t out2[2]);
107
108/**
109 * @brief Build a complete single-register write transaction (instruction word + one data byte)
110 * ready to clock out over SPI.
111 * @return 3 (bytes written to @p out), or 0 if @p out is null / @p cap < 3.
112 */
113size_t pc_ad9238_build_write(uint16_t reg_addr, uint8_t value, uint8_t *out, size_t cap);
114
115/**
116 * @brief Build a single-register read instruction (the 2-byte header; the caller then clocks one
117 * further byte to receive the register value, since this is a half-duplex codec with no
118 * transport of its own).
119 * @return 2 (bytes written to @p out), or 0 if @p out is null / @p cap < 2.
120 */
121size_t pc_ad9238_build_read(uint16_t reg_addr, uint8_t *out, size_t cap);
122
123/**
124 * @brief Build the "device update" transfer transaction (write 0x01 to AD9238_REG_DEVICE_UPDATE):
125 * every shadowed register write above needs this to take effect.
126 * @return 3, or 0 if @p out is null / @p cap < 3.
127 */
128size_t pc_ad9238_build_transfer(uint8_t *out, size_t cap);
129
130#endif // PC_ENABLE_AD9238
131
132#endif // PROTOCORE_AD9238_H
User-facing configuration for ProtoCore.