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

> Learn how to set up SPI communication for ADAR1000 and ADF4382 devices in PLFM RADAR firmware using STM32F7 and a layered SPI abstraction. Get efficient control for your radar system.

- Repository: [NawfalMotii79/PLFM_RADAR](https://github.com/NawfalMotii79/PLFM_RADAR)
- Tags: how-to-guide
- Published: 2026-08-20

---

**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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/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:

```c
// 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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/adf4382a_manager.c) lines 56-64

The `stm32_spi_ops` implementation in [`stm32_spi.c`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/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 |

```c
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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/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:

```c
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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/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:

```c
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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/main.h). The driver source files ([`adar1000.c`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/adar1000.c), [`adar1000.h`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/adar1000.h)) are located in the same firmware directory as the ADF4382 components.

---

## Complete Usage Example

Initialize both synthesizers and apply phase shift:

```c
#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`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_1_Microcontroller/Inc/main.h). These symbols are referenced throughout [`adf4382a_manager.c`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/adf4382a_manager.c) and the ADAR1000 driver.