# `hmc833` - Encapsulates control for the HMC833 frequency synthesizer device.
#
# Copyright (C) 2020 Dyadic Pty Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import (
List, Tuple, Optional
)
import math
from enum import IntFlag
[docs]class FrequencyRangeException(Exception):
"""Raised when a requested output frequency is out of range.
"""
pass
class UnknownGainSettingException(Exception):
"""Raised when an invalid gain setting is specified.
"""
pass
[docs]class hmc833(object):
"""Encapsulates control for the HMC833 frequency synthesizer device.
Documentation for the HMC833 frequency synthesizer rfblocks module
can be found here: `An HMC833 Frequency Synthesizer <../boards/HMC833-Synth.html>`_
"""
MIN_FUNDAMENTAL: float = 1500.0
"The minimum VCO fundamental frequency"
MAX_FUNDAMENTAL: float = 3000.0
"The maximum VCO fundamental frequency"
MIN_FREQUENCY: float = 25.0
"The minimum synthesizer output frequency"
MAX_FREQUENCY: float = 6000.0
"The maximum synthesizer output frequency"
MIN_CP_GAIN: int = 0
MAX_CP_GAIN: int = 127
[docs] class ReferenceSource(IntFlag):
"""An `enum` containing the possible settings for the reference
source. (``hmc833.ReferenceSource.INTERNAL``,
``hmc833.ReferenceSource.EXTERNAL``)
"""
INTERNAL = 0b00
EXTERNAL = 0b01
[docs] class OutputBufferGain(IntFlag):
"""An `enum` containing possible gain settings for the device output buffer.
(``ad9552.OutputBufferGain.MAXGAIN_MINUS_9DB``,
``ad9552.OutputBufferGain.MAXGAIN_MINUS_6DB``,
``ad9552.OutputBufferGain.MAXGAIN_MINUS_3DB``,
``ad9552.OutputBufferGain.MAXGAIN``)
"""
MAXGAIN_MINUS_9DB = 0b00
MAXGAIN_MINUS_6DB = 0b01
MAXGAIN_MINUS_3DB = 0b10
MAXGAIN = 0b11
@classmethod
def has_value(cls, value):
return value in cls._value2member_map_
[docs] class DividerGain(IntFlag):
"""An `enum` containing possible gain settings for the divider output stage.
(``ad9552.DividerGain.MAXGAIN_MINUS_3DB``,
``ad9552.DividerGain.MAXGAIN``)
"""
MAXGAIN_MINUS_3DB = 0b0
MAXGAIN = 0b1
@classmethod
def has_value(cls, value):
return value in cls._value2member_map_
DEFAULT_REFSRC: ReferenceSource = ReferenceSource.INTERNAL
DEFAULT_REF_FREQ: float = 50.0
DEFAULT_CP_GAIN: int = 100
CP_GAIN_STEP: float = 20e-6
MAX_DIVIDER: int = 62
"Maximum RF output divider value"
DIVIDER_VALUES: List[int] = list(range(2, MAX_DIVIDER+1, 2))
"Legal RF output divider values"
def __init__(
self,
sen: str = None,
ld_sdo: str = None,
ref: str = None,
fref: float = DEFAULT_REF_FREQ,
refdiv: int = 1,
refsrc: ReferenceSource = ReferenceSource.INTERNAL,
cp_gain: int = DEFAULT_CP_GAIN) -> None:
"""
:param sen: The HMC833 serial port enable (`SEN`) controller pin.
:type sen: str
:param ld_sdo: The HMC833 lock detect and GPO serial output
controller pin.
:type ld_sdo: str
:param ref: The PLO board reference select controller pin.
:type ref: str
:param fref: The input reference frequency in MHz.
Default: 50 MHz.
:type fref: float
:param refdiv: The input reference divider value (1..16,383),
default 1.
:type refdiv: int
:param refsrc: The PLO reference source.
:param type: hmc833.ReferenceSource
"""
self.sen = sen.upper() if sen else sen
self.ld_sdo = ld_sdo.upper() if ld_sdo else ld_sdo
self.ref = ref.upper() if ref else ref
self._fref = fref
self._refdiv = refdiv
self._refsrc = refsrc
self._cp_gain = cp_gain
self._buf_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB
self._div_gain = hmc833.DividerGain.MAXGAIN_MINUS_3DB
self._mute_vco = False
# We keep track of the currently configured values of
# the integer and fractional dividers so that we can determine
# when it's necessary to change the SD fractional modulator
# state (via self.init_registers).
self.__nfrac: Optional[int] = None
self.__nint: Optional[int] = None
# We also keep track of the output RF divider ratio.
# This is configured in VCO_Reg_02h which is also used
# to configure the output buffer and divider buffer gains.
# Therefore, in order to set gain values we also need to
# ensure that the divider value remains correct.
self.__divide_ratio: Optional[int] = None
self.__doubler: Optional[bool] = None
def __repr__(self) -> str:
return "{}({!r})".format(self.__class__.__name__, vars(self))
@property
def mute_vco(self) -> bool:
"""The synthesizer output mute setting. Set to ``False`` to
enable RF output, ``True`` to mute the output.
Note that this will set the value of the :py:attr:`mute_vco`
property only. Updating the PLO hardware should be
done separately. See, for example,
:py:meth:`config_vco_mute`.
"""
return self._mute_vco
@mute_vco.setter
def mute_vco(self, mute: bool) -> None:
self._mute_vco = mute
@property
def fref(self) -> float:
"""The current input reference frequency in MHz.
Note that this will set the value of the :py:attr:`fref`
property only. Updating the PLO hardware should be
done separately. See, for example,
:py:meth:`config_reference_divider`.
Note also that if :py:attr:`fref` or :py:attr:`refdiv`
are changed then the PLO output frequency tuning must
also be updated. This can be done by calling:
.. code:: python
plo.config_frequency(fout, full_reg_update=True)
.. seealso:: :meth:`.HMC833Controller.configure_refdiv` and
:meth:`.HMC833Controller.configure_refsrc`.
"""
return self._fref
@fref.setter
def fref(self, fref: float) -> None:
self._fref = fref
@property
def refdiv(self) -> int:
"""The current reference frequency divider value.
Note that this will set the value of the :py:attr:`refdiv`
property only. Updating the PLO hardware should be
done separately. See, for example,
:py:meth:`config_reference_divider`.
Note also that if :py:attr:`fref` or :py:attr:`refdiv`
are changed then the PLO output frequency tuning must
also be updated. This can be done by calling:
.. code:: python
plo.config_frequency(fout, full_reg_update=True)
.. seealso:: :meth:`.HMC833Controller.configure_refdiv` and
:meth:`.HMC833Controller.configure_refsrc`.
"""
return self._refdiv
@refdiv.setter
def refdiv(self, refdiv: int) -> None:
self._refdiv = refdiv
@property
def refsrc(self) -> ReferenceSource:
"""The current reference source.
"""
return self._refsrc
@refsrc.setter
def refsrc(self, src: ReferenceSource) -> None:
self._refsrc = src
@property
def cp_gain(self) -> int:
"""The charge pump gain setting.
"""
return self._cp_gain
@cp_gain.setter
def cp_gain(self, gain: int) -> None:
if gain < hmc833.MIN_CP_GAIN:
self._cp_gain = hmc833.MIN_CP_GAIN
if gain > hmc833.MAX_CP_GAIN:
self._cp_gain = hmc833.MAX_CP_GAIN
else:
self._cp_gain = gain
@property
def buf_gain(self) -> OutputBufferGain:
"""The output buffer gain setting.
"""
return self._buf_gain
@buf_gain.setter
def buf_gain(self, gain: OutputBufferGain) -> None:
if hmc833.OutputBufferGain.has_value(gain):
self._buf_gain = gain
else:
raise UnknownGainSettingException
@property
def div_gain(self) -> DividerGain:
"""The divider output stage gain setting.
"""
return self._div_gain
@div_gain.setter
def div_gain(self, gain: DividerGain) -> None:
if hmc833.DividerGain.has_value(gain):
self._div_gain = gain
else:
raise UnknownGainSettingException
[docs] def auto_gain(self, fout: float) -> None:
"""Set the ``buf_gain`` and ``div_gain`` based on the specified
output frequency.
This method will adjust the output buffer and divider output
buffer gains for optimal power flatness across all output
frequencies (from 25 to 6000 MHz).
:param fout: The device output frequency in MHz.
:type fout: float
.. note::
Invoking this method will set appropriate values for ``buf_gain``
and ``div_gain``. The ``config_gain`` method should then be called
in order to generate a command which can be used to configure
the gain settings on the actual device.
.. seealso::
:py:meth:`buf_gain`
:py:meth:`div_gain`
:py:meth:`config_gain`
>>> plo1 = hmc833('d1', 'c5')
>>> plo1.auto_gain(560)
>>> plo1.buf_gain
<OutputBufferGain.MAXGAIN_MINUS_9DB: 0>
>>> plo1.div_gain
<DividerGain.MAXGAIN: 1>
>>> plo1.auto_gain(1560)
>>> plo1.buf_gain
<OutputBufferGain.MAXGAIN_MINUS_9DB: 0>
>>> plo1.div_gain
<DividerGain.MAXGAIN_MINUS_3DB: 0>
>>> plo1.auto_gain(2560)
>>> plo1.buf_gain
<OutputBufferGain.MAXGAIN_MINUS_3DB: 2>
>>> plo1.div_gain
<DividerGain.MAXGAIN_MINUS_3DB: 0>
>>> plo1.auto_gain(3560)
>>> plo1.buf_gain
<OutputBufferGain.MAXGAIN_MINUS_3DB: 2>
>>> plo1.div_gain
<DividerGain.MAXGAIN_MINUS_3DB: 0>
>>> plo1.auto_gain(4560)
>>> plo1.buf_gain
<OutputBufferGain.MAXGAIN: 3>
>>> plo1.div_gain
<DividerGain.MAXGAIN_MINUS_3DB: 0>
"""
if fout < 1500.0:
self.div_gain = hmc833.DividerGain.MAXGAIN
self.buf_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB
elif fout < 2500.0:
self.div_gain = hmc833.DividerGain.MAXGAIN_MINUS_3DB
self.buf_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB
elif fout <= 4300.0:
self.div_gain = hmc833.DividerGain.MAXGAIN_MINUS_3DB
self.buf_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_3DB
else:
self.div_gain = hmc833.DividerGain.MAXGAIN_MINUS_3DB
self.buf_gain = hmc833.OutputBufferGain.MAXGAIN
[docs] def pin_config(self) -> str:
"""Initialize controller pin configuration.
:return: A string specifying the commands required to initialize the
connected controller pins.
>>> plo1 = hmc833('d1', 'c5', fref=50.0)
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.pin_config()
'OD1:LD1:IC5:W00:'
>>> plo2.pin_config()
'OD2:LD2:IC6:OB5:LB5:W00:'
"""
cmd = ''
if self.sen:
cmd += 'O{}:L{}:'.format(self.sen, self.sen)
if self.ld_sdo:
cmd += 'I{}:'.format(self.ld_sdo)
if self.ref:
cmd += 'O{}:'.format(self.ref)
cmd += self.config_refsrc()
# Toggle the SCK line. This will put the connected HMC833 into
# 'open' mode. (See Section 1.17.2 of the HMC833 data sheet.)
cmd += 'W00:'
return cmd
[docs] def chip_reset(self) -> str:
"""Reset the chip internal logic to default states.
:return: The command for reseting the PLL subsystem SPI registers.
From Section 1.13 of the HMC833 data sheet: The HMC833LP6GE
features a hardware Power on Reset (``POR``). All chip
registers will be reset to default states approximately 250
us after power up.
The PLL subsystem SPI registers may also be soft reset by an
SPI write to register ``rst_swrst`` (``Reg_00h``). Note that the
soft reset does not clear the SPI mode of operation referred
to in section 1.17.2. The soft reset is applied by writing
``Reg_00h[5]=1``. The reset is a one time event that occurs
immediately. The reset bit does not have to be returned to
0 after a reset. It should be noted that the VCO subsystem
is not affected by the PLL soft reset. The VCO subsystem
registers can only be reset by removing the power supply.
>>> plo1 = hmc833('d1', 'c5')
>>> plo1.chip_reset()
'W00,00,20,00:HD1:LD1:'
"""
return 'W00,00,20,00:H{}:L{}:'.format(self.sen, self.sen)
[docs] def divider_values(self,
fout: float) -> Tuple[int, int, bool, int]:
"""Calculate the N divider values.
:param fout: The desired output frequency in MHz.
:type fout: float
:returns: A tuple containing the parameters for the fractional
frequency tuning: ``(n_int, n_frac, doubler, divider)``
where:
``n_int``: is the integer division ratio, a number between
20 and 524,284.
``n_frac`` : is the fractional part, from 0 to 2^24.
``doubler`` : is True if the frequency doubler must be
enabled, False if the frequency doubler should be disabled.
``divider`` : the required setting for the output frequency
divider. If this is 1 then the divider is not used.
:raises: FrequencyRangeException
If the specified output frequency is out of range.
>>> plo1 = hmc833('d1', 'c5')
>>> plo1.divider_values(762)
(30, 8053064, False, 2)
>>> plo1.divider_values(1762)
(35, 4026532, False, 1)
>>> plo1.divider_values(3762)
(37, 10401874, True, 1)
>>> plo1.divider_values(5762)
(57, 10401874, True, 1)
>>> plo1.divider_values(6762)
Traceback (most recent call last):
...
FrequencyRangeException
>>> plo1.divider_values(20)
Traceback (most recent call last):
...
FrequencyRangeException
"""
doubler = False
divider = 1
fund = fout
if fout > hmc833.MAX_FUNDAMENTAL:
doubler = True
fund = fout/2
if fund > hmc833.MAX_FUNDAMENTAL:
# The specified fout is out of range
raise FrequencyRangeException
elif fout < hmc833.MIN_FUNDAMENTAL:
# If fout < 1500 MHz we need an output dividing factor.
# Use the greatest dividing factor such that
# fout*divider is between hmc833.MIN_FUNDAMENTAL and
# hmc833.MAX_FUNDAMENTAL
try:
divider = [d for d in hmc833.DIVIDER_VALUES
if fout*d >= hmc833.MIN_FUNDAMENTAL
and fout*d <= hmc833.MAX_FUNDAMENTAL][-1]
except IndexError:
# The specified fout is out of range
raise FrequencyRangeException from None
fund = fout * divider
nf_f, nf_i = math.modf((self.refdiv/self.fref) * fund)
n_int = int(nf_i)
n_frac = round(nf_f * (1 << 24))
return n_int, n_frac, doubler, divider
[docs] def config_refsrc(self) -> str:
"""Configure the reference source.
:returns: The command string required to set the reference
source switch.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.config_refsrc()
''
>>> plo2.config_refsrc()
'LB5:'
"""
cmd = ""
if self.ref is not None:
if self.refsrc == hmc833.ReferenceSource.INTERNAL:
cmd = "H{}:".format(self.ref)
else:
cmd = "L{}:".format(self.ref)
return cmd
[docs] def config_reference_divider(self) -> str:
"""Configure the reference divider value.
:returns: The command string required to set the reference
divider value.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.config_reference_divider()
'W00,00,01,10:HD1:LD1:'
>>> plo2.config_reference_divider()
'W00,00,04,10:HD2:LD2:'
"""
ref_str = '{:04x}'.format(self.refdiv & 0x3fff)
return 'W00,{},{},10:H{}:L{}:'.format(ref_str[0:2], ref_str[2:4],
self.sen, self.sen)
[docs] def config_spi_chipen(self) -> str:
"""Configure device to accept the SPI power down command.
:returns: The command required to reset the device via the SPI.
.. note::
From the HMC833 data sheet: It is possible to ignore the CEN
pin, by clearing ``rst_chipen_pin_select`` (``Reg_01h[0]=0``).
Control of Power Down Mode then comes from the serial port
register ``rst_chipen_from_spi``, ``Reg_01h[1]``.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.config_spi_chipen()
'W00,00,02,08:HD1:LD1:'
>>> plo2.config_spi_chipen()
'W00,00,02,08:HD2:LD2:'
"""
return 'W00,00,02,08:H{}:L{}:'.format(self.sen, self.sen)
[docs] def config_frequency_registers(self,
nint: int,
nfrac: int) -> str:
"""Configure the device registers for the specified fractional divide ratio
:param nint: The integer part of the frequency tuning.
:type nint: 19-bit int (20 to 524,284)
:param nfrac: The fractional part of the frequency tuning.
:type nfrac: 24-bit int (0 to 2^24)
:return: The command string required to set the necessary device
register values.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(1000.0)
>>> plo1.config_frequency_registers(n_int, n_frac)
'W00,00,28,18:HD1:LD1:W00,00,00,20:HD1:LD1:'
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(1004.0)
>>> plo1.config_frequency_registers(n_int, n_frac)
'W00,00,28,18:HD1:LD1:W28,f5,c3,20:HD1:LD1:'
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(4207.0)
>>> plo1.config_frequency_registers(n_int, n_frac)
'W00,00,2a,18:HD1:LD1:W11,eb,85,20:HD1:LD1:'
>>> n_int, n_frac, doubler, divide_ratio = plo2.divider_values(4207.0)
>>> plo2.config_frequency_registers(n_int, n_frac)
'W00,00,21,18:HD2:LD2:Wa7,ef,9e,20:HD2:LD2:'
"""
cmd = ''
reg_03h = '{:08x}'.format(((nint & 0x7ffff) << 8) | 0x18)
cmd += 'W{},{},{},{}:H{}:L{}:'.format(reg_03h[0:2], reg_03h[2:4],
reg_03h[4:6], reg_03h[6:8],
self.sen, self.sen)
self.__nint = nint
if (self.__nfrac is None) or (self.__nfrac != nfrac):
reg_04h = '{:08x}'.format(((nfrac & 0xffffff) << 8) | 0x20)
cmd += 'W{},{},{},{}:H{}:L{}:'.format(reg_04h[0:2], reg_04h[2:4],
reg_04h[4:6], reg_04h[6:8],
self.sen, self.sen)
self.__nfrac = nfrac
return cmd
[docs] def init_vco_registers(self) -> str:
"""Initialize VCO registers 4 and 5.
This initialization can be done once at device power up or
reset.
:return: The command required to initialize ``VCO_Reg_04h``
and ``VCO_Reg_05h``.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.init_vco_registers()
'W00,01,88,28:HD1:LD1:W00,16,28,28:HD1:LD1:W00,60,A0,28:HD1:LD1:'
>>> plo2.init_vco_registers()
'W00,01,88,28:HD2:LD2:W00,16,28,28:HD2:LD2:W00,60,A0,28:HD2:LD2:'
"""
# VCO_Reg_01h
#
# 8 7 6 5 4 3 2 1 0
# | |
# | Master Enable VCO Subsystem
# Manual Mode PLL buffer enable
vco_reg_01h = 0b0000_0011
reg_05h = '{:06X}'.format((((vco_reg_01h << 7) | 0b0001_000) << 8)
| 0b0101_000)
cmd = 'W00,{},{},{}:H{}:L{}:'.format(reg_05h[0:2], reg_05h[2:4],
reg_05h[4:6], self.sen, self.sen)
# - Configure the VCO subsystem via Reg_05h
# * As per the HMC833 data sheet (Section 3.6), VCO_Reg_05h
# is configured with:
#
# 0001_0110_0010_1000 = 0x1628
# \/\_/\/\_/\___/\_/
# | | | | | VCO subsystem addr
# | | | | VCO_Reg_05h
# | | | CF L = 0b00
# | | CF ML = 0b11
# | CF MH = 0b10
# CF H = 0b00
#
# * As per the HMC833 data sheet (Section 3.5), VCO_Reg_04h
# is configured with:
#
# 0110_0000_1010_0000 = 0x60A0
# \/\/ \/\__/\___/\_/
# | | | | | VCO subsystem addr
# | | | | VCO_Reg_04h
# | | | VCO bias = 0b001
# | | PLL buffer bias = 0b00
# | FndLmtr bias = 0b10
# Preset cal 0 = 0b01
#
cmd += 'W00,16,28,28:H{}:L{}:'.format(self.sen, self.sen)
cmd += 'W00,60,A0,28:H{}:L{}:'.format(self.sen, self.sen)
return cmd
[docs] def config_vco_divider(self, out_div: int) -> str:
"""Configure the VCO output divider.
:param out_div: The required VCO divider value.
:type out_div: int
:return: The command string required to configure the specified
VCO divide value.
.. note::
This method may also be used to configure the VCO divider and
output buffer gains. The gain values must have been previously
set using :py:meth:`buf_gain` and/or :py:meth:`div_gain`.
The ``out_div`` parameter should be set to the currently configured
VCO divide value (available internally as ``self.__divide_ratio``).
"""
# VCO_Reg_02h
#
# 8 7 6 5 4 3 2 1 0
# | \_/ \_________/
# | | RF divide ratio
# | RF output buf. gain control
# divider gain control
vco_reg_02h = ((self.div_gain & 0x1) << 8) \
| ((self.buf_gain & 0x3) << 6) \
| out_div & 0x3f
reg_05h = '{:06X}'.format((((vco_reg_02h << 7) | 0b0010_000) << 8)
| 0b0101_000)
cmd = 'W00,{},{},{}:H{}:L{}:'.format(reg_05h[0:2], reg_05h[2:4],
reg_05h[4:6], self.sen, self.sen)
return cmd
[docs] def config_vco_doubler(self, doubler: bool) -> str:
"""Configure the VCO output frequency doubler.
:param doubler: True to activate the frequency doubler, False to
deactivate it.
:type doubler: bool
:return: The command string required to configure the specified
VCO frequency doubler state.
.. note::
This method may also be used to mute the VCO output.
The VCO mute state must have been previously
set using :py:meth:`mute_vco`.
The ``doubler`` parameter should be set to the currently configured
VCO frequency doubler state (available internally as
``self.__doubler``).
"""
# VCO_Reg_03h
#
# 0000_0000_0
# \/| |
# || fund./doubler: 0 = enable doubler, 1 = fundamental
# |manual RFO mode: 0 = autoRFO (recommended)
# RF buf. bias: 0b10 for fout <= 3000MHz (VCO_Reg_03h[0] = 1)
# 0b00 for fout > 3000MHz (VCO_Reg_03h[0] = 0)
if doubler:
if self.mute_vco:
vco_reg_03h = 0b0000_0010_0
else:
vco_reg_03h = 0b0000_0000_0
else:
if self.mute_vco:
vco_reg_03h = 0b0000_1010_1
else:
vco_reg_03h = 0b0000_1000_1
reg_05h = '{:06X}'.format((((vco_reg_03h << 7) | 0b0011_000) << 8)
| 0b0101_000)
cmd = 'W00,{},{},{}:H{}:L{}:'.format(reg_05h[0:2], reg_05h[2:4],
reg_05h[4:6], self.sen, self.sen)
return cmd
[docs] def config_vco_registers(self,
out_div: int,
doubler: bool) -> str:
"""Configure VCO registers for the specified divide ratio/doubler.
:param out_div: The specified output divide ratio. This should
be in the range 1..62 although only the values
in `[1] + hmc833.DIVIDER_VALUES` are legal.
:type out_div: int
:param doubler: True if the output frequency doubler should be
enabled, False if the doubler should be disabled.
:type doubler: bool
:return: The commands required to set the necessary VCO
register values.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(1000.0)
>>> plo1.config_vco_registers(divide_ratio, doubler)
'W00,01,10,28:HD1:LD1:W00,08,98,28:HD1:LD1:W00,00,00,28:HD1:LD1:'
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(1004.0)
>>> plo1.config_vco_registers(divide_ratio, doubler)
''
>>> n_int, n_frac, doubler, divide_ratio = plo1.divider_values(4207.0)
>>> plo1.config_vco_registers(divide_ratio, doubler)
'W00,00,90,28:HD1:LD1:W00,00,18,28:HD1:LD1:W00,00,00,28:HD1:LD1:'
>>> n_int, n_frac, doubler, divide_ratio = plo2.divider_values(4207.0)
>>> plo2.config_vco_registers(divide_ratio, doubler)
'W00,00,90,28:HD2:LD2:W00,00,18,28:HD2:LD2:W00,00,00,28:HD2:LD2:'
"""
cmd = ''
if (self.__divide_ratio is None) or (self.__divide_ratio != out_div):
cmd += self.config_vco_divider(out_div)
self.__divide_ratio = out_div
if (self.__doubler is None) or (self.__doubler != doubler):
cmd += self.config_vco_doubler(doubler)
self.__doubler = doubler
if len(cmd):
# Close out VCO register programming by writing reg_05h = 0
cmd += self.close_vco_config()
return cmd
[docs] def config_chargepump(self, sd_mod_en: bool) -> str:
"""Configure the PLO sigma/delta modulator.
:param en: True for enabling the SD modulator. False to disable.
:type en: bool
:return: The command string required to effect the register
initialization.
"""
f_pd = self._fref/self._refdiv * 1e6
i_cp = self.cp_gain * hmc833.CP_GAIN_STEP
cp_offset = round(min(4.3e-9 * f_pd * i_cp, 0.25 * i_cp) * 1e6)
if sd_mod_en:
cp_conf = 0x400000
else:
cp_conf = 0x000000
cp_conf |= ((int(cp_offset/5) << 14) | (self.cp_gain << 7)
| self.cp_gain)
reg_09h = '{:08X}'.format((cp_conf << 8) | 0x48)
cmd = 'W{},{},{},{}:H{}:L{}:'.format(reg_09h[0:2], reg_09h[2:4],
reg_09h[4:6], reg_09h[6:8],
self.sen, self.sen)
return cmd
[docs] def enable_sd_modulator(self, en: bool) -> str:
"""Configure the PLO sigma/delta modulator.
:param en: True for enabling the SD modulator. False to disable.
:type en: bool
:return: The command string required to effect the register
initialization.
"""
if en:
# - Enable the SD modulator, Reg_06h[11] = 1
# - Connect modulator in circuit, Reg_06h[7] = 0
#
# 0000_0011_0000_1111_0100_1010_0011_0000
# | || | \/\/ \____/
# | || | | | Reg_06h
# | || | | fractional seed (B29D08h seed)
# | || | modulator type (mode B)
# | || 0 = use modulator (fractional mode)
# | |1 = auto seed
# | 1 = modulator uses VCO divider clock
# 1 = enable SD modulator
cmd = 'W03,0F,4A,30:H{}:L{}:'.format(self.sen, self.sen)
# reg_09h = '{:08X}'.format((0x1CBFFF << 8) | 0x48)
# Icp = 2mA:
# reg_09h = '{:08X}'.format((0x15B264 << 8) | 0x48)
# Icp = 1mA
# reg_09h = '{:08X}'.format((0x0AD932 << 8) | 0x48)
else:
# - Disable the SD modulator, Reg_06h[11] = 0
# - Bypass the modulator, Reg_06h[7] = 1
#
# 0000_0011_0000_0111_1100_1010_0011_0000
# | || | \/\/ \____/
# | || | | | Reg_06h
# | || | | fractional seed (B29D08h seed)
# | || | modulator type (mode B)
# | || 1 = bypass modulator (integer mode)
# | |1 = auto seed
# | 1 = modulator uses VCO divider clock
# 0 = disable SD modulator
#
# Note: SD modulator mode B is recommended. The max. PD freq.
# in this mode is 100 MHz. (max. PD freq. for mode A
# is 80 MHz.)
cmd = 'W03,07,CA,30:H{}:L{}:'.format(self.sen, self.sen)
# reg_09h = '{:08X}'.format((0x5CBFFF << 8) | 0x48)
# Icp = 2mA:
# reg_09h = '{:08X}'.format((0x55B264 << 8) | 0x48)
# Icp = 1mA
# reg_09h = '{:08X}'.format((0x4AD932 << 8) | 0x48)
#cmd += 'W{},{},{},{}:H{}:L{}:'.format(reg_09h[0:2], reg_09h[2:4],
# reg_09h[4:6], reg_09h[6:8],
# self.sen, self.sen)
cmd += self.config_chargepump(en)
return cmd
[docs] def init_registers(self,
nint: int,
nfrac: int,
out_div: int,
doubler: bool = False) -> str:
"""Initialize the frequency, SD modulator and VCO subsystem registers.
:param nint: The integer part of the frequency tuning.
:type nint: 19-bit int
:param nfrac: The fractional part of the frequency tuning.
:type nfrac: 24-bit int
:param out_div: The RF output divider value.
:type out_div: 5-bit int
:param doubler: Enables or disables the output frequency doubler.
:type doubler: bool
:return: The command string required to effect the register
initialization.
.. note::
The synthesizer output frequency is controlled by the following
registers:
- Frequency register, integer part, ``Reg_03h[18:0]``
- Frequency register, fractional part, ``Reg_04h[23:0]``
The SD modulator is configured by the following:
- Fractional bypass, ``Reg_06h[7]``
- SD enable, ``Reg_06h[11]``
The VCO subsystem registers are accessed via ``Reg_05h``.
``Reg_05h`` is a special register used for indirect addressing
of the VCO subsystem. Writes to ``Reg_05h`` are automatically
forwarded to the VCO subsystem by the VCO SPI state machine
controller.
- ``Reg_05h[2:0]`` holds the VCO subsystem address: 0b000
- ``Reg_05h[6:3]`` is the VCO subsystem register address
- ``Reg_05h[15:7]`` is the data to be written to the VCO register
The VCO subsystem registers control the following:
- RF divide ratio, ``VCO_Reg_02h[5:0]``
- RF output buffer gain control, ``VCO_Reg_02h[7:6]``
- Divider output stage gain control, ``VCO_Reg_02h[8]``
- Fundamental/doubler mode selection, ``VCO_Reg_03h[0]``
- RF buffer bias, ``VCO_Reg_03h[4:3]``
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.init_registers(*plo1.divider_values(1000.0))
'W03,07,CA,30:HD1:LD1:W15,B2,64,48:HD1:LD1:W00,21,4d,38:HD1:LD1:WC1,BE,FF,40:HD1:LD1:W00,20,46,50:HD1:LD1:W07,C0,61,58:HD1:LD1:W00,00,C1,78:HD1:LD1:'
>>> plo2.init_registers(*plo2.divider_values(4207.0))
'W03,0F,4A,30:HD2:LD2:W59,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:'
"""
cmd = ''
# Setting nfrac to 0 indicates that integer mode should be used.
if nfrac == 0:
cmd += self.enable_sd_modulator(False)
else:
cmd += self.enable_sd_modulator(True)
# - Configure lock detect and auto-relock via Reg_07h
#
# 0000_0000_0010_0001_0100_1101_0011_1000
# | \__/| |\_/ \____/
# | | | | | Reg_07h
# | | | | lock detect window (2048)
# | | | enable lock detect
# | | lock detect window type (digital)
# | |
# | digital window duration (2 cycles)
# auto-relock
cmd += 'W00,21,4D,38:H{}:L{}:'.format(self.sen, self.sen)
cmd += self.config_analog_enables()
cmd += self.config_autocal()
# - Configure phase detect via Reg_0Bh
#
# 0000_0111_1100_0000_0110_0001_0101_1000
# \_/|| \_/ \____/
# | || | Reg_0Bh
# | || PD reset path delay
# | |enable PD UP output
# | enable PD DN output
# cycle slip prevention disabled
cmd += 'W07,C0,61,58:H{}:L{}:'.format(self.sen, self.sen)
# - Configure GPO and LD_SDO via Reg_0Fh
# We want LD_SDO to always provide the lock detect signal
#
# 0000_0000_0000_0000_1100_0001_0111_1000
# || \____/ \____/
# || | Reg_0Fh
# || GPO select: lock detect output
# |Prevent SDO automux. 1 = output GPO data only
# LD_SDO pin driver always on
cmd += 'W00,00,C1,78:H{}:L{}:'.format(self.sen, self.sen)
return cmd
[docs] def device_initialize(self, fout: float) -> str:
"""Configure the PLL after power up.
:param fout: The initial output frequency in MHz.
:type fout: float
:return: The command string required to effect the
device initialization.
To configure the PLL after power up:
1. Configure the reference divider (write to ``Reg_02h``), if
required.
2. Configure the delta-sigma modulator (write to
``Reg_06h``).
- Configuration involves selecting the mode of the
delta-sigma modulator (Mode A or Mode B), selection of the
delta-sigma modulator seed value, and configuration of the
delta-sigma modulator clock scheme. It is recommended to use
the values found in the Hittite PLL evaluation board control
software register files.
3. Configure the charge pump current and charge pump offset
current (write to ``Reg_09h``)
4. Configure the VCO Subsystem (write to ``Reg_05h``, for
more information see section 1.19, and
*3.0 VCO Subsystem Register Map*.
Detailed writes to the VCO subsystem via PLL ``Reg_05h`` at
start-up are available in the Register Setting Files found in
the Hittite PLL Evaluation Software
5. Configure the VCO output divider/doubler, if needed in the
VCO subsystem via PLL ``Reg_05h``.
6. Program the frequency of operation
- Program the integer part (write to ``Reg_03h``)
- Program the fractional part (write to ``Reg_04h``)
Once the HMC833LP6GE is configured after startup, in most
cases the user only needs to change frequencies by writing
to ``Reg_03h`` integer register, ``Reg_04h`` fractional
register, and ``Reg_05h`` to change the VCO output divider or
doubler setting if needed, and possibly adjust the charge
pump settings by writing to ``Reg_09h``.
>>> plo1 = hmc833('d6', 'd7')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.device_initialize(1000.0)
'W00,00,20,00:HD6:LD6:W00,00,02,08:HD6:LD6:W00,00,01,10:HD6:LD6:W00,01,88,28:HD6:LD6:W00,16,28,28:HD6:LD6:W00,60,A0,28:HD6:LD6:W00,01,10,28:HD6:LD6:W00,08,98,28:HD6:LD6:W00,00,00,28:HD6:LD6:W03,07,CA,30:HD6:LD6:W15,B2,64,48:HD6:LD6:W00,21,4d,38:HD6:LD6:WC1,BE,FF,40:HD6:LD6:W00,20,46,50:HD6:LD6:W07,C0,61,58:HD6:LD6:W00,00,C1,78:HD6:LD6:W00,00,28,18:HD6:LD6:W00,00,00,20:HD6:LD6:'
>>> plo1.device_initialize(3409.0)
'W00,00,20,00:HD6:LD6:W00,00,02,08:HD6:LD6:W00,00,01,10:HD6:LD6:W00,01,88,28:HD6:LD6:W00,16,28,28:HD6:LD6:W00,60,A0,28:HD6:LD6:W00,00,90,28:HD6:LD6:W00,00,18,28:HD6:LD6:W00,00,00,28:HD6:LD6:W03,0F,4A,30:HD6:LD6:W55,B2,64,48:HD6:LD6:W00,21,4d,38:HD6:LD6:WC1,BE,FF,40:HD6:LD6:W00,20,46,50:HD6:LD6:W07,C0,61,58:HD6:LD6:W00,00,C1,78:HD6:LD6:W00,00,22,18:HD6:LD6:W17,0a,3d,20:HD6:LD6:'
>>> plo2.device_initialize(1000.0)
'W00,00,20,00:HD2:LD2:LB5:W00,00,02,08:HD2:LD2:W00,00,04,10:HD2:LD2:W00,01,88,28:HD2:LD2:W00,16,28,28:HD2:LD2:W00,60,A0,28:HD2:LD2:W00,01,10,28:HD2:LD2:W00,08,98,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,07,CA,30:HD2:LD2:W19,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,20,18:HD2:LD2:W00,00,00,20:HD2:LD2:'
>>> plo2.device_initialize(2791.0)
'W00,00,20,00:HD2:LD2:LB5:W00,00,02,08:HD2:LD2:W00,00,04,10:HD2:LD2:W00,01,88,28:HD2:LD2:W00,16,28,28:HD2:LD2:W00,60,A0,28:HD2:LD2:W00,00,90,28:HD2:LD2:W00,08,98,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,0F,4A,30:HD2:LD2:W59,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,2c,18:HD2:LD2:Wa7,ef,9e,20:HD2:LD2:'
"""
self.__divide_ratio = None
self.__doubler = None
self.__nint = None
self.__nfrac = None
n_int, n_frac, doubler, divide_ratio = self.divider_values(fout)
cmd = self.chip_reset()
cmd += self.config_refsrc()
cmd += self.config_spi_chipen()
cmd += self.config_reference_divider()
cmd += self.init_vco_registers()
cmd += self.config_vco_registers(divide_ratio, doubler)
cmd += self.init_registers(n_int, n_frac, divide_ratio, doubler)
cmd += self.config_frequency_registers(n_int, n_frac)
return cmd
[docs] def config_device_state(self) -> None:
"""Configure the device state.
Set ``Reg_01h[0]`` (``rst_chipen_pin_select``) to 0 in order to
control PLL enable via SPI. PLL enable is then controlled using
``Reg_01h[1]`` (``rst_chipen_from_spi``) subject to masking
using the bits in ``Reg_01h[2:7]``.
If ``AutoRFO`` mode is enabled (via ``VCO_Reg_03h[2]`` = 0) as
recommended in the HMC833 datasheet, then the VCO subsystem is
powered up/down by setting ``VCO_Reg_01h[0]`` (Master enable VCO
subsystem) to 0b1 and 0b0 respectively. If ``AutoRFO`` mode is
disabled (via ``VCO_Reg_03h`` = 1) then the VCO subsystem is
powered down by setting ``VCO_Reg_01h[0]`` = 0 and setting
``VCO_Reg_01h[0]`` = 1 will power up the various VCO subsystem
components subject to the setting of the associated bit in
``VCO_Reg_01h``. In short, it's best to go with the recommended
``AutoRFO`` enabled setting in ``VCO_Reg_03h``
.. note::
Not currently implemented.
"""
pass
[docs] def config_analog_enables(self):
"""Configure the 'analog enables register' (Reg_08h).
:return: The command string required to set PLO Reg_08h.
"""
# - Configure analog enables via Reg_08h
#
# 1100_0001_1011_1110_1111_1111_0100_0000
# | || || |||| \____/
# | || || |||| Reg_08h
# | || || |||bias ref. enable
# | || || ||charge pump enable
# | || || |phase det. enable
# | || || ref. buffer enable
# | || |VCO buffer enable
# | || GPO enable
# | |prescaler clk enable
# | VCO buf. and prescaler bias enable
# high freq. ref. 1 = >=200MHz, 0 = <200MHz
#
# Note: setting Reg_08h[21] to 1 appears to cause the
# PLO to have lock problems.
# In any case, for reference input powers of
# about -5dBm leaving Reg_08h[21] set to 0 does
# not cause any issues even with ref. freqs >350MHz.
cmd = 'WC1,BE,FF,40:H{}:L{}:'.format(self.sen, self.sen)
return cmd
[docs] def config_autocal(self):
"""Configure the 'autocal register' (Reg_0Ah).
:return: The command string required to set PLO Reg_0Ah.
"""
# - Configure VCO autocal via Reg_0Ah
#
# 0000_0000_0010_0000_0100_0110_0101_0000
# \/ \/ \/\__/\_/ \____/
# | | | | | Reg_0Ah
# | | | | vtune resolution (128)
# | | | vco curve adjust: disabled
# | | wait state setup
# | number of SAR bits in VCO
# FM/VSPI clk select, max. 50MHz (input ref/4)
# Note that if fref is over 200 MHz then Reg_0Ah[14:13]
# (FM/VSPI clk select) needs to be set to an appropriate
# value (see Section 2.12 of the HMC833 datasheet).
if self.fref > 200.0:
cmd = 'W00,40,46,50:H{}:L{}:'.format(self.sen, self.sen)
else:
cmd = 'W00,20,46,50:H{}:L{}:'.format(self.sen, self.sen)
return cmd
[docs] def config_frequency(self,
fout: float,
full_reg_update: bool = False) -> str:
"""Configure for a specified output frequency
When configuring the PLO registers for a specified frequency we try
to minimize the number of register updates in order to reduce the time
taken to effect the changes. This optimization process produces
incorrect results in some circumstances, for example when the PLO
reference divider is changed. In cases such as this the ~full_reg_update~
should be set to ~True~ in order to update all relevant PLO registers
when configuring the output frequency.
:param fout: The required output frequency in MHz.
:type fout: float
:param reset_state: Force an update of the relevant device registers.
:type reset_state: bool
:return: The command string required to effect the specified
frequency output.
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo2.device_initialize(1000.0)
'W00,00,20,00:HD2:LD2:LB5:W00,00,02,08:HD2:LD2:W00,00,04,10:HD2:LD2:W00,01,88,28:HD2:LD2:W00,16,28,28:HD2:LD2:W00,60,A0,28:HD2:LD2:W00,01,10,28:HD2:LD2:W00,08,98,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,07,CA,30:HD2:LD2:W19,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,20,18:HD2:LD2:W00,00,00,20:HD2:LD2:'
>>> plo2.config_frequency(512.0)
'W00,02,10,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,0F,4A,30:HD2:LD2:W59,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,20,18:HD2:LD2:Wc4,9b,a6,20:HD2:LD2:'
>>> plo2.config_frequency(764.0)
'W00,01,10,28:HD2:LD2:W00,00,00,28:HD2:LD2:W00,00,18,18:HD2:LD2:W72,b0,21,20:HD2:LD2:'
>>> plo2.config_frequency(500.0)
'W00,03,10,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,07,CA,30:HD2:LD2:W19,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,30,18:HD2:LD2:W00,00,00,20:HD2:LD2:'
>>> plo2.config_frequency(1000.0)
'W00,01,10,28:HD2:LD2:W00,00,00,28:HD2:LD2:W00,00,20,18:HD2:LD2:'
"""
if full_reg_update:
self.__doubler = self.__divide_ratio = None
self.__nint = self.__nfrac = None
n_int, n_frac, doubler, divide_ratio = self.divider_values(fout)
cmd = ''
cmd += self.config_vco_registers(divide_ratio, doubler)
if full_reg_update:
cmd += self.init_registers(n_int, n_frac, divide_ratio, doubler)
elif (n_frac == 0 and self.__nfrac != 0) or \
(self.__nfrac == 0 and n_frac != 0):
# Then the SD fractional modulator state needs to change.
cmd += self.init_registers(n_int, n_frac, divide_ratio, doubler)
cmd += self.config_frequency_registers(n_int, n_frac)
return cmd
[docs] def config_gain(self) -> str:
"""Configure current ``buf_gain`` and ``div_gain`` settings.
:return:
A command string to configure the device gain settings.
.. seealso::
:py:meth:`config_vco_registers`
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.device_initialize(1000.0)
'W00,00,20,00:HD1:LD1:W00,00,02,08:HD1:LD1:W00,00,01,10:HD1:LD1:W00,01,88,28:HD1:LD1:W00,16,28,28:HD1:LD1:W00,60,A0,28:HD1:LD1:W00,01,10,28:HD1:LD1:W00,08,98,28:HD1:LD1:W00,00,00,28:HD1:LD1:W03,07,CA,30:HD1:LD1:W15,B2,64,48:HD1:LD1:W00,21,4d,38:HD1:LD1:WC1,BE,FF,40:HD1:LD1:W00,20,46,50:HD1:LD1:W07,C0,61,58:HD1:LD1:W00,00,C1,78:HD1:LD1:W00,00,28,18:HD1:LD1:W00,00,00,20:HD1:LD1:'
>>> plo2.device_initialize(1000.0)
'W00,00,20,00:HD2:LD2:LB5:W00,00,02,08:HD2:LD2:W00,00,04,10:HD2:LD2:W00,01,88,28:HD2:LD2:W00,16,28,28:HD2:LD2:W00,60,A0,28:HD2:LD2:W00,01,10,28:HD2:LD2:W00,08,98,28:HD2:LD2:W00,00,00,28:HD2:LD2:W03,07,CA,30:HD2:LD2:W19,32,64,48:HD2:LD2:W00,21,4d,38:HD2:LD2:WC1,BE,FF,40:HD2:LD2:W00,40,46,50:HD2:LD2:W07,C0,61,58:HD2:LD2:W00,00,C1,78:HD2:LD2:W00,00,20,18:HD2:LD2:W00,00,00,20:HD2:LD2:'
>>> plo1.auto_gain(1000.0)
>>> plo2.auto_gain(1000.0)
>>> plo1.config_gain()
'W00,81,10,28:HD1:LD1:'
>>> plo2.config_gain()
'W00,81,10,28:HD2:LD2:'
>>> plo2.auto_gain(3817.0)
>>> plo2.config_gain()
'W00,41,10,28:HD2:LD2:'
>>> plo2.buf_gain = hmc833.OutputBufferGain.MAXGAIN
>>> plo2.config_gain()
'W00,61,10,28:HD2:LD2:'
>>> plo2.buf_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_3DB
>>> plo2.config_gain()
'W00,41,10,28:HD2:LD2:'
"""
# It's easiest just to invoke config_vco_divider.
# This will also (re)set VCO_Reg_03h.
cmd = self.config_vco_divider(self.__divide_ratio)
return cmd
[docs] def config_vco_mute(self) -> str:
"""Enable or disable VCO output while maintaining PLL/VCO lock.
The mute function provides over 40 dB of isolation throughout
the operating range of the HMC833LP6GE.
:return: The command string to effect the currently set mute state.
>>> plo1 = hmc833('d1', 'c5')
>>> plo2 = hmc833('d2', 'c6', 'b5', fref=250.0, refdiv=4,
... refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> plo1.device_initialize(1000.0)
'W00,00,20,00:HD1:LD1:W00,00,02,08:HD1:LD1:W00,00,01,10:HD1:LD1:W00,01,88,28:HD1:LD1:W00,16,28,28:HD1:LD1:W00,60,A0,28:HD1:LD1:W00,01,10,28:HD1:LD1:W00,08,98,28:HD1:LD1:W00,00,00,28:HD1:LD1:W03,07,CA,30:HD1:LD1:W15,B2,64,48:HD1:LD1:W00,21,4d,38:HD1:LD1:WC1,BE,FF,40:HD1:LD1:W00,20,46,50:HD1:LD1:W07,C0,61,58:HD1:LD1:W00,00,C1,78:HD1:LD1:W00,00,28,18:HD1:LD1:W00,00,00,20:HD1:LD1:'
>>> plo1.config_vco_mute()
'W00,01,88,28:HD1:LD1:W00,08,98,28:HD1:LD1:'
"""
# VCO_Reg_01h
#
# 8 7 6 5 4 3 2 1 0
# | |
# | Master Enable VCO Subsystem
# Manual Mode PLL buffer enable
vco_reg_01h = 0b0000_0011
reg_05h = '{:06X}'.format((((vco_reg_01h << 7) | 0b0001_000) << 8)
| 0b0101_000)
cmd = 'W00,{},{},{}:H{}:L{}:'.format(reg_05h[0:2], reg_05h[2:4],
reg_05h[4:6], self.sen, self.sen)
cmd += self.config_vco_doubler(self.__doubler)
return cmd
def close_vco_config(self):
return 'W00,00,00,28:H{}:L{}:'.format(self.sen, self.sen)
[docs] def check_is_locked(self) -> str:
"""Check if the device PLL is locked.
:return: The command string for checking the device PLL lock status.
"""
return 'P{}:'.format(self.ld_sdo)
if __name__ == '__main__':
import doctest
doctest.testmod()