Skip to content

Commit 1062e75

Browse files
committed
adi: admfm8000: initial helper class for FMCW TX board
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
1 parent 4779adb commit 1062e75

File tree

6 files changed

+605
-0
lines changed

6 files changed

+605
-0
lines changed

adi/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
from adi.adis16550 import adis16550
9191
from adi.adl5240 import adl5240
9292
from adi.adl5960 import adl5960
93+
from adi.admfm8000 import admfm8000
9394
from adi.admv8818 import admv8818
9495
from adi.adpd188 import adpd188
9596
from adi.adpd410x import adpd410x

adi/admfm8000.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Copyright (C) 2026 Analog Devices, Inc.
2+
#
3+
# SPDX short identifier: ADIBSD
4+
5+
import warnings
6+
7+
from adi.ad9910 import ad9910
8+
from adi.adf41513 import adf41513
9+
from adi.adrf5720 import adrf5720
10+
11+
12+
class admfm8000:
13+
"""ADMFM8000 FMCW Transmitter
14+
15+
This class provides a high-level interface for controlling the ADMFM8000.
16+
The system consists of an AD9910 DDS, an ADF41513 PLL, and an ADRF5720 DSA.
17+
Helper functions to configure the DDS modes have the frequency values
18+
automatically scaled down based on the PLL reference input frequency and the
19+
desired VCO output frequency.
20+
"""
21+
22+
def __init__(self, uri="", vco_feedback_div=16, pll_refin_frequency=50e6):
23+
self.dds = ad9910(uri)
24+
self.pll = adf41513(uri)
25+
self.dsa = adrf5720(uri, "adrf5730")
26+
self._pll_refin_init_frequency = pll_refin_frequency
27+
self._pll_refin_frequency = pll_refin_frequency
28+
self._vco_feedback_div = vco_feedback_div
29+
self.vco_frequency = 25.6e9
30+
31+
@property
32+
def attenuation(self):
33+
"""Get/Set the DSA attenuation in dB"""
34+
return self.dsa.attenuation
35+
36+
@attenuation.setter
37+
def attenuation(self, value):
38+
self.dsa.attenuation = value
39+
40+
@property
41+
def pll_refin_frequency(self):
42+
"""Get/Set the PLL reference input frequency in Hz
43+
44+
This value should represent the center frequency of the signal generated
45+
by the DDS that is fed into the PLL reference input.
46+
"""
47+
return self._pll_refin_frequency
48+
49+
@pll_refin_frequency.setter
50+
def pll_refin_frequency(self, value):
51+
if value > 80e6 or value < 10e6:
52+
raise ValueError("Invalid PLL RefIn frequency")
53+
self._pll_refin_frequency = value
54+
self.vco_frequency = self._vco_frequency
55+
56+
@property
57+
def vco_frequency(self):
58+
"""Get/Set the VCO frequency output value in Hz
59+
60+
This value should represent the center frequency of the signal generated
61+
by the external VCO. It takes into account the current PLL settings and
62+
the VCO feedback divider.
63+
"""
64+
return self._vco_frequency
65+
66+
@vco_frequency.setter
67+
def vco_frequency(self, value):
68+
pll_multiplier = (value / self._vco_feedback_div) / self._pll_refin_frequency
69+
self.pll.frequency = pll_multiplier * self._pll_refin_init_frequency
70+
self._vco_frequency = value
71+
72+
def single_tone_config(self, profile=0, frequency=None, scale=None, phase=None):
73+
"""Configure the DDS for single tone output with respect to VCO frequency output.
74+
75+
:param profile: DDS Profile number
76+
:param frequency: Output frequency in Hz
77+
:param scale: Output scale (0.0 to 1.0)
78+
:param phase: Output phase in radians
79+
"""
80+
self.dds.profile = profile
81+
if frequency is not None:
82+
freq_scale = self._pll_refin_frequency / self.vco_frequency
83+
self.dds.st.profiles[profile].frequency = frequency * freq_scale
84+
if phase is not None:
85+
self.dds.st.profiles[profile].phase = phase
86+
if scale is not None:
87+
self.dds.st.profiles[profile].scale = scale
88+
89+
def parallel_port_config(
90+
self, enable=True, frequency_np=None, cyclic=True, rate=None
91+
):
92+
"""Configure the DDS Parallel Port channel
93+
94+
:param enable: Enable/Disable the Parallel Port channel
95+
:param frequency_np: numpy array of frequency values in Hz
96+
:param cyclic: Enable/Disable cyclic mode for the DMA buffer
97+
:param rate: Rate of the Parallel Port channel in samples/second
98+
"""
99+
if enable:
100+
self.dds.parallel_port.enable = 1
101+
else:
102+
self.dds.tx_destroy_buffer()
103+
self.dds.parallel_port.enable = 0
104+
105+
if rate is not None:
106+
try:
107+
self.dds.parallel_port.rate = rate
108+
except Exception as ex:
109+
warnings.warn(f"Failed to set Parallel Port rate: {ex}")
110+
111+
if frequency_np is not None:
112+
freq_scale = self._pll_refin_frequency / self.vco_frequency
113+
self.dds.parallel_port.frequency_push(
114+
frequency_np * freq_scale, cyclic=cyclic
115+
)
116+
117+
def digital_ramp_config(
118+
self,
119+
enable=None,
120+
mode=None,
121+
freq_min=None,
122+
freq_max=None,
123+
inc_step=None,
124+
dec_step=None,
125+
inc_rate=None,
126+
dec_rate=None,
127+
inc_ramp_time=None,
128+
dec_ramp_time=None,
129+
**kwargs,
130+
):
131+
"""Configure the DDS Digital Ramp Generator (DRG)
132+
133+
:param enable: Enable/Disable the Digital Ramp Generator
134+
:param mode: DRG Operating Mode as defined in ad9910.digital_ramp_generator.mode
135+
:param freq_min: Minimum frequency in Hz
136+
:param freq_max: Maximum frequency in Hz
137+
:param inc_step: Increment step in Hz
138+
:param dec_step: Decrement step in Hz
139+
:param inc_rate: Increment rate in samples/second
140+
:param dec_rate: Decrement rate in samples/second
141+
:param inc_ramp_time: Ramp time for increment in seconds
142+
:param dec_ramp_time: Ramp time for decrement in seconds
143+
:param kwargs: Additional DRG attributes to set as key-value pairs
144+
"""
145+
freq_scale = self._pll_refin_frequency / self.vco_frequency
146+
self.dds.drg.destination = ad9910.destination.FREQUENCY
147+
148+
for attr, value in kwargs.items():
149+
try:
150+
self.dds.drg._set_iio_attr(attr, value)
151+
except Exception as ex:
152+
warnings.warn(f"Failed to set attribute {attr} on DRG channel: {ex}")
153+
154+
if freq_min is not None:
155+
self.dds.drg.frequency.min = freq_min * freq_scale
156+
if freq_max is not None:
157+
self.dds.drg.frequency.max = freq_max * freq_scale
158+
if mode is not None:
159+
self.dds.drg.operating_mode = mode
160+
161+
if inc_step is None and inc_rate is None and inc_ramp_time is not None:
162+
inc_rate = self.dds.sysclk_frequency / 4 # max DRG update rate
163+
inc_step = (freq_max - freq_min) / (inc_rate * inc_ramp_time)
164+
165+
if dec_step is None and dec_rate is None and dec_ramp_time is not None:
166+
dec_rate = self.dds.sysclk_frequency / 4 # max DRG update rate
167+
dec_step = (freq_max - freq_min) / (dec_rate * dec_ramp_time)
168+
169+
if inc_step is not None:
170+
self.dds.drg.frequency.increment = inc_step * freq_scale
171+
if dec_step is not None:
172+
self.dds.drg.frequency.decrement = dec_step * freq_scale
173+
if inc_rate is not None:
174+
self.dds.drg.increment_rate = inc_rate
175+
if dec_rate is not None:
176+
self.dds.drg.decrement_rate = dec_rate
177+
if enable is not None:
178+
self.dds.drg.enable = 1 if enable else 0
179+
180+
def ram_control_profile_config(
181+
self, profile=0, mode=None, addr_range=None, rate=None
182+
):
183+
"""Configure the DDS RAM Control Profiles
184+
185+
:param profile: RAM Control Profile number
186+
:param mode: RAM Control Mode as defined in ad9910.ram_control.mode
187+
:param addr_range: RAM address range as a tuple (start_addr, end_addr)
188+
:param rate: RAM update rate in samples/second
189+
"""
190+
self.dds.profile = profile
191+
if isinstance(addr_range, tuple) and len(addr_range) == 2:
192+
self.dds.ram.profiles[profile].address_range = addr_range
193+
if mode is not None:
194+
self.dds.ram.profiles[profile].operating_mode = mode
195+
if rate is not None:
196+
self.dds.ram.profiles[profile].rate = rate
197+
198+
def ram_control_config(self, enable=True, frequency_np=None):
199+
"""Load frequency list with respect to VCO frequency output into DDS RAM in Hz
200+
201+
:param enable: Enable/Disable the RAM Mode
202+
:param frequency_np: numpy array of frequency values in Hz
203+
"""
204+
if frequency_np is not None:
205+
self.dds.ram.destination = ad9910.destination.FREQUENCY
206+
207+
if self.dds.ram.enable == 1:
208+
# make sure RAM is disabled before loading new values
209+
self.dds.ram.enable = 0
210+
211+
freq_scale = self._pll_refin_frequency / self.vco_frequency
212+
self.dds.ram.frequency_load(frequency_np * freq_scale)
213+
214+
self.dds.ram.enable = 1 if enable else 0
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
adi.admfm8000 module
2+
====================
3+
4+
.. automodule:: adi.admfm8000
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

doc/source/devices/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Supported Devices
9696
adi.adis16550
9797
adi.adl5240
9898
adi.adl5960
99+
adi.admfm8000
99100
adi.admv8818
100101
adi.adpd1080
101102
adi.adpd188

0 commit comments

Comments
 (0)