Source code for ptb.adf4350

from math import ceil
from fractions import Fraction
import logging


logger = logging.getLogger(__name__)


[docs]class ADF4350: """From the dtasheet with analogies of naming from https://github.com/analogdevicesinc/no-OS/blob/master/drivers/adf4350/adf4350.h """ @staticmethod def reg0_fract(x): return (x & 0xFFF) << 3 @staticmethod def reg0_int(x): return (x & 0xFFFF) << 15 @staticmethod def reg1_mod(x): return (x & 0xFFF) << 3 @staticmethod def reg1_phase(x): return (x & 0xFFF) << 15 reg1_prescaler = 1 << 27 reg2_counter_reset_en = 1 << 3 reg2_cp_threestate_en = 1 << 4 reg2_power_down_en = 1 << 5 reg2_pd_polarity_pos = 1 << 6 reg2_ldp_6ns = 1 << 7 reg2_ldp_10ns = 0 << 7 reg2_ldf_fract_n = 0 << 8 reg2_ldf_int_n = 1 << 8 @staticmethod def reg2_charge_pump_curr_ua(x): return (((x - 312) // 312) & 0xF) << 9 reg2_double_buff_en = 1 << 13 @staticmethod def reg2_10bit_r_cnt(x): return x << 14 reg2_rdiv2_en = 1 << 24 reg2_rmult2_en = 1 << 25 @staticmethod def reg2_muxout(x): return x << 26 @staticmethod def reg2_noise_mode(x): return x << 29 @staticmethod def reg3_12bit_clkdiv(x): return x << 3 @staticmethod def reg3_12bit_clkdiv_mode(x): return x << 16 reg3_12bit_csr_en = 1 << 18 reg3_charge_cancellation_en = 1 << 21 reg3_anti_backlash_3ns_en = 1 << 22 reg3_band_sel_clock_mode_high = 1 << 23 @staticmethod def reg4_output_pwr(x): return x << 3 reg4_rf_out_en = 1 << 5 @staticmethod def reg4_aux_output_pwr(x): return x << 6 reg4_aux_output_en = 1 << 8 reg4_aux_output_fund = 1 << 9 reg4_aux_output_div = 0 << 9 reg4_mute_till_lock_en = 1 << 10 reg4_vco_pwrdown_en = 1 << 11 @staticmethod def reg4_8bit_band_sel_clkdiv(x): return x << 12 @staticmethod def reg4_rf_div_sel(x): return x << 20 reg4_feedback_divided = 0 << 23 reg4_feedback_fund = 1 << 23 reg5_ld_pin_mode_low = 0 << 22 reg5_ld_pin_mode_digital = 1 << 22 reg5_ld_pin_mode_high = 3 << 22 max_out_freq = 4.4e9 # Hz min_out_freq = 34.375e6 # Hz min_vco_freq = 2.2e9 # Hz max_freq_45_presc = 3e9 # Hz max_freq_pfd = 32e6 # Hz max_bandsel_clk = 125e3 # Hz max_freq_refin = 250e6 # Hz max_modulus = 4095 max_r_cnt = 1023 ref_frequency = None ref_div_factor = None ref_doubler_en = False ref_div2_en = False channel_spacing = 0. phase_detector_polarity_positive_en = True lock_detect_precision_6ns_en = False lock_detect_function_integer_n_en = False charge_pump_curr = 2500 # uA muxout_select = 0 low_spur_mode_en = False powerdown_en = None cycle_slip_reduction_en = True charge_cancellation_en = False # ADF4351 anti_backlash_3ns_en = False # ADF4351 band_select_clock_mode_high_en = False # ADF4351 clk_divider_12bit = 150 clk_divider_mode = 0 aux_output_en = False aux_output_fundamental_en = False mute_till_lock_en = False output_power = 3 aux_output_power = 0 def _f_pfd(self, r_cnt): return self.ref_frequency * (1 + self.ref_doubler_en) / ( r_cnt * (1 + self.ref_div2_en))
[docs] def set_frequency(self, f_out): """Set output frequency. Args: f_out (float): Desired frequency Returns: Actual output frequency set """ if not (self.min_out_freq <= f_out <= self.max_out_freq): raise ValueError("invalid frequency") # determine output divider and VCO frequency rf_div_sel = 0 f_vco = f_out while f_vco < self.min_vco_freq: rf_div_sel += 1 f_vco *= 2 assert 0 <= rf_div_sel <= 6 logger.info("VCO frequency %g GHz", f_vco/1e9) logger.info("output divider %i", 1 << rf_div_sel) # select prescaler prescaler_en = f_vco > self.max_freq_45_presc logger.info("prescaler_en %i", prescaler_en) # select reference divider and PFD frequency r_cnt = self.ref_div_factor if not r_cnt: r_cnt = ceil(self._f_pfd(1)/self.max_freq_pfd) assert 1 <= r_cnt <= self.max_r_cnt logger.info("R counter value %i", r_cnt) f_pfd = self._f_pfd(r_cnt) assert f_pfd <= self.max_freq_pfd logger.info("PFD frequency %g MHz", f_pfd/1e6) n_int, df = divmod(f_vco, f_pfd) n_int = int(n_int) n_int_min = 75 if prescaler_en else 23 assert n_int_min <= n_int <= 1 << 16 logger.info("N divider integral part %i", n_int) logger.info("N divider fractional part %g MHz", df/1e6) if df: if self.channel_spacing: n_mod = int(round(f_pfd/self.channel_spacing)) while n_mod > self.max_modulus: n_mod //= 2 n_fract = int(round(df/f_pfd*n_mod)) else: n_rat = Fraction(df/f_pfd) logger.info("N divider fraction %s", n_rat) n_rat = n_rat.limit_denominator(self.max_modulus) n_fract, n_mod = n_rat.numerator, n_rat.denominator logger.info("N divider fract/mod %i/%i", n_fract, n_mod) logger.info("channel spacing %g kHz", f_pfd/n_mod/1e3) logger.info("frequency error %g Hz", f_pfd*n_fract/n_mod - df) else: n_fract = 0 n_mod = 1 assert 1 <= n_mod <= self.max_modulus assert 0 <= n_fract < n_mod if n_mod > 1: assert not self.lock_detect_function_integer_n_en # determine clock divider for band selection logic band_sel_div = int(f_pfd / self.max_bandsel_clk) assert 1 <= band_sel_div <= 255 logger.info("VCO band selection logic clock divider %i", band_sel_div) self._regs = list(range(6)) # control bits self._regs[0] |= self.reg0_int(n_int) | self.reg0_fract(n_fract) self._regs[1] |= (self.reg1_phase(1) | self.reg1_mod(n_mod) | (prescaler_en * self.reg1_prescaler)) self._regs[2] |= ( self.reg2_10bit_r_cnt(r_cnt) | (0 * self.reg2_double_buff_en) | (self.ref_doubler_en * self.reg2_rmult2_en) | (self.ref_div2_en * self.reg2_rdiv2_en) | (self.phase_detector_polarity_positive_en * self.reg2_pd_polarity_pos) | (self.lock_detect_precision_6ns_en * self.reg2_ldp_6ns) | (self.lock_detect_function_integer_n_en * self.reg2_ldf_int_n) | self.reg2_charge_pump_curr_ua(self.charge_pump_curr) | self.reg2_muxout(self.muxout_select) | self.reg2_noise_mode(self.low_spur_mode_en*0x3)) self._regs[3] |= ( (self.cycle_slip_reduction_en * self.reg3_12bit_csr_en) | (self.charge_cancellation_en * self.reg3_charge_cancellation_en) | (self.anti_backlash_3ns_en * self.reg3_anti_backlash_3ns_en) | (self.band_select_clock_mode_high_en * self.reg3_band_sel_clock_mode_high) | self.reg3_12bit_clkdiv(self.clk_divider_12bit) | self.reg3_12bit_clkdiv_mode(self.clk_divider_mode)) self._regs[4] |= (self.reg4_feedback_fund | self.reg4_rf_div_sel(rf_div_sel) | self.reg4_8bit_band_sel_clkdiv(band_sel_div) | self.reg4_rf_out_en | self.reg4_output_pwr(self.output_power) | self.reg4_aux_output_pwr(self.aux_output_power) | (self.aux_output_en * self.reg4_aux_output_en) | (self.aux_output_fundamental_en * self.reg4_aux_output_fund) | (self.mute_till_lock_en * self.reg4_mute_till_lock_en)) self._regs[5] |= self.reg5_ld_pin_mode_digital | 0x00180000 return f_pfd*(n_int + n_fract/n_mod)/(1 << rf_div_sel)
if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) a = ADF4350() a.ref_frequency = 10e6 a.ref_div_factor = 1 a.ref_div2_en = False a.ref_doubler_en = True f = 2e9 f += 432.1234567e3 print(a.set_frequency(f)) print(["{:#010x}".format(r) for r in reversed(a._regs)]) print(["{:#010x}".format(r) for r in reversed([ 0x00640000, 0x08008009, 0x02004E42, 0x000404B3, 0x009C803C, 0x00580005])])