mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-07 07:23:43 +00:00
Working dwell time feature
- Bugfixes: - improve SPI timing in FPGA - fix markers and reduce CPU load when using markers with fast traces - New features: - dwell time configurable in acquisition toolbar - PLL settling delay in device configuration - device configuration persistent across power cycles
This commit is contained in:
parent
24314e2361
commit
a4faeb28b0
35 changed files with 516 additions and 289 deletions
|
|
@ -273,7 +273,7 @@ inline void App_Process() {
|
|||
}
|
||||
break;
|
||||
case Protocol::PacketType::DeviceConfiguration:
|
||||
HW::setAcquisitionFrequencies(recv_packet.deviceConfig);
|
||||
HW::setDeviceConfig(recv_packet.deviceConfig);
|
||||
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
|
||||
break;
|
||||
case Protocol::PacketType::SetTrigger:
|
||||
|
|
|
|||
|
|
@ -216,7 +216,6 @@ using DeviceInfo = struct _deviceInfo {
|
|||
uint8_t limits_maxAmplitudePoints;
|
||||
uint64_t limits_maxFreqHarmonic;
|
||||
uint8_t num_ports;
|
||||
uint16_t limits_minDwellTime;
|
||||
uint16_t limits_maxDwellTime;
|
||||
};
|
||||
|
||||
|
|
@ -440,6 +439,7 @@ using DeviceConfig = struct _deviceconfig {
|
|||
uint32_t IF1;
|
||||
uint8_t ADCprescaler;
|
||||
uint16_t DFTphaseInc;
|
||||
uint8_t PLLSettlingDelay;
|
||||
} V1;
|
||||
struct {
|
||||
uint32_t ip;
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ void FPGA::SetMode(Mode mode) {
|
|||
Low(AUX2);
|
||||
Delay::us(1);
|
||||
High(CS);
|
||||
// Configure SPI to use faster speed of 32MHz
|
||||
// Configure SPI to use faster speed of 42.5MHz
|
||||
FPGA_SPI.Instance->CR1 = (FPGA_SPI.Instance->CR1 & ~SPI_CR1_BR_Msk) | SPI_BAUDRATEPRESCALER_4;
|
||||
break;
|
||||
case Mode::SourcePLL:
|
||||
|
|
@ -361,15 +361,15 @@ void FPGA::SetMode(Mode mode) {
|
|||
Low(AUX2);
|
||||
Delay::us(1);
|
||||
High(AUX1);
|
||||
// Configure SPI to use slower speed of 16MHz (MAX2871 is limited to 20MHz)
|
||||
FPGA_SPI.Instance->CR1 = (FPGA_SPI.Instance->CR1 & ~SPI_CR1_BR_Msk) | SPI_BAUDRATEPRESCALER_8;
|
||||
// Configure SPI to use slower speed of 10.625MHz (MAX2871 is limited to 20MHz)
|
||||
FPGA_SPI.Instance->CR1 = (FPGA_SPI.Instance->CR1 & ~SPI_CR1_BR_Msk) | SPI_BAUDRATEPRESCALER_16;
|
||||
break;
|
||||
case Mode::LOPLL:
|
||||
Low(CS);
|
||||
Low(AUX1);
|
||||
Delay::us(1);
|
||||
High(AUX2);
|
||||
// Configure SPI to use slower speed of 16MHz (MAX2871 is limited to 20MHz)
|
||||
// Configure SPI to use slower speed of 10.625MHz (MAX2871 is limited to 20MHz)
|
||||
FPGA_SPI.Instance->CR1 = (FPGA_SPI.Instance->CR1 & ~SPI_CR1_BR_Msk) | SPI_BAUDRATEPRESCALER_8;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
#include "stm.hpp"
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
#define LOG_MODULE "STM"
|
||||
#include "Log.h"
|
||||
|
||||
using Callback = void(*)(void);
|
||||
static constexpr uint8_t numCallbacks = 10;
|
||||
static Callback callbacks[numCallbacks];
|
||||
|
|
@ -34,6 +38,7 @@ bool STM::DispatchToInterrupt(void (*cb)(void)) {
|
|||
return true;
|
||||
} else {
|
||||
// already at limit
|
||||
LOG_ERR("Interrupt dispatch queue full");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,17 @@ static bool StatusUpdateFlag = true;
|
|||
static Protocol::ReferenceSettings ref;
|
||||
static volatile uint64_t lastISR;
|
||||
|
||||
static uint32_t IF1 = HW::DefaultIF1;
|
||||
static uint32_t IF2 = HW::DefaultIF2;
|
||||
static uint32_t ADCsamplerate = HW::DefaultADCSamplerate;
|
||||
static uint8_t ADCprescaler = HW::DefaultADCprescaler;
|
||||
static uint16_t DFTphaseInc = HW::DefaultDFTphaseInc;
|
||||
// increment when device config struct format changed. If a wrong version is found in flash, it will revert to default values
|
||||
static constexpr uint16_t deviceConfigVersion = 0x001;
|
||||
|
||||
using DeviceConfig = struct _devicConfig {
|
||||
uint16_t version;
|
||||
Protocol::DeviceConfig config;
|
||||
uint32_t IF2;
|
||||
uint32_t ADCsamplerate;
|
||||
};
|
||||
|
||||
static DeviceConfig deviceConfig;
|
||||
|
||||
using namespace HWHAL;
|
||||
|
||||
|
|
@ -100,6 +106,8 @@ bool HW::Init() {
|
|||
|
||||
activeMode = Mode::Idle;
|
||||
|
||||
LoadDeviceConfig();
|
||||
|
||||
Si5351.Init();
|
||||
|
||||
// Use Si5351 to generate reference frequencies for other PLLs and ADC
|
||||
|
|
@ -152,12 +160,12 @@ bool HW::Init() {
|
|||
FPGA::DisableHardwareOverwrite();
|
||||
|
||||
// Set default ADC samplerate
|
||||
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, ADCprescaler);
|
||||
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, deviceConfig.config.V1.ADCprescaler);
|
||||
// Set phase increment according to
|
||||
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, DFTphaseInc);
|
||||
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, deviceConfig.config.V1.DFTphaseInc);
|
||||
|
||||
// Set default settling time
|
||||
FPGA::SetSettlingTime(HW::DefaultDwellTime);
|
||||
FPGA::SetSettlingTime(deviceConfig.config.V1.PLLSettlingDelay);
|
||||
|
||||
Exti::SetCallback(FPGA_INTR_GPIO_Port, FPGA_INTR_Pin, Exti::EdgeType::Rising, Exti::Pull::Down, FPGA_Interrupt);
|
||||
|
||||
|
|
@ -190,7 +198,7 @@ bool HW::Init() {
|
|||
} else {
|
||||
LOG_INFO("LO1 VCO map complete");
|
||||
}
|
||||
LO1.SetFrequency(1000000000 + IF1);
|
||||
LO1.SetFrequency(1000000000 + deviceConfig.config.V1.IF1);
|
||||
LO1.UpdateFrequency();
|
||||
LOG_DEBUG("LO temp: %u", LO1.GetTemp());
|
||||
|
||||
|
|
@ -428,37 +436,39 @@ Si5351C::PLLSource HW::Ref::getSource() {
|
|||
}
|
||||
}
|
||||
|
||||
void HW::setAcquisitionFrequencies(Protocol::DeviceConfig s) {
|
||||
IF1 = s.V1.IF1;
|
||||
ADCprescaler = s.V1.ADCprescaler;
|
||||
DFTphaseInc = s.V1.DFTphaseInc;
|
||||
float ADCrate = (float) FPGA::Clockrate / ADCprescaler;
|
||||
IF2 = ADCrate * DFTphaseInc / 4096;
|
||||
ADCsamplerate = ADCrate;
|
||||
static void updateOtherParametersFromProtocolDeviceConfig() {
|
||||
float ADCrate = (float) FPGA::Clockrate / deviceConfig.config.V1.ADCprescaler;
|
||||
deviceConfig.IF2 = ADCrate * deviceConfig.config.V1.DFTphaseInc / 4096;
|
||||
deviceConfig.ADCsamplerate = ADCrate;
|
||||
}
|
||||
|
||||
void HW::setDeviceConfig(Protocol::DeviceConfig s) {
|
||||
deviceConfig.config = s;
|
||||
if(deviceConfig.config.V1.PLLSettlingDelay < HW::MinPLLSettlingDelay) {
|
||||
deviceConfig.config.V1.PLLSettlingDelay = HW::MinPLLSettlingDelay;
|
||||
}
|
||||
updateOtherParametersFromProtocolDeviceConfig();
|
||||
SaveDeviceConfig();
|
||||
}
|
||||
|
||||
Protocol::DeviceConfig HW::getDeviceConfig() {
|
||||
Protocol::DeviceConfig s;
|
||||
s.V1.ADCprescaler = ADCprescaler;
|
||||
s.V1.DFTphaseInc = DFTphaseInc;
|
||||
s.V1.IF1 = IF1;
|
||||
return s;
|
||||
return deviceConfig.config;
|
||||
}
|
||||
|
||||
uint32_t HW::getIF1() {
|
||||
return IF1;
|
||||
return deviceConfig.config.V1.IF1;
|
||||
}
|
||||
|
||||
uint32_t HW::getIF2() {
|
||||
return IF2;
|
||||
return deviceConfig.IF2;
|
||||
}
|
||||
|
||||
uint32_t HW::getADCRate() {
|
||||
return ADCsamplerate;
|
||||
return deviceConfig.ADCsamplerate;
|
||||
}
|
||||
|
||||
uint8_t HW::getADCPrescaler() {
|
||||
return ADCprescaler;
|
||||
return deviceConfig.config.V1.ADCprescaler;
|
||||
}
|
||||
|
||||
uint64_t HW::getLastISRTimestamp() {
|
||||
|
|
@ -496,6 +506,44 @@ void HW::updateDeviceStatus() {
|
|||
}
|
||||
|
||||
uint16_t HW::getDFTPhaseInc() {
|
||||
return DFTphaseInc;
|
||||
return deviceConfig.config.V1.DFTphaseInc;
|
||||
}
|
||||
|
||||
uint8_t HW::getPLLSettlingDelay() {
|
||||
return deviceConfig.config.V1.PLLSettlingDelay;
|
||||
}
|
||||
|
||||
bool HW::LoadDeviceConfig() {
|
||||
HWHAL::flash.read(flash_address, sizeof(deviceConfig), &deviceConfig);
|
||||
if(deviceConfig.version != deviceConfigVersion) {
|
||||
LOG_WARN("Invalid version in flash, expected %u, got %u", deviceConfigVersion, deviceConfig.version);
|
||||
SetDefaultDeviceConfig();
|
||||
return false;
|
||||
} else {
|
||||
LOG_INFO("Device config loaded from flash");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool HW::SaveDeviceConfig() {
|
||||
if(!HWHAL::flash.eraseRange(flash_address, flash_size)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t write_size = sizeof(deviceConfig);
|
||||
if(write_size % Flash::PageSize != 0) {
|
||||
// round up to next page
|
||||
write_size += Flash::PageSize - write_size % Flash::PageSize;
|
||||
}
|
||||
return HWHAL::flash.write(flash_address, write_size, &deviceConfig);
|
||||
}
|
||||
|
||||
void HW::SetDefaultDeviceConfig() {
|
||||
memset(&deviceConfig, 0, sizeof(deviceConfig));
|
||||
deviceConfig.version = deviceConfigVersion;
|
||||
deviceConfig.config.V1.IF1 = HW::DefaultIF1;
|
||||
deviceConfig.config.V1.ADCprescaler = HW::DefaultADCprescaler;
|
||||
deviceConfig.config.V1.DFTphaseInc = HW::DefaultDFTphaseInc;
|
||||
deviceConfig.config.V1.PLLSettlingDelay = HW::DefaultPLLSettlingDelay;
|
||||
updateOtherParametersFromProtocolDeviceConfig();
|
||||
LOG_INFO("Device config set to default");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ static constexpr uint32_t BandSwitchFrequency = 25000000;
|
|||
static constexpr uint32_t DefaultLO2 = DefaultIF1 - DefaultIF2;
|
||||
static constexpr uint8_t LO2Multiplier = 13;
|
||||
static constexpr uint32_t SI5351CPLLAlignedFrequency = DefaultLO2 * LO2Multiplier;
|
||||
static constexpr uint16_t DefaultDwellTime = 60;
|
||||
static constexpr uint16_t DefaultPLLSettlingDelay = 60;
|
||||
static constexpr uint16_t MinPLLSettlingDelay = 10;
|
||||
|
||||
static constexpr uint8_t DefaultADCprescaler = FPGA::Clockrate / DefaultADCSamplerate;
|
||||
static_assert(DefaultADCprescaler * DefaultADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly");
|
||||
|
|
@ -87,7 +88,6 @@ static constexpr Protocol::DeviceInfo Info = {
|
|||
.limits_maxAmplitudePoints = Cal::maxPoints,
|
||||
.limits_maxFreqHarmonic = 18000000000,
|
||||
.num_ports = 2,
|
||||
.limits_minDwellTime = 0,
|
||||
.limits_maxDwellTime = 10239,
|
||||
};
|
||||
|
||||
|
|
@ -135,13 +135,21 @@ namespace Ref {
|
|||
Si5351C::PLLSource getSource();
|
||||
}
|
||||
|
||||
// Acquisition frequency settings
|
||||
void setAcquisitionFrequencies(Protocol::DeviceConfig s);
|
||||
// Device configuration settings
|
||||
constexpr uint32_t flash_address = Firmware::maxSize + Cal::flash_size; // stored directly behind calibration in flash
|
||||
constexpr uint32_t flash_size = 4096; // reserve one sector for now
|
||||
|
||||
bool LoadDeviceConfig();
|
||||
bool SaveDeviceConfig();
|
||||
void SetDefaultDeviceConfig();
|
||||
|
||||
void setDeviceConfig(Protocol::DeviceConfig s);
|
||||
Protocol::DeviceConfig getDeviceConfig();
|
||||
uint32_t getIF1();
|
||||
uint32_t getIF2();
|
||||
uint32_t getADCRate();
|
||||
uint8_t getADCPrescaler();
|
||||
uint16_t getDFTPhaseInc();
|
||||
uint8_t getPLLSettlingDelay();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ static_assert(alternativePhaseInc * alternativeSamplerate == 4096 * HW::DefaultI
|
|||
|
||||
// Constants for USB buffer overflow prevention
|
||||
static constexpr uint16_t maxPointsBetweenHalts = 40;
|
||||
static constexpr uint32_t reservedUSBbuffer = maxPointsBetweenHalts * (sizeof(Protocol::Datapoint) + 8 /*USB packet overhead*/);
|
||||
static constexpr uint16_t full2portDatapointSize = 66;// See Protocol::VNADatapoint::
|
||||
static constexpr uint32_t reservedUSBbuffer = (maxPointsBetweenHalts + 2 /*additional buffer*/) * (full2portDatapointSize + 8 /*USB packet overhead*/);
|
||||
|
||||
using namespace HWHAL;
|
||||
|
||||
|
|
@ -118,6 +119,9 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
|||
if(settings.points > FPGA::MaxPoints) {
|
||||
settings.points = FPGA::MaxPoints;
|
||||
}
|
||||
if(settings.dwell_time > HW::Info.limits_maxDwellTime) {
|
||||
settings.dwell_time = HW::Info.limits_maxDwellTime;
|
||||
}
|
||||
settings = s;
|
||||
// calculate factor between adjacent points for log sweep for faster calculation when sweeping
|
||||
logMultiplier = pow((double) settings.f_stop / settings.f_start, 1.0 / (settings.points-1));
|
||||
|
|
@ -132,7 +136,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
|||
// has to be one less than actual number of samples
|
||||
FPGA::SetSamplesPerPoint(samplesPerPoint);
|
||||
|
||||
FPGA::SetSettlingTime(s.dwell_time);
|
||||
FPGA::SetSettlingTime(s.dwell_time + HW::getPLLSettlingDelay());
|
||||
|
||||
// reset unlevel flag if it was set from a previous sweep/mode
|
||||
HW::SetOutputUnlevel(false);
|
||||
|
|
@ -216,6 +220,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
|||
pointsWithoutHalt++;
|
||||
if(pointsWithoutHalt > maxPointsBetweenHalts) {
|
||||
needs_halt = true;
|
||||
pointsWithoutHalt = 0;
|
||||
}
|
||||
} else {
|
||||
pointsWithoutHalt = 0;
|
||||
|
|
@ -304,7 +309,9 @@ static void PassOnData() {
|
|||
Protocol::PacketInfo info;
|
||||
info.type = Protocol::PacketType::VNADatapoint;
|
||||
info.VNAdatapoint = &data;
|
||||
Communication::Send(info);
|
||||
if(!Communication::Send(info)) {
|
||||
LOG_ERR("Failed to transmit VNADatapoint");
|
||||
}
|
||||
data.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue