IO control on pluto appears to be working
This commit is contained in:
parent
67ddbb0f90
commit
1a76c4e7ab
|
@ -105,7 +105,7 @@ class MainWindow(QMainWindow):
|
|||
prog_sweep.setMaximum(100)
|
||||
prog_sweep.setFormat("%v / %m")
|
||||
# prog_sweep.setTextVisible(False)
|
||||
prog_sweep.setValue(50)
|
||||
prog_sweep.setValue(0)
|
||||
window_layout.addWidget(prog_sweep)
|
||||
self.prog_sweep = prog_sweep
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# %% imports
|
||||
import copy
|
||||
from enum import IntEnum, unique
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, Tuple
|
||||
from typing import Any, Callable, Dict, Literal, Tuple
|
||||
|
||||
import adi
|
||||
|
||||
# import iio
|
||||
import iio
|
||||
import numpy as np
|
||||
import skrf as rf
|
||||
import xarray as xr
|
||||
|
@ -34,6 +34,38 @@ def generate_tone(f: float, fs: float, N: int = 1024, scale: int = 2**14):
|
|||
return iq
|
||||
|
||||
|
||||
@unique
|
||||
class AD9361Register(IntEnum):
|
||||
AUXDAC1_WORD = 0x018
|
||||
AUXDAC2_WORD = 0x019
|
||||
AUXDAC1_CONFIG = 0x01A
|
||||
AUXDAC2_CONFIG = 0x01B
|
||||
AUXADC_CLOCK_DIVIDER = 0x01C
|
||||
AUXADC_CONFIG = 0x01D
|
||||
AUXADC_WORD_MSB = 0x01E
|
||||
AUXADC_WORD_LSB = 0x01F
|
||||
AUTO_GPIO = 0x020
|
||||
AGC_GAIN_LOCK_DELAY = 0x021
|
||||
AGC_ATTACK_DELAY = 0x022
|
||||
AUXDAC_ENABLE_CONTROL = 0x023
|
||||
RX_LOAD_SYNTH_DELAY = 0x024
|
||||
TX_LOAD_SYNTH_DELAY = 0x025
|
||||
EXTERNAL_LNA_CONTROL = 0x026
|
||||
GPO_FORCE_AND_INIT = 0x027
|
||||
GPO0_RX_DELAY = 0x028
|
||||
GPO1_RX_DELAY = 0x029
|
||||
GPO2_RX_DELAY = 0x02A
|
||||
GPO3_RX_DELAY = 0x02B
|
||||
GPO0_TX_DELAY = 0x02C
|
||||
GPO1_TX_DELAY = 0x02D
|
||||
GPO2_TX_DELAY = 0x02E
|
||||
GPO3_TX_DELAY = 0x02F
|
||||
AUXDAC1_RX_DELAY = 0x030
|
||||
AUXDAC1_TX_DELAY = 0x031
|
||||
AUXDAC2_RX_DELAY = 0x032
|
||||
AUXDAC2_TX_DELAY = 0x033
|
||||
|
||||
|
||||
class Charon:
|
||||
FREQUENCY_OFFSET = 1e6
|
||||
|
||||
|
@ -49,7 +81,8 @@ class Charon:
|
|||
self.frequency = frequency
|
||||
|
||||
# everything RF
|
||||
self.sdr = adi.ad9361(uri=f"ip:{ip}")
|
||||
uri = f"ip:{ip}"
|
||||
self.sdr = adi.ad9361(uri=uri)
|
||||
for attr, expected in [
|
||||
("adi,2rx-2tx-mode-enable", True),
|
||||
# ("adi,gpo-manual-mode-enable", True),
|
||||
|
@ -81,14 +114,16 @@ class Charon:
|
|||
self.sdr.tx_hardwaregain_chan0 = -10
|
||||
|
||||
# # switch control
|
||||
# ctx = iio.Context(uri)
|
||||
# self.ctrl = ctx.find_device("ad9361-phy")
|
||||
# # raw ad9361 register accesss:
|
||||
# # https://ez.analog.com/linux-software-drivers/f/q-a/120853/control-fmcomms3-s-gpo-with-python
|
||||
# # https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf # noqa: E501
|
||||
# self.ctrl.reg_write(0x26, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
|
||||
# self._set_gpo(self.ports[0] - 1)
|
||||
# # TODO: init AuxDAC
|
||||
ctx = iio.Context(uri)
|
||||
self.ctrl = ctx.find_device("ad9361-phy")
|
||||
# raw ad9361 register accesss:
|
||||
# https://ez.analog.com/linux-software-drivers/f/q-a/120853/control-fmcomms3-s-gpo-with-python
|
||||
# https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf # noqa: E501
|
||||
self.ctrl.reg_write(AD9361Register.EXTERNAL_LNA_CONTROL, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
|
||||
self.ctrl.reg_write(AD9361Register.AUXDAC_ENABLE_CONTROL, 0x3F)
|
||||
self._set_gpo(0b0000)
|
||||
self._set_dac(value=0, channel=1)
|
||||
self._set_dac(value=0, channel=2)
|
||||
|
||||
def get_config(self) -> Dict[str, Any]:
|
||||
config = dict()
|
||||
|
@ -113,10 +148,45 @@ class Charon:
|
|||
return config
|
||||
|
||||
def _get_gpo(self) -> int:
|
||||
return (self.ctrl.reg_read(0x27) >> 4) & 0x0F
|
||||
return (self.ctrl.reg_read(AD9361Register.GPO_FORCE_AND_INIT) >> 4) & 0x0F
|
||||
|
||||
def _set_gpo(self, value: int) -> None:
|
||||
self.ctrl.reg_write(0x27, (value & 0x0F) << 4) # bits 7-4: GPO3-0
|
||||
self.ctrl.reg_write(AD9361Register.GPO_FORCE_AND_INIT, (value & 0x0F) << 4) # bits 7-4: GPO3-0
|
||||
|
||||
def _set_dac(self, value: int, channel: Literal[1, 2]):
|
||||
if channel not in [1, 2]:
|
||||
raise ValueError(f"Invalid channel {channel}. Must be 1 or 2")
|
||||
|
||||
if value > 0x3FF or value < 0:
|
||||
raise ValueError("Invalid value for 10 bit DAC. Must be between 0 and 0x3FF (inclusive)")
|
||||
|
||||
@unique
|
||||
class Vref(IntEnum):
|
||||
VREF_1V0 = 0b00
|
||||
VREF_1V5 = 0b01
|
||||
VREF_2V0 = 0b10
|
||||
VREF_2V5 = 0b11
|
||||
|
||||
@unique
|
||||
class StepFactor(IntEnum):
|
||||
FACTOR_2 = 0b0
|
||||
FACTOR_1 = 0b1
|
||||
|
||||
# https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf
|
||||
# page 13
|
||||
# vout = 0.97 * vref + (0.000738 + 9e-6 * (vref * 1.6 - 2)) * auxdac_word[9:0] * step_factor - 0.3572 * step_factor + 0.05
|
||||
# vout ~= (vref - 0.3572 * step_factor) + 0.000738 * auxdac_word[9:0] * step_factor
|
||||
# which gives a 1.5V swing with step_factor == 2 and 0.75V swing with step_factor == 1
|
||||
# vref basically just changes the minimum voltage with negligible impact on output scaling
|
||||
|
||||
self.ctrl.reg_write(
|
||||
AD9361Register.__getitem__(f"AUXDAC{channel}_WORD"),
|
||||
(value >> 2) & 0xFF,
|
||||
)
|
||||
self.ctrl.reg_write(
|
||||
AD9361Register.__getitem__(f"AUXDAC{channel}_CONFIG"),
|
||||
(value & 0x3) | (Vref.VREF_1V0.value << 2) | (StepFactor.FACTOR_2 << 4),
|
||||
)
|
||||
|
||||
def set_output_power(self, power: float):
|
||||
# FIXME: this is a hack because I don't want to go through re-calibration
|
||||
|
|
Loading…
Reference in New Issue
Block a user