ADF4155
Recommended for New Designs
The ADF4155 allows implementation of fractional-N or integer-N phase-locked loop (PLL) frequency synthesizers when used with an external loop filter, external...
Datasheet
ADF4155 on Analog.com
Hello,
i have a frequency synthesizer board, uses ADF4155 and V600ME45-LF VCO in charge pump line. The loop filter is passive, I_CP set correctly, PD_Pol correct. The problem is the Vtune is too low, 60 mV. No matter which frequency I choose, the VCO Output is not changing nor is anything on RFOut changing? Can someone identify if there is some problem in my code? Or is it rather a hardware issue?
Thank you in advance.
The register bits are set in another helper file. Here is my core code:
#!/usr/bin/env python3
"""
AFD4155 Controller using FT232H via pyftdi
This script provides a class to control the AFD4155 frequency synthesizer
through SPI using an FT232H USB adapter and pyftdi library.
"""
import time
from pyftdi.spi import SpiController
from pll_h import *
from math import gcd
class AFD4155Controller:
"""
Controls an AFD4155 frequency synthesizer via SPI using FT232H
"""
# AFD4155 Register addresses
REG_N_DIVIDER = 0x00 # N divider register
REG_R_DIVIDER = 0x01 # R divider register
REG_CONTROL = 0x02 # Control register
def __init__(self, ftdi_url="ftdi://ftdi:232h/1"):
"""
Initialize the AFD4155 controller
Args:
ftdi_url: URL for the FTDI device (default: 'ftdi://ftdi:232h/1')
"""
self.spi_controller = SpiController(cs_count=2)
self.spi_controller.configure(ftdi_url)
# Configure SPI port 0 with mode 0 (CPOL=0, CPHA=0) and 1MHz
self.spi = self.spi_controller.get_port(cs=1, freq=1e6, mode=0)
# Initialize platform data and state
self.pdata = ADF4155PlatformData(
clkin=100e6, # Reference clock frequency in Hz
channel_spacing=1e6, # Channel spacing in Hz
ref_doubler_en=1, # Enable reference doubler
ref_div2_en=0, # Disable reference divider
r0_user_settings=0, # Default settings for Register 0
r1_user_settings=1, # Default settings for Register 1
r2_user_settings=2, # Default settings for Register 2
r3_user_settings=0x20000003, # Default settings for Register 3
r4_user_settings=4, # Default settings for Register 4
r5_user_settings=5, # Default settings for Register 5
r6_user_settings=6, # Default settings for Register 6
r7_user_settings=0x00000067, # Default settings for Register 7
r8_user_settings=8 # Default settings for Register 8
)
self.state = ADF4155State(self.pdata) # Initialize the state
# Reference frequency in MHz
self.doubler = 0 # Reference doubler (0 = disabled, 1 = enabled)
self.div2 = 0 # Reference divider (0 = disabled, 1 = enabled)
self.R = 1 # R divider (default: 1)
self.MOD1 = 4095 # Modulus for fractional-N synthesis (default: 4095)
self.r_counter = 1 # R counter value (default: 1)
# Initialize the chip with default settings
#self.reset()
#def reset(self):
"""Reset the AFD4155 to default settings"""
# Default control register: muxout=digital lock detect, power-down mode=normal
# self.write_register(self.REG_CONTROL, 0x002580)
def write_register(self, reg_addr, data):
"""
Write data to the specified register while preserving reserved bits.
Args:
reg_addr: Register address (0-8).
data: 32-bit data to write to the register (only user-configurable bits will be modified).
"""
# Get the reserved bit mask for the current register
reserved_mask = RESERVED_MASKS.get(reg_addr, 0x00000000)
# Read the current register value (if possible)
# If reading is not supported, use the default value from pll_h.py
current_value = self.state.reg_val[reg_addr]
# Preserve reserved bits and update only user-configurable bits
new_value = (current_value & reserved_mask) | (data & ~reserved_mask)
# Prepare the 32-bit word
word = (new_value & 0xFFFFFFF0) | (reg_addr & 0x0F)
# Convert to bytes (MSB first)
byte_data = word.to_bytes(4, byteorder="big")
# Debug print
print(f"Writing to R{reg_addr}: 0x{word:08X} (Bytes: {byte_data.hex()})")
# Send the data to the SPI bus
self.spi.exchange(byte_data)
# Update the state for the register that was written to
self.state.reg_val[reg_addr] = word
# Give some time for the chip to process the command
time.sleep(0.01)
def adf4155_set_freq(self, freq_MHz):
"""
Set the output frequency of the ADF4155 PLL.
Args:
freq_MHz: Desired output frequency in MHz.
Returns:
Actual set frequency in MHz.
"""
# Validate input frequency
if freq_MHz <= 0:
raise ValueError(f"Invalid frequency: {freq_MHz} MHz. Frequency must be greater than 0.")
state = self.state
REFIN = 100e6
MOD1 = 2**24
#RF_DIVIDER = 1
CHANNEL_SPACING = 200e3
ref_doubler_en = 0
ref_div2_en = 0
ref_freq = REFIN * (2 if ref_doubler_en else 1) / (2 if ref_div2_en else 1)
print(f"Reference Frequency: {ref_freq}")
R = 1
f_PFD = ref_freq / R
print(f"Phase Frequency Detector (f_PFD): {f_PFD}")
VCO_freq= freq_MHz*R
MOD2 = int(f_PFD / gcd(int(f_PFD), int(CHANNEL_SPACING)))
gcdd = gcd(int(f_PFD), int(CHANNEL_SPACING))
print(f"gcd: {gcdd}")
print(f"MOD2: {MOD2}")
print(f"MOD1: {MOD1}")
N = VCO_freq * 1e6 / f_PFD
print(f"N Counter: {N}")
INT = int(N)
frac = N - INT
print(f"INT: {INT}, FRAC: {frac}")
if INT < 23:
min_R = (freq_MHz * 1e6) / (23 * f_PFD)
R = int(min_R) + 1
f_PFD = ref_freq / R
N = freq_MHz * 1e6 / f_PFD
INT = int(N)
frac = N - INT
print(f"Adjusted R: {R}, New f_PFD: {f_PFD}, New INT: {INT}, New FRAC: {frac}")
if INT < 23:
raise ValueError(f"Unable to adjust R to ensure INT >= 23 for frequency {freq_MHz} MHz")
if INT > 65535:
raise ValueError(f"Requested frequency {freq_MHz} MHz is too high")
FRAC1 = int(frac * MOD1)
remainder = frac * MOD1 - FRAC1
FRAC2 = int(remainder * MOD2)
print(f"FRAC1: {FRAC1}, Remainder: {remainder}, FRAC2: {FRAC2}")
if FRAC1 < 0 or FRAC1 >= MOD1:
raise ValueError(f"FRAC1 ({FRAC1}) must be between 0 and {MOD1 - 1}")
if FRAC2 < 0 or FRAC2 >= MOD2:
raise ValueError(f"FRAC2 ({FRAC2}) must be between 0 and {MOD2 - 1}")
actual_freq = (INT + (FRAC1 + (FRAC2 / MOD2)) / MOD1) * (f_PFD / R)
print(f"Actual Frequency: {actual_freq}")
state.fpfd = f_PFD
state.r_cnt = R
state.r0_int = INT
state.r0_fract = FRAC1
state.r2_mod = MOD2
# Configure the registers in descending order (Register 8 to Register 0)
# Register 8: Control Bits
state.reg_val[REG8] = DITHER1_EN(1) | PHASE_WORD(32137185) | R8_CTRL
# Register 7: Control Bits
state.reg_val[REG7] = RESERVED7(3) | LOCK_DET_CYC(1) | LOL_MODE(0) | LD_MODE(0) | R7_CTRL
# Register 6: Control Bits
state.reg_val[REG6] = RESERVED6_1(1) | RESERVED6_2(39)|REF_IN_MODE(1) | RF_DIV_SEL(R) | RF_OUT_EN(1) | R6_CTRL
# Register 5: Control Bits
state.reg_val[REG5] = PULSE_BLEED_DELAY(0) | CSR_EN(0) | PB(0) | ABP_SELECT(0) | R5_CTRL
# Register 4: Control Bits
state.reg_val[REG4] = R_COUNTER(R) | MUXOUT(6) | REF_DOUBLER(ref_doubler_en) | RDIV2(ref_div2_en) | CHARGE_PUMP(3) |PD_POL(1)| R4_CTRL
# Register 3: Auxiliary Fractional Value FRAC2
state.reg_val[REG3] = RESERVED3(2048) | FRAC2_VAL(FRAC2) | R3_CTRL
# Register 2: Auxiliary Modulus MOD2
state.reg_val[REG2] = MOD2_VAL(MOD2) | R2_CTRL
# Register 1: Fractional Value FRAC1
state.reg_val[REG1] = FRAC1_VAL(FRAC1) | R1_CTRL
# Register 0: Integer and Prescaler
state.reg_val[REG0] = INT_VAL(INT) | PRESCALER(0) | R0_CTRL
# Write register values to the device in descending order (Register 8 to Register 0)
for reg_addr in range(8, -1, -1): # Start from 8 and go down to 0
self.write_register(reg_addr, state.reg_val[reg_addr])
# Return the actual frequency that was set
return actual_freq / 1e6 # Convert back to MHz
def close(self):
"""Close the SPI connection"""
self.spi_controller.terminate()
def main():
"""Example usage of the AFD4155Controller"""
try:
# Create controller instance
synth = AFD4155Controller()
# Set to 2.0 GHz with 10MHz reference
actual_freq = synth.adf4155_set_freq(6000)
print(f"Set frequency to {actual_freq:.3f} MHz")
# Set output power to maximum
# synth.set_power_level(3)
#print("Set power level to maximum")
# Set MUXOUT to Digital Lock Detect (bits 29-27 = 001)
#synth.set_muxout() # No argument needed
#print("Set MUXOUT to Digital Lock Detect (bits 29-27 = 001)")
# Print register values for debugging
print("Register Values:")
print(f"R0: 0x{synth.state.reg_val[REG0]:08X}")
print(f"R1: 0x{synth.state.reg_val[REG1]:08X}")
print(f"R2: 0x{synth.state.reg_val[REG2]:08X}")
print(f"R3: 0x{synth.state.reg_val[REG3]:08X}")
print(f"R4: 0x{synth.state.reg_val[REG4]:08X}")
print(f"R5: 0x{synth.state.reg_val[REG5]:08X}")
print(f"R6: 0x{synth.state.reg_val[REG6]:08X}")
print(f"R7: 0x{synth.state.reg_val[REG7]:08X}")
print(f"R8: 0x{synth.state.reg_val[REG8]:08X}")
# Wait for a few seconds
time.sleep(5)
# Clean up
synth.close()
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
Best regards
Rina
Hi RinaG ,
It may be tricky to debug your source code without an actual hardware, so can you confirm the following.
Can you verify your SPI is working correctly by capturing your SPI timing for a single register read and write sequence, you can share this with us to have a look.
Check that there is a change in the power supply voltage post initialization.
I assume when you write a register, you are able to readback exactly the same value every time. Please confirm these and let us know.
Jude