SPI Communication Setup for ADAR1000 and ADF4382 Devices in PLFM RADAR Firmware

The PLFM RADAR repository uses a layered SPI abstraction with software-controlled chip-selects on shared SPI4 to drive the ADAR1000 phase-shifter arrays and ADF4382 frequency synthesizers from an STM32F7.

This implementation targets RF beam-forming applications where precise synchronization between transmit and receive local oscillators is critical. The firmware structures SPI operations through platform-independent operations that decouple hardware details from device-specific register manipulation, allowing clean initialization sequences and reliable multi-device coordination.

Hardware Abstraction Layer

The foundation rests in adf4382a_manager.c (lines 8-14) where HAL handles and GPIO definitions are declared:

  • hspi4 — the shared STM32 SPI peripheral
  • TX_CE_Pin / RX_CE_Pin — chip-enable lines for power sequencing
  • TX_CS_Pin / RX_CS_Pin — software-controlled chip-selects
  • Timer handles for PWM-based DELADJ and DELSTR control

This separation allows the same SPI bus to service multiple devices without electrical contention.


SPI "Extra" Structure for Software Chip-Select

Each device instance carries a stm32_spi_extra struct containing the HAL handle and GPIO information for its dedicated CS line:

// TX synthesizer SPI extras
spi_tx_extra.hspi    = &hspi4;
spi_tx_extra.cs_port = TX_CS_GPIO_Port;
spi_tx_extra.cs_pin  = TX_CS_Pin;

// RX synthesizer SPI extras  
spi_rx_extra.hspi    = &hspi4;
spi_rx_extra.cs_port = RX_CS_GPIO_Port;
spi_rx_extra.cs_pin  = RX_CS_Pin;

Source: adf4382a_manager.c lines 56-64

The stm32_spi_ops implementation in stm32_spi.c automatically toggles these GPIO lines around each transfer, eliminating the need for manual CS management in higher-level code.


SPI Parameter Configuration

Both synthesizers share SPI4 with identical timing parameters but independent chip-select routing. The no_os_spi_init_param structure specifies:

Parameter Value Purpose
device_id ADF4382A_SPI_DEVICE_ID (4) Identifies the SPI peripheral
max_speed_hz ADF4382A_SPI_SPEED_HZ (10 MHz) Maximum SCK frequency
mode NO_OS_SPI_MODE_0 CPOL=0, CPHA=0 clock polarity/phase
bit_order NO_OS_SPI_BIT_ORDER_MSB_FIRST Register-compatible byte order
platform_ops &stm32_spi_ops HAL-backed transfer functions
extra &spi_tx_extra or &spi_rx_extra Device-specific GPIO context
manager->spi_tx_param.device_id    = ADF4382A_SPI_DEVICE_ID;
manager->spi_tx_param.max_speed_hz = ADF4382A_SPI_SPEED_HZ;
manager->spi_tx_param.mode        = NO_OS_SPI_MODE_0;
manager->spi_tx_param.chip_select = TX_CS_Pin;
manager->spi_tx_param.bit_order   = NO_OS_SPI_BIT_ORDER_MSB_FIRST;
manager->spi_tx_param.platform_ops = &stm32_spi_ops;
manager->spi_tx_param.extra       = &spi_tx_extra;

Source: adf4382a_manager.c lines 65-73

The RX side configuration (lines 74-82) mirrors this structure with &spi_rx_extra substituted.


Chip-Enable Power Sequencing

Before SPI transactions begin, the manager asserts CE pins for both devices with controlled rise timing:

set_chip_enable(TX_CE_Pin, true);
set_chip_enable(RX_CE_Pin, true);
no_os_udelay(1000);   // 1 ms settle time

Source: adf4382a_manager.c lines 22-28

This power-up sequence guarantees stable supply rails before the ADF4382 PLLs start their lock acquisition.


Device Initialization Flow in ADF4382A_Manager_Init()

The complete initialization spanning lines 36-91 and 124-191 follows this strict order:

  1. Assert CE pins — power-up both TX and RX synthesizers
  2. Initialize TX synthesizer (adf4382_init) — configure 10.5 GHz LO
  3. Delay 5 ms (no_os_udelay(5000)) — prevent supply sag between inits
  4. Initialize RX synthesizer (adf4382_init) — configure 10.38 GHz LO
  5. Set output power (adf4382_set_out_power) — 12 dB default for both
  6. Enable channel 0, disable channel 1 (adf4382_set_en_chan) — match hardware wiring
  7. Set manager->initialized = true — gate subsequent sync operations

The initialization flag at step 7 fixes a historical bug where sync methods were called before device readiness (see comment lines 87-91).


ADAR1000 Phase-Shifter SPI Handling

The ADAR1000 driver follows the identical software-CS pattern, using stm32_spi_ops from the same abstraction layer. Register writes execute through:

HAL_GPIO_WritePin(ADAR1000_CS_GPIO_Port, ADAR1000_CS_Pin, GPIO_PIN_RESET);
stm32_spi_transfer(&spi_handle, tx_buf, rx_buf, len);
HAL_GPIO_WritePin(ADAR1000_CS_GPIO_Port, ADAR1000_CS_Pin, GPIO_PIN_SET);

GPIO definitions for ADAR1000_CS_Pin and ADAR1000_CE_Pin reside in the CubeMX-generated main.h. The driver source files (adar1000.c, adar1000.h) are located in the same firmware directory as the ADF4382 components.


Complete Usage Example

Initialize both synthesizers and apply phase shift:

#include "adf4382a_manager.h"

int main(void)
{
    ADF4382A_Manager lo_mgr;
    int ret;

    ret = ADF4382A_Manager_Init(&lo_mgr, SYNC_METHOD_TIMED);
    if (ret != ADF4382A_MANAGER_OK) {
        printf("ADF4382 init failed: %d\n", ret);
        return ret;
    }

    /* 500 ps phase shift on TX device (index 0) */
    ret = ADF4382A_SetPhaseShift(&lo_mgr, 0, 500);
    if (ret) 
        printf("TX phase-shift failed %d\n", ret);
}

Summary

  • Shared SPI4 peripheral with software-controlled CS enables dual synthesizer operation without hardware multiplexers
  • stm32_spi_extra structs bind each logical device to distinct GPIO pins for independent addressing
  • Strict power sequencing (CE assertion, 1 ms delay) precedes any register access
  • Initialization flag prevents premature sync method execution
  • Common stm32_spi_ops abstraction unifies ADAR1000 and ADF4382 drivers for maintainability

Frequently Asked Questions

What SPI mode does the PLFM RADAR firmware use for ADAR1000 and ADF4382?

The firmware configures SPI mode 0 (NO_OS_SPI_MODE_0) with CPOL=0 and CPHA=0, operating at 10 MHz maximum speed with MSB-first bit order. These parameters match the register interface requirements of both Analog Devices components.

Why use software-controlled chip-select instead of hardware NSS?

Software CS allows independent device selection on a shared SPI bus. The ADF4382 TX and RX synthesizers both connect to SPI4 but use separate GPIO pins (TX_CS_Pin, RX_CS_Pin). This eliminates external mux hardware while the stm32_spi_ops layer handles automatic toggle timing around each transfer.

How does the firmware synchronize the TX and RX local oscillators?

After successful initialization (manager->initialized = true), the manager selects between TIMED sync (ADF4382A_SetupTimedSync) or EZSYNC (ADF4382A_SetupEZSync) methods. TIMED uses programmable delays for deterministic phase alignment; EZSYNC relies on hardware reference edges for simpler synchronization.

Where are the GPIO pin definitions located for SPI chip-selects?

All GPIO port/pin mappings — including TX_CS_Pin, RX_CS_Pin, TX_CE_Pin, RX_CE_Pin, and ADAR1000_CS_Pin — are generated by STM32CubeMX and stored in 9_Firmware/9_1_Microcontroller/Inc/main.h. These symbols are referenced throughout adf4382a_manager.c and the ADAR1000 driver.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →