Last active
December 23, 2024 10:29
-
-
Save stavinsky/fd75b5b292349119983727f9f5708cc6 to your computer and use it in GitHub Desktop.
adf4351 controlled by ftdi chip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
CS pin should be connected to LE pin of IC