Reference control added
This commit is contained in:
parent
6960498fcb
commit
e266d37b96
Binary file not shown.
@ -82,14 +82,11 @@ Device::~Device()
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::Configure(Protocol::SweepSettings settings)
|
||||
bool Device::SendPacket(Protocol::PacketInfo packet)
|
||||
{
|
||||
if(m_connected) {
|
||||
unsigned char buffer[128];
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::SweepSettings;
|
||||
p.settings = settings;
|
||||
unsigned int length = Protocol::EncodePacket(p, buffer, sizeof(buffer));
|
||||
unsigned char buffer[1024];
|
||||
unsigned int length = Protocol::EncodePacket(packet, buffer, sizeof(buffer));
|
||||
if(!length) {
|
||||
qCritical() << "Failed to encode packet";
|
||||
return false;
|
||||
@ -107,78 +104,35 @@ bool Device::Configure(Protocol::SweepSettings settings)
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::Configure(Protocol::SweepSettings settings)
|
||||
{
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::SweepSettings;
|
||||
p.settings = settings;
|
||||
return SendPacket(p);
|
||||
}
|
||||
|
||||
bool Device::SetManual(Protocol::ManualControl manual)
|
||||
{
|
||||
if(m_connected) {
|
||||
unsigned char buffer[128];
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::ManualControl;
|
||||
p.manual = manual;
|
||||
unsigned int length = Protocol::EncodePacket(p, buffer, sizeof(buffer));
|
||||
if(!length) {
|
||||
qCritical() << "Failed to encode packet";
|
||||
return false;
|
||||
}
|
||||
int actual_length;
|
||||
auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0);
|
||||
if(ret < 0) {
|
||||
qCritical() << "Error sending data: "
|
||||
<< libusb_strerror((libusb_error) ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::ManualControl;
|
||||
p.manual = manual;
|
||||
return SendPacket(p);
|
||||
}
|
||||
|
||||
bool Device::SendFirmwareChunk(Protocol::FirmwarePacket &fw)
|
||||
{
|
||||
if(m_connected) {
|
||||
unsigned char buffer[Protocol::FirmwareChunkSize + 4 + 8];
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::FirmwarePacket;
|
||||
p.firmware = fw;
|
||||
unsigned int length = Protocol::EncodePacket(p, buffer, sizeof(buffer));
|
||||
if(!length) {
|
||||
qCritical() << "Failed to encode packet";
|
||||
return false;
|
||||
}
|
||||
int actual_length;
|
||||
auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0);
|
||||
if(ret < 0) {
|
||||
qCritical() << "Error sending data: "
|
||||
<< libusb_strerror((libusb_error) ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::FirmwarePacket;
|
||||
p.firmware = fw;
|
||||
return SendPacket(p);
|
||||
}
|
||||
|
||||
bool Device::SendCommandWithoutPayload(Protocol::PacketType type)
|
||||
{
|
||||
if(m_connected) {
|
||||
unsigned char buffer[32];
|
||||
Protocol::PacketInfo p;
|
||||
p.type = type;
|
||||
unsigned int length = Protocol::EncodePacket(p, buffer, sizeof(buffer));
|
||||
if(!length) {
|
||||
qCritical() << "Failed to encode packet";
|
||||
return false;
|
||||
}
|
||||
int actual_length;
|
||||
auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0);
|
||||
if(ret < 0) {
|
||||
qCritical() << "Error sending data: "
|
||||
<< libusb_strerror((libusb_error) ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
Protocol::PacketInfo p;
|
||||
p.type = type;
|
||||
return SendPacket(p);
|
||||
}
|
||||
|
||||
std::set<QString> Device::GetDevices()
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
// connect to a VNA device. If serial is specified only connecting to this device, otherwise to the first one found
|
||||
Device(QString serial = QString());
|
||||
~Device();
|
||||
bool SendPacket(Protocol::PacketInfo packet);
|
||||
bool Configure(Protocol::SweepSettings settings);
|
||||
bool SetManual(Protocol::ManualControl manual);
|
||||
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
|
||||
|
@ -403,7 +403,6 @@ void VNA::ConnectToDevice(QString serial)
|
||||
lConnectionStatus.setText("Connected to " + device->serial());
|
||||
qInfo() << "Connected to " << device->serial();
|
||||
lDeviceInfo.setText(device->getLastDeviceInfoString());
|
||||
device->Configure(settings);
|
||||
connect(device, &Device::DatapointReceived, this, &VNA::NewDatapoint);
|
||||
connect(device, &Device::LogLineReceived, &deviceLog, &DeviceLog::addLine);
|
||||
connect(device, &Device::ConnectionLost, this, &VNA::DeviceConnectionLost);
|
||||
@ -414,21 +413,26 @@ void VNA::ConnectToDevice(QString serial)
|
||||
ui->actionManual_Control->setEnabled(true);
|
||||
ui->menuDefault_Calibration->setEnabled(true);
|
||||
ui->actionFirmware_Update->setEnabled(true);
|
||||
// Check if default calibration exists and attempt to load it
|
||||
QSettings settings;
|
||||
auto key = "DefaultCalibration"+device->serial();
|
||||
if (settings.contains(key)) {
|
||||
auto filename = settings.value(key).toString();
|
||||
qDebug() << "Attempting to load default calibration file \"" << filename << "\"";
|
||||
if(QFile::exists(filename)) {
|
||||
cal.openFromFile(filename);
|
||||
ApplyCalibration(cal.getType());
|
||||
{
|
||||
// Check if default calibration exists and attempt to load it
|
||||
QSettings settings;
|
||||
auto key = "DefaultCalibration"+device->serial();
|
||||
if (settings.contains(key)) {
|
||||
auto filename = settings.value(key).toString();
|
||||
qDebug() << "Attempting to load default calibration file \"" << filename << "\"";
|
||||
if(QFile::exists(filename)) {
|
||||
cal.openFromFile(filename);
|
||||
ApplyCalibration(cal.getType());
|
||||
}
|
||||
ui->actionRemoveDefaultCal->setEnabled(true);
|
||||
} else {
|
||||
qDebug() << "No default calibration file set for this device";
|
||||
ui->actionRemoveDefaultCal->setEnabled(false);
|
||||
}
|
||||
ui->actionRemoveDefaultCal->setEnabled(true);
|
||||
} else {
|
||||
qDebug() << "No default calibration file set for this device";
|
||||
ui->actionRemoveDefaultCal->setEnabled(false);
|
||||
}
|
||||
// Configure initial state of device
|
||||
device->Configure(settings);
|
||||
UpdateReference();
|
||||
} catch (const runtime_error e) {
|
||||
DisconnectDevice();
|
||||
UpdateDeviceList();
|
||||
@ -553,26 +557,28 @@ void VNA::CreateToolbars()
|
||||
// Reference toolbar
|
||||
auto tb_reference = new QToolBar("Reference", this);
|
||||
tb_reference->addWidget(new QLabel("Ref:"));
|
||||
toolbars.referenceType = new QComboBox();
|
||||
toolbars.referenceType->addItem("Int");
|
||||
toolbars.referenceType->addItem("Ext");
|
||||
auto refInAuto = new QCheckBox("Auto");
|
||||
refInAuto->setChecked(true);
|
||||
toolbars.referenceType->setEnabled(false);
|
||||
connect(refInAuto, &QCheckBox::clicked, [this](bool checked) {
|
||||
// TODO change device settings
|
||||
toolbars.referenceType->setEnabled(!checked);
|
||||
toolbars.reference.type = new QComboBox();
|
||||
toolbars.reference.type->addItem("Int");
|
||||
toolbars.reference.type->addItem("Ext");
|
||||
toolbars.reference.automatic = new QCheckBox("Auto");
|
||||
connect(toolbars.reference.automatic, &QCheckBox::clicked, [this](bool checked) {
|
||||
toolbars.reference.type->setEnabled(!checked);
|
||||
UpdateReference();
|
||||
});
|
||||
tb_reference->addWidget(toolbars.referenceType);
|
||||
tb_reference->addWidget(refInAuto);
|
||||
// toolbars.reference.automatic->setChecked(true);
|
||||
tb_reference->addWidget(toolbars.reference.type);
|
||||
tb_reference->addWidget(toolbars.reference.automatic);
|
||||
tb_reference->addSeparator();
|
||||
tb_reference->addWidget(new QLabel("Ref out:"));
|
||||
auto refOutEnabled = new QCheckBox();
|
||||
auto refOutFreq = new QComboBox();
|
||||
refOutFreq->addItem("10 MHz");
|
||||
refOutFreq->addItem("100 MHz");
|
||||
tb_reference->addWidget(refOutEnabled);
|
||||
tb_reference->addWidget(refOutFreq);
|
||||
toolbars.reference.outputEnabled = new QCheckBox();
|
||||
toolbars.reference.outFreq = new QComboBox();
|
||||
toolbars.reference.outFreq->addItem("10 MHz");
|
||||
toolbars.reference.outFreq->addItem("100 MHz");
|
||||
tb_reference->addWidget(toolbars.reference.outputEnabled);
|
||||
tb_reference->addWidget(toolbars.reference.outFreq);
|
||||
connect(toolbars.reference.type, qOverload<int>(&QComboBox::currentIndexChanged), this, &VNA::UpdateReference);
|
||||
connect(toolbars.reference.outFreq, qOverload<int>(&QComboBox::currentIndexChanged), this, &VNA::UpdateReference);
|
||||
connect(toolbars.reference.outputEnabled, &QCheckBox::clicked, this, &VNA::UpdateReference);
|
||||
|
||||
addToolBar(tb_reference);
|
||||
|
||||
@ -860,6 +866,35 @@ void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
|
||||
});
|
||||
}
|
||||
|
||||
void VNA::UpdateReference()
|
||||
{
|
||||
if(!device) {
|
||||
// can't update without a device connected
|
||||
return;
|
||||
}
|
||||
Protocol::ReferenceSettings s = {};
|
||||
if(toolbars.reference.automatic->isChecked()) {
|
||||
s.AutomaticSwitch = 1;
|
||||
}
|
||||
if(toolbars.reference.type->currentText()=="Ext") {
|
||||
s.UseExternalRef = 1;
|
||||
}
|
||||
if(toolbars.reference.outputEnabled->isChecked()) {
|
||||
switch(toolbars.reference.outFreq->currentIndex()) {
|
||||
case 0:
|
||||
s.ExtRefOuputFreq = 10000000;
|
||||
break;
|
||||
case 1:
|
||||
s.ExtRefOuputFreq = 100000000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::Reference;
|
||||
p.reference = s;
|
||||
device->SendPacket(p);
|
||||
}
|
||||
|
||||
void VNA::ConstrainAndUpdateFrequencies()
|
||||
{
|
||||
if(settings.f_stop > maxFreq) {
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "Device/devicelog.h"
|
||||
#include "preferences.h"
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
@ -63,6 +64,7 @@ private slots:
|
||||
void DisableCalibration(bool force = false);
|
||||
void ApplyCalibration(Calibration::Type type);
|
||||
void StartCalibrationMeasurement(Calibration::Measurement m);
|
||||
void UpdateReference();
|
||||
|
||||
signals:
|
||||
void CalibrationMeasurementComplete(Calibration::Measurement m);
|
||||
@ -110,7 +112,12 @@ private:
|
||||
QStackedWidget *central;
|
||||
|
||||
struct {
|
||||
QComboBox *referenceType;
|
||||
struct {
|
||||
QComboBox *type;
|
||||
QCheckBox *automatic;
|
||||
QCheckBox *outputEnabled;
|
||||
QComboBox *outFreq;
|
||||
} reference;
|
||||
} toolbars;
|
||||
|
||||
Preferences pref;
|
||||
|
@ -100,6 +100,7 @@ void App_Start() {
|
||||
|
||||
uint32_t lastNewPoint = HAL_GetTick();
|
||||
bool sweepActive = false;
|
||||
Protocol::ReferenceSettings reference;
|
||||
|
||||
while (1) {
|
||||
uint32_t notification;
|
||||
@ -113,39 +114,16 @@ void App_Start() {
|
||||
lastNewPoint = HAL_GetTick();
|
||||
if(result.pointNum == settings.points - 1) {
|
||||
// end of sweep
|
||||
// read PLL temperatures
|
||||
uint8_t tempSource, tempLO;
|
||||
VNA::GetTemps(&tempSource, &tempLO);
|
||||
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
|
||||
// Read ADC min/max
|
||||
auto limits = FPGA::GetADCLimits();
|
||||
#define ADC_LIMIT 30000
|
||||
VNA::Ref::applySettings(reference);
|
||||
// Compile info packet
|
||||
packet.type = Protocol::PacketType::DeviceInfo;
|
||||
packet.info.FPGA_configured = 1;
|
||||
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|
||||
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|
||||
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
|
||||
packet.info.ADC_overload = true;
|
||||
} else {
|
||||
packet.info.ADC_overload = false;
|
||||
}
|
||||
packet.info.FW_major = FW_MAJOR;
|
||||
packet.info.FW_minor = FW_MINOR;
|
||||
packet.info.HW_Revision = HW_REVISION;
|
||||
auto status = FPGA::GetStatus();
|
||||
packet.info.LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
|
||||
packet.info.source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
|
||||
packet.info.extRefAvailable = 0;
|
||||
packet.info.extRefInUse = 0;
|
||||
packet.info.temperatures.LO1 = tempLO;
|
||||
packet.info.temperatures.source = tempSource;
|
||||
packet.info.temperatures.MCU = 0;
|
||||
VNA::fillDeviceInfo(&packet.info);
|
||||
Communication::Send(packet);
|
||||
FPGA::ResetADCLimits();
|
||||
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
|
||||
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
|
||||
limits.Rmin, limits.Rmax);
|
||||
// Start next sweep
|
||||
FPGA::StartSweep();
|
||||
}
|
||||
@ -194,6 +172,13 @@ void App_Start() {
|
||||
manual = packet.manual;
|
||||
VNA::ConfigureManual(manual, VNAStatusCallback);
|
||||
break;
|
||||
case Protocol::PacketType::Reference:
|
||||
reference = packet.reference;
|
||||
if(!sweepActive) {
|
||||
// can update right now
|
||||
VNA::Ref::applySettings(reference);
|
||||
}
|
||||
break;
|
||||
#ifdef HAS_FLASH
|
||||
case Protocol::PacketType::ClearFlash:
|
||||
FPGA::AbortSweep();
|
||||
@ -238,7 +223,10 @@ void App_Start() {
|
||||
if(sweepActive && HAL_GetTick() - lastNewPoint > 1000) {
|
||||
LOG_WARN("Timed out waiting for point, last received point was %d", result.pointNum);
|
||||
LOG_WARN("FPGA status: 0x%04x", FPGA::GetStatus());
|
||||
FPGA::AbortSweep();
|
||||
// restart the current sweep
|
||||
VNA::Init();
|
||||
VNA::Ref::applySettings(reference);
|
||||
VNA::ConfigureSweep(settings, VNACallback);
|
||||
sweepActive = true;
|
||||
lastNewPoint = HAL_GetTick();
|
||||
|
@ -187,6 +187,23 @@ static int16_t EncodeSweepSettings(Protocol::SweepSettings d, uint8_t *buf,
|
||||
return e.getSize();
|
||||
}
|
||||
|
||||
static Protocol::ReferenceSettings DecodeReferenceSettings(uint8_t *buf) {
|
||||
Protocol::ReferenceSettings d;
|
||||
Decoder e(buf);
|
||||
e.get<uint32_t>(d.ExtRefOuputFreq);
|
||||
d.AutomaticSwitch = e.getBits(1);
|
||||
d.UseExternalRef = e.getBits(1);
|
||||
return d;
|
||||
}
|
||||
static int16_t EncodeReferenceSettings(Protocol::ReferenceSettings d, uint8_t *buf,
|
||||
uint16_t bufSize) {
|
||||
Encoder e(buf, bufSize);
|
||||
e.add<uint32_t>(d.ExtRefOuputFreq);
|
||||
e.addBits(d.AutomaticSwitch, 1);
|
||||
e.addBits(d.UseExternalRef, 1);
|
||||
return e.getSize();
|
||||
}
|
||||
|
||||
static Protocol::DeviceInfo DecodeDeviceInfo(uint8_t *buf) {
|
||||
Protocol::DeviceInfo d;
|
||||
Decoder e(buf);
|
||||
@ -388,6 +405,9 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
|
||||
case PacketType::SweepSettings:
|
||||
info->settings = DecodeSweepSettings(&data[4]);
|
||||
break;
|
||||
case PacketType::Reference:
|
||||
info->reference = DecodeReferenceSettings(&data[4]);
|
||||
break;
|
||||
case PacketType::DeviceInfo:
|
||||
info->info = DecodeDeviceInfo(&data[4]);
|
||||
break;
|
||||
@ -403,6 +423,7 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
|
||||
case PacketType::Ack:
|
||||
case PacketType::PerformFirmwareUpdate:
|
||||
case PacketType::ClearFlash:
|
||||
case PacketType::Nack:
|
||||
// no payload, nothing to do
|
||||
break;
|
||||
case PacketType::None:
|
||||
@ -421,6 +442,9 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
|
||||
case PacketType::SweepSettings:
|
||||
payload_size = EncodeSweepSettings(packet.settings, &dest[4], destsize - 8);
|
||||
break;
|
||||
case PacketType::Reference:
|
||||
payload_size = EncodeReferenceSettings(packet.reference, &dest[4], destsize - 8);
|
||||
break;
|
||||
case PacketType::DeviceInfo:
|
||||
payload_size = EncodeDeviceInfo(packet.info, &dest[4], destsize - 8);
|
||||
break;
|
||||
@ -436,6 +460,7 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
|
||||
case PacketType::Ack:
|
||||
case PacketType::PerformFirmwareUpdate:
|
||||
case PacketType::ClearFlash:
|
||||
case PacketType::Nack:
|
||||
// no payload, nothing to do
|
||||
break;
|
||||
case PacketType::None:
|
||||
|
@ -23,6 +23,12 @@ using SweepSettings = struct _sweepSettings {
|
||||
int16_t cdbm_excitation; // in 1/100 dbm
|
||||
};
|
||||
|
||||
using ReferenceSettings = struct _referenceSettings {
|
||||
uint32_t ExtRefOuputFreq;
|
||||
uint8_t AutomaticSwitch:1;
|
||||
uint8_t UseExternalRef:1;
|
||||
};
|
||||
|
||||
using DeviceInfo = struct _deviceInfo {
|
||||
uint16_t FW_major;
|
||||
uint16_t FW_minor;
|
||||
@ -102,6 +108,7 @@ enum class PacketType : uint8_t {
|
||||
ClearFlash = 8,
|
||||
PerformFirmwareUpdate = 9,
|
||||
Nack = 10,
|
||||
Reference = 11,
|
||||
};
|
||||
|
||||
using PacketInfo = struct _packetinfo {
|
||||
@ -109,6 +116,7 @@ using PacketInfo = struct _packetinfo {
|
||||
union {
|
||||
Datapoint datapoint;
|
||||
SweepSettings settings;
|
||||
ReferenceSettings reference;
|
||||
DeviceInfo info;
|
||||
ManualControl manual;
|
||||
ManualStatus status;
|
||||
|
@ -63,6 +63,8 @@ bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size)
|
||||
|
||||
bool FPGA::Init(HaltedCallback cb) {
|
||||
halted_cb = cb;
|
||||
SysCtrlReg = 0;
|
||||
ISRMaskReg = 0;
|
||||
// Reset FPGA
|
||||
High(FPGA_RESET);
|
||||
SetMode(Mode::FPGA);
|
||||
|
@ -2,12 +2,32 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
||||
#define LOG_MODULE "SI5351"
|
||||
#include "Log.h"
|
||||
|
||||
bool Si5351C::Init(uint32_t clkin_freq) {
|
||||
bool success = true;
|
||||
FreqCLKINDiv = 0;
|
||||
|
||||
// Disable OEB pin functionality
|
||||
success &= WriteRegister(Reg::OEBPinMask, 0xFF);
|
||||
// Disable all outputs
|
||||
success &= WriteRegister(Reg::OutputEnableControl, 0xFF);
|
||||
|
||||
// Enable fanouts
|
||||
success &= WriteRegister(Reg::FanoutEnable, 0xD0);
|
||||
|
||||
if(success) {
|
||||
LOG_INFO("Initialized");
|
||||
} else {
|
||||
LOG_ERR("Initialization failed");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Si5351C::ConfigureCLKIn(uint32_t clkin_freq) {
|
||||
bool success = true;
|
||||
|
||||
uint8_t clkinDiv = 0;
|
||||
while (clkin_freq / (1 << clkinDiv) > 40000000) {
|
||||
@ -26,19 +46,6 @@ bool Si5351C::Init(uint32_t clkin_freq) {
|
||||
value |= clkinDiv << 6;
|
||||
success &= WriteRegister(Reg::PLLInputSource, value);
|
||||
|
||||
// Disable OEB pin functionality
|
||||
success &= WriteRegister(Reg::OEBPinMask, 0xFF);
|
||||
// Disable all outputs
|
||||
success &= WriteRegister(Reg::OutputEnableControl, 0xFF);
|
||||
|
||||
// Enable fanouts
|
||||
success &= WriteRegister(Reg::FanoutEnable, 0xD0);
|
||||
|
||||
if(success) {
|
||||
LOG_INFO("Initialized");
|
||||
} else {
|
||||
LOG_ERR("Initialization failed");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -278,6 +285,17 @@ bool Si5351C::WriteRegisterRange(Reg start, const uint8_t *data, uint8_t len) {
|
||||
I2C_MEMADD_SIZE_8BIT, (uint8_t*) data, len, 100) == HAL_OK;
|
||||
}
|
||||
|
||||
bool Si5351C::ExtCLKAvailable() {
|
||||
uint8_t value;
|
||||
ReadRegister(Reg::DeviceStatus, &value);
|
||||
LOG_DEBUG("Device status: 0x%02x", value);
|
||||
if (value & 0x10) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Si5351C::ReadRegisterRange(Reg start, uint8_t *data, uint8_t len) {
|
||||
return HAL_I2C_Mem_Read(i2c, address, (int) start,
|
||||
I2C_MEMADD_SIZE_8BIT, data, len, 100) == HAL_OK;
|
||||
@ -338,3 +356,5 @@ bool Si5351C::ReadRawCLKConfig(uint8_t clknum, uint8_t *config) {
|
||||
auto reg = (Reg) ((int) Reg::MS0_CONFIG + 8 * clknum);
|
||||
return ReadRegisterRange(reg, config, 8);
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
FreqCLKINDiv(0) {
|
||||
};
|
||||
bool Init(uint32_t clkin_freq = 0);
|
||||
bool ConfigureCLKIn(uint32_t clkin_freq);
|
||||
bool SetPLL(PLL pll, uint32_t frequency, PLLSource src);
|
||||
bool SetCLK(uint8_t clknum, uint32_t frequency, PLL source, DriveStrength strength = DriveStrength::mA2, uint32_t PLLFreqOverride = 0);
|
||||
bool SetCLKtoXTAL(uint8_t clknum);
|
||||
@ -38,6 +39,7 @@ public:
|
||||
bool Disable(uint8_t clknum);
|
||||
bool Locked(PLL pll);
|
||||
bool ResetPLL(PLL pll);
|
||||
bool ExtCLKAvailable();
|
||||
|
||||
// Direct register access of clk configuration registers
|
||||
// config has to point to a buffer containing at least 8 bytes
|
||||
|
@ -36,6 +36,9 @@ static uint16_t IFTableIndexCnt = 0;
|
||||
|
||||
static constexpr uint32_t BandSwitchFrequency = 25000000;
|
||||
|
||||
static uint32_t extOutFreq = 0;
|
||||
static bool extRefInUse = false;
|
||||
|
||||
using namespace VNAHAL;
|
||||
|
||||
static void HaltedCallback() {
|
||||
@ -129,6 +132,10 @@ bool VNA::Init() {
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::XTAL);
|
||||
while(!Si5351.Locked(Si5351C::PLL::B));
|
||||
|
||||
extRefInUse = 0;
|
||||
extOutFreq = 0;
|
||||
Si5351.Disable(SiChannel::ReferenceOut);
|
||||
|
||||
// Both MAX2871 get a 100MHz reference
|
||||
Si5351.SetCLK(SiChannel::Source, 100000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Source);
|
||||
@ -137,10 +144,6 @@ bool VNA::Init() {
|
||||
// 16MHz FPGA clock
|
||||
Si5351.SetCLK(SiChannel::FPGA, 16000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::FPGA);
|
||||
// TODO reference settings controllable through USB
|
||||
// // 10 MHz external reference clock
|
||||
// Si5351.SetCLK(6, 10000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA8);
|
||||
// Si5351.Enable(6);
|
||||
|
||||
// Generate second LO with Si5351
|
||||
Si5351.SetCLK(SiChannel::Port1LO2, IF1 - IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
@ -410,3 +413,80 @@ bool VNA::GetTemps(uint8_t *source, uint8_t *lo) {
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VNA::fillDeviceInfo(Protocol::DeviceInfo *info) {
|
||||
// read PLL temperatures
|
||||
uint8_t tempSource, tempLO;
|
||||
VNA::GetTemps(&tempSource, &tempLO);
|
||||
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
|
||||
// Read ADC min/max
|
||||
auto limits = FPGA::GetADCLimits();
|
||||
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
|
||||
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
|
||||
limits.Rmin, limits.Rmax);
|
||||
#define ADC_LIMIT 30000
|
||||
// Set VNA related member of info struct
|
||||
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|
||||
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|
||||
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
|
||||
info->ADC_overload = true;
|
||||
} else {
|
||||
info->ADC_overload = false;
|
||||
}
|
||||
auto status = FPGA::GetStatus();
|
||||
info->LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
|
||||
info->source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
|
||||
info->extRefAvailable = Ref::available();
|
||||
info->extRefInUse = extRefInUse;
|
||||
info->temperatures.LO1 = tempLO;
|
||||
info->temperatures.source = tempSource;
|
||||
info->temperatures.MCU = 0;
|
||||
FPGA::ResetADCLimits();
|
||||
}
|
||||
|
||||
bool VNA::Ref::available() {
|
||||
return Si5351.ExtCLKAvailable();
|
||||
}
|
||||
|
||||
bool VNA::Ref::applySettings(Protocol::ReferenceSettings s) {
|
||||
if(extOutFreq != s.ExtRefOuputFreq) {
|
||||
extOutFreq = s.ExtRefOuputFreq;
|
||||
if(extOutFreq == 0) {
|
||||
Si5351.Disable(SiChannel::ReferenceOut);
|
||||
LOG_INFO("External reference output disabled");
|
||||
} else {
|
||||
Si5351.SetCLK(SiChannel::ReferenceOut, extOutFreq, Si5351C::PLL::A);
|
||||
Si5351.Enable(SiChannel::ReferenceOut);
|
||||
LOG_INFO("External reference output set to %luHz", extOutFreq);
|
||||
}
|
||||
}
|
||||
bool useExternal = s.UseExternalRef || (s.AutomaticSwitch && Ref::available());
|
||||
if(useExternal != extRefInUse) {
|
||||
// switch between internal and external reference
|
||||
extRefInUse = useExternal;
|
||||
if(extRefInUse) {
|
||||
if(!Ref::available()) {
|
||||
LOG_WARN("Forced switch to external reference but no signal detected");
|
||||
}
|
||||
Si5351.ConfigureCLKIn(10000000);
|
||||
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::CLKIN);
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::CLKIN);
|
||||
LOG_INFO("Switched to external reference");
|
||||
FPGA::Enable(FPGA::Periphery::ExtRefLED);
|
||||
} else {
|
||||
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::XTAL);
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::XTAL);
|
||||
LOG_INFO("Switched to internal reference");
|
||||
FPGA::Disable(FPGA::Periphery::ExtRefLED);
|
||||
}
|
||||
}
|
||||
constexpr uint32_t lock_timeout = 10;
|
||||
uint32_t start = HAL_GetTick();
|
||||
while(!Si5351.Locked(Si5351C::PLL::A) || !Si5351.Locked(Si5351C::PLL::A)) {
|
||||
if(HAL_GetTick() - start > lock_timeout) {
|
||||
LOG_ERR("Clock distributor PLLs failed to lock");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -12,7 +12,14 @@ using StatusCallback = void(*)(FPGA::SamplingResult);
|
||||
bool Init();
|
||||
bool ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb);
|
||||
bool ConfigureManual(Protocol::ManualControl m, StatusCallback cb);
|
||||
|
||||
// Only call the following function when the sweep is inactive
|
||||
bool GetTemps(uint8_t *source, uint8_t *lo);
|
||||
void fillDeviceInfo(Protocol::DeviceInfo *info);
|
||||
namespace Ref {
|
||||
bool available();
|
||||
bool applySettings(Protocol::ReferenceSettings s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user