Skip to content

Instantly share code, notes, and snippets.

@stavinsky
Last active December 23, 2024 10:29
Show Gist options
  • Save stavinsky/fd75b5b292349119983727f9f5708cc6 to your computer and use it in GitHub Desktop.
Save stavinsky/fd75b5b292349119983727f9f5708cc6 to your computer and use it in GitHub Desktop.
adf4351 controlled by ftdi chip
import argparse
import struct
from pyftdi.ftdi import Ftdi
from math import log2
from pyftdi.spi import SpiController
def calculate_adf4351_registers(
f_out,
f_ref,
r_div=1,
doubler=False,
output_power=3,
output_divider=None,
):
# Auto-calculate output divider if not specified
if output_divider is None:
for d in [1, 2, 4, 8, 16, 32, 64]:
f_vco = f_out * d
if 2200e6 <= f_vco <= 4400e6:
output_divider = d
break
else:
raise ValueError("Output frequency is out of range.")
else:
f_vco = f_out * output_divider
# Phase Detector Frequency
f_pfd = f_ref / r_div * (2 if doubler else 1)
# Integer-N and Fractional-N calculations
n = int(f_vco / f_pfd)
mod = 2
frac = 0
while mod <= 4096:
frac = int(((f_vco / f_pfd) - n) * (mod)) # 12-bit fractional
result_freq = (n + (frac / mod)) * f_pfd / output_divider
if (result_freq - f_out) == 0:
break
mod = mod + 1
# r1
mod = mod << 3
phase_value = 1 << 15
prescaller = 1 << 27
# R2
charge_pump_current = 0b0111 << 9
pd_polarity = 1 << 6
r_counter = r_div << 14
# R4
rf_output_enable = 1 << 5
vco_power_down = 0 << 11
mtld = 0 << 10
aux_output_select = 0 << 9
aux_output_enable = 0b0 << 8
aux_output_power = 0 << 6
band_select_clock_div = 80 << 12
feedback_select = 1 << 23
# Registers
# r0 = (n << 15) | (frac << 3) | 0x0 # N and FRAC values
r0 = (n << 15) | (frac << 3) | 0x0 # N and FRAC values
# r1 = (mod << 3) | 0x1
r1 = prescaller | phase_value | mod | 0x1
# r2 = (0x1 << 28) | (0x1 << 27) | (0x3 << 15) | (0x1 << 9) | (int(charge_pump_current * 10) << 12) | 0x2
r2 = r_counter | pd_polarity | charge_pump_current | 0x2
r3 = 0x4B3 | 0x3
r4 = (
feedback_select
| (round(log2(output_divider)) << 20)
| band_select_clock_div
| vco_power_down
| mtld
| aux_output_select
| aux_output_enable
| aux_output_power
| rf_output_enable
| (output_power << 3)
| 0x4
)
r5 = 0x580005 | 0x5
return r0, r1, r2, r3, r4, r5
class AD4351:
@staticmethod
def scan():
Ftdi.show_devices()
def __init__(
self,
conn_string: str = "ftdi://ftdi:2232:FT2232HL/1",
f_ref: int = int(10e6),
output_power: int = 3,
):
spi = SpiController()
spi.configure(conn_string)
slave = spi.get_port(cs=0, freq=10e6, mode=0)
gpio = spi.get_gpio()
gpio.set_direction(0x10, 0x10)
gpio.write(0b0 << 4)
self._gpio = gpio
self._slave = slave
self._registers = []
self.enable(status=True)
self.f_ref = f_ref
self.output_power = output_power
def enable(self, status: bool = True):
# pin 4 for power pin
if status:
self._gpio.write(0b1 << 4)
else:
self._gpio.write(0b0 << 4)
def write_registers(self):
for reg in self._registers[::-1]: # Send R5 to R0
self._slave.write(struct.pack(">I", reg))
def set_frequency(self, freq: int = int(35e6)):
self._registers = calculate_adf4351_registers(
freq, self.f_ref, output_power=self.output_power
)
self.write_registers()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="ADF4351 Command-Line Interface")
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# 'scan' command
scan_parser = subparsers.add_parser("scan", help="Scan available frequencies")
# 'set_frequency' command
set_frequency_parser = subparsers.add_parser(
"set_frequency", help="Set output frequency"
)
set_frequency_parser.add_argument(
"frequency",
type=float,
help="Frequency in Hz to set (e.g., 100000000 for 100 MHz)",
)
set_frequency_parser.add_argument(
"--device",
type=str,
default="ftdi://ftdi:2232:FT2232HL/1",
help="Target device identifier (default: 'ftdi://ftdi:2232:FT2232HL/1')",
)
# Parse the arguments
args = parser.parse_args()
# Handle the commands
if args.command == "scan":
AD4351.scan()
elif args.command == "set_frequency":
pll = AD4351(args.device)
pll.enable()
pll.set_frequency(int(args.frequency))
else:
parser.print_help()
## usage python main.py set_frequency 100e6
@stavinsky
Copy link
Author

CS pin should be connected to LE pin of IC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment