# # Copyright 2018 Ettus Research, a National Instruments Company # # SPDX-License-Identifier: GPL-3.0-or-later # """ ADF400x driver class Compatible with ADF4001 and ADF4002. """ from builtins import object from usrp_mpm.mpmlog import get_logger BASE_REF_CLOCK_FREQ = 40e6 DEFAULT_REF_CLOCK_FREQ = 20e6 class ADF400x(object): """ Generic driver class for ADF4002 access. Inputs: freq : frequency of reference input parent_log : logger of parent """ def __init__(self, regs_iface, freq=None, parent_log=None): self.log = \ parent_log.getChild("ADF400x") if parent_log is not None \ else get_logger("ADF400x") self.regs_iface = regs_iface assert hasattr(self.regs_iface, 'transfer24_8') self.transfer24_8 = regs_iface.transfer24_8 # Instantiate our own copy of the register mapping and update some values self.adf400x_regs = ADF400xRegs() # N counter = fVCO/PFD self.adf400x_regs.n_counter = 4 self.adf400x_regs.charge_pump_current_1 = 7 self.adf400x_regs.charge_pump_current_2 = 7 # Set MUXOUT to Digital Lock Detect self.adf400x_regs.muxout = ADF400xRegs.MUXOUT_DLD self.adf400x_regs.counter_reset = ADF400xRegs.COUNTER_RESET_NORMAL self.adf400x_regs.phase_detector_polarity = ADF400xRegs.PHASE_DETECTOR_POLARITY_POS # Turn on Charge Pump self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_NORMAL # Set the N counter if freq is None: freq = DEFAULT_REF_CLOCK_FREQ self._set_ref_counter(freq) # Now initialize the ADF400x self.program_regs() def program_regs(self): """ Run through the programming sequence """ # No control over CE, only LE, therefore we use the initialization latch method self._write_reg(3) # Conduct a function latch (2) self._write_reg(2) # Write R counter latch (0) self._write_reg(0) # Write N counter latch (1) self._write_reg(1) def _write_reg(self, addr): """Write the expected value to the given addr""" reg_val = self.adf400x_regs.get_reg(addr) self.log.trace("Writing {:06x} to spidev".format(reg_val)) self.transfer24_8(reg_val) def set_lock_to_ext_ref(self, external): """Set the clock source to external""" if bool(external): self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_NORMAL else: self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_TRISTATE self.program_regs() def _set_ref_counter(self, freq): """ Set R Counter based on reference frequency """ # Calculate R counter fVCO = N * (fREF/R) ref_counter = int(self.adf400x_regs.n_counter * freq / BASE_REF_CLOCK_FREQ) if self.adf400x_regs.ref_counter == ref_counter: self.log.trace("No change to ref counter value ({}); \ returning early".format(ref_counter)) return self.log.trace("Setting ref counter to {}".format(ref_counter)) # Limits from the datasheet assert 1 <= ref_counter <= 16383 self.adf400x_regs.ref_counter = ref_counter def set_ref_freq(self, freq): """Set the input reference frequency""" self._set_ref_counter(freq) self.program_regs() class ADF400xRegs(object): """Register map for ADF400x""" # TODO: Move each field into an Enum or something # TODO: Add setters/getters for each field # anti backlash widths ANTI_BACKLASH_WIDTH_2_9NS = 0 ANTI_BACKLASH_WIDTH_1_3NS = 1 ANTI_BACKLASH_WIDTH_6_0NS = 2 ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3 # lock detect precision LOCK_DETECT_PRECISION_3CYC = 0 LOCK_DETECT_PRECISION_5CYC = 1 # charge pump gain CHARGE_PUMP_GAIN_1 = 0 CHARGE_PUMP_GAIN_2 = 1 # counter reset COUNTER_RESET_NORMAL = 0 COUNTER_RESET_RESET = 1 # power down POWER_DOWN_NORMAL = 0 POWER_DOWN_ASYNC = 1 POWER_DOWN_SYNC = 3 # muxout MUXOUT_TRISTATE_OUT = 0 MUXOUT_DLD = 1 MUXOUT_NDIV = 2 MUXOUT_AVDD = 3 MUXOUT_RDIV = 4 MUXOUT_NCH_OD_ALD = 5 MUXOUT_SDO = 6 MUXOUT_GND = 7 # phase detector polarity PHASE_DETECTOR_POLARITY_NEG = 0 PHASE_DETECTOR_POLARITY_POS = 1 # charge pump mode CHARGE_PUMP_NORMAL = 0 CHARGE_PUMP_TRISTATE = 1 # fastlock mode FASTLOCK_MODE_DISABLED = 0 FASTLOCK_MODE_1 = 1 FASTLOCK_MODE_2 = 2 # timer counter control TIMEOUT_3CYC = 0 TIMEOUT_7CYC = 1 TIMEOUT_11CYC = 2 TIMEOUT_15CYC = 3 TIMEOUT_19CYC = 4 TIMEOUT_23CYC = 5 TIMEOUT_27CYC = 6 TIMEOUT_31CYC = 7 TIMEOUT_35CYC = 8 TIMEOUT_39CYC = 9 TIMEOUT_43CYC = 10 TIMEOUT_47CYC = 11 TIMEOUT_51CYC = 12 TIMEOUT_55CYC = 13 TIMEOUT_59CYC = 14 TIMEOUT_63CYC = 15 def __init__(self): """Set the default configuration""" self.ref_counter = 0 self.n_counter = 0 self.charge_pump_current_1 = 0 self.charge_pump_current_2 = 0 self.anti_backlash_width = ADF400xRegs.ANTI_BACKLASH_WIDTH_2_9NS self.lock_detect_precision = ADF400xRegs.LOCK_DETECT_PRECISION_3CYC self.charge_pump_gain = ADF400xRegs.CHARGE_PUMP_GAIN_1 self.counter_reset = ADF400xRegs.COUNTER_RESET_NORMAL self.power_down = ADF400xRegs.POWER_DOWN_NORMAL self.muxout = ADF400xRegs.MUXOUT_TRISTATE_OUT self.phase_detector_polarity = ADF400xRegs.PHASE_DETECTOR_POLARITY_NEG self.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_TRISTATE self.fastlock_mode = ADF400xRegs.FASTLOCK_MODE_DISABLED self.timer_counter_control = ADF400xRegs.TIMEOUT_3CYC def get_reg(self, addr): """Get the register value to write to the given addr""" reg = 0 if addr == 0: reg |= (self.ref_counter & 0x003FFF) << 2 reg |= (self.anti_backlash_width & 0x000003) << 16 reg |= (self.lock_detect_precision & 0x000001) << 20 elif addr == 1: reg |= (self.n_counter & 0x001FFF) << 8 reg |= (self.charge_pump_gain & 0x000001) << 21 elif addr == 2: reg |= (self.counter_reset & 0x000001) << 2 reg |= (self.power_down & 0x000001) << 3 reg |= (self.muxout & 0x000007) << 4 reg |= (self.phase_detector_polarity & 0x000001) << 7 reg |= (self.charge_pump_mode & 0x000001) << 8 reg |= (self.fastlock_mode & 0x000003) << 9 reg |= (self.timer_counter_control & 0x00000F) << 11 reg |= (self.charge_pump_current_1 & 0x000007) << 15 reg |= (self.charge_pump_current_2 & 0x000007) << 18 reg |= (self.power_down & 0x000002) << 20 elif addr == 3: reg |= (self.counter_reset & 0x000001) << 2 reg |= (self.power_down & 0x000001) << 3 reg |= (self.muxout & 0x000007) << 4 reg |= (self.phase_detector_polarity & 0x000001) << 7 reg |= (self.charge_pump_mode & 0x000001) << 8 reg |= (self.fastlock_mode & 0x000003) << 9 reg |= (self.timer_counter_control & 0x00000F) << 11 reg |= (self.charge_pump_current_1 & 0x000007) << 15 reg |= (self.charge_pump_current_2 & 0x000007) << 18 reg |= (self.power_down & 0x000002) << 20 reg |= addr return reg