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:
Jan Käberich 2025-01-03 14:36:10 +01:00
parent 24314e2361
commit a4faeb28b0
35 changed files with 516 additions and 289 deletions

View file

@ -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:

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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");
}

View file

@ -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();
}

View file

@ -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();
}