From 875f3b017044a32cc7bc409c3841343bf6060654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Mon, 16 Nov 2020 20:05:29 +0100 Subject: [PATCH] Partial source calibration dialog --- Software/PC_Application/Application.pro | 5 + .../Calibration/amplitudecaldialog.cpp | 187 ++++++++++++++++++ .../Calibration/amplitudecaldialog.h | 77 ++++++++ .../Calibration/amplitudecaldialog.ui | 107 ++++++++++ .../Calibration/sourcecaldialog.cpp | 8 + .../Calibration/sourcecaldialog.h | 17 ++ Software/PC_Application/Device/device.cpp | 4 + Software/PC_Application/Device/device.h | 3 +- .../Device/firmwareupdatedialog.cpp | 2 +- .../Device/manualcontroldialog.cpp | 1 - .../SpectrumAnalyzer/spectrumanalyzer.cpp | 2 - Software/PC_Application/VNA/vna.cpp | 2 - Software/PC_Application/appwindow.cpp | 13 ++ Software/PC_Application/appwindow.h | 1 + Software/PC_Application/main.ui | 10 + .../VNA_embedded/Application/AmplitudeCal.cpp | 136 +++++++++++++ .../VNA_embedded/Application/AmplitudeCal.hpp | 30 +++ Software/VNA_embedded/Application/App.cpp | 32 ++- .../Application/Communication/Protocol.cpp | 33 ++++ .../Application/Communication/Protocol.hpp | 13 ++ .../Application/Drivers/FPGA/FPGA.cpp | 5 +- .../Application/Drivers/FPGA/FPGA.hpp | 2 +- .../Application/Drivers/Flash.cpp | 93 ++++++++- .../Application/Drivers/Flash.hpp | 9 +- .../VNA_embedded/Application/Firmware.cpp | 15 +- .../VNA_embedded/Application/Firmware.hpp | 6 +- Software/VNA_embedded/Application/HW_HAL.cpp | 2 + Software/VNA_embedded/Application/HW_HAL.hpp | 2 + 28 files changed, 784 insertions(+), 33 deletions(-) create mode 100644 Software/PC_Application/Calibration/amplitudecaldialog.cpp create mode 100644 Software/PC_Application/Calibration/amplitudecaldialog.h create mode 100644 Software/PC_Application/Calibration/amplitudecaldialog.ui create mode 100644 Software/PC_Application/Calibration/sourcecaldialog.cpp create mode 100644 Software/PC_Application/Calibration/sourcecaldialog.h create mode 100644 Software/VNA_embedded/Application/AmplitudeCal.cpp create mode 100644 Software/VNA_embedded/Application/AmplitudeCal.hpp diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index abe26bc..763208d 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -1,11 +1,13 @@ HEADERS += \ ../VNA_embedded/Application/Communication/Protocol.hpp \ + Calibration/amplitudecaldialog.h \ Calibration/calibration.h \ Calibration/calibrationtracedialog.h \ Calibration/calkit.h \ Calibration/calkitdialog.h \ Calibration/json.hpp \ Calibration/measurementmodel.h \ + Calibration/sourcecaldialog.h \ CustomWidgets/colorpickerbutton.h \ CustomWidgets/informationbox.h \ CustomWidgets/siunitedit.h \ @@ -48,11 +50,13 @@ HEADERS += \ SOURCES += \ ../VNA_embedded/Application/Communication/Protocol.cpp \ + Calibration/amplitudecaldialog.cpp \ Calibration/calibration.cpp \ Calibration/calibrationtracedialog.cpp \ Calibration/calkit.cpp \ Calibration/calkitdialog.cpp \ Calibration/measurementmodel.cpp \ + Calibration/sourcecaldialog.cpp \ CustomWidgets/colorpickerbutton.cpp \ CustomWidgets/informationbox.cpp \ CustomWidgets/qwtplotpiecewisecurve.cpp \ @@ -102,6 +106,7 @@ win32:LIBS += -LC:\Qwt-6.1.4\lib -lqwt QT += widgets FORMS += \ + Calibration/amplitudecaldialog.ui \ Calibration/calibrationtracedialog.ui \ Calibration/calkitdialog.ui \ CustomWidgets/tilewidget.ui \ diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.cpp b/Software/PC_Application/Calibration/amplitudecaldialog.cpp new file mode 100644 index 0000000..3c0119e --- /dev/null +++ b/Software/PC_Application/Calibration/amplitudecaldialog.cpp @@ -0,0 +1,187 @@ +#include "amplitudecaldialog.h" +#include "ui_amplitudecaldialog.h" +#include "mode.h" +#include "unit.h" +#include + +AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) : + QDialog(parent), + ui(new Ui::AmplitudeCalDialog), + dev(dev), + model(this) +{ + activeMode = Mode::getActiveMode(); + activeMode->deactivate(); + dev->SetIdle(); + ui->setupUi(this); + ui->view->setModel(&model); + ui->view->setColumnWidth(AmplitudeModel::ColIndexFreq, 100); + ui->view->setColumnWidth(AmplitudeModel::ColIndexCorrectionFactors, 150); + ui->view->setColumnWidth(AmplitudeModel::ColIndexPort1, 150); + ui->view->setColumnWidth(AmplitudeModel::ColIndexPort2, 150); + connect(dev, &Device::AmplitudeCorrectionPointReceived, this, &AmplitudeCalDialog::ReceivedPoint); + connect(ui->load, &QPushButton::clicked, this, &AmplitudeCalDialog::LoadFromDevice); + connect(ui->add, &QPushButton::clicked, this, &AmplitudeCalDialog::AddPoint); + connect(ui->remove, &QPushButton::clicked, this, &AmplitudeCalDialog::RemovePoint); +} + +AmplitudeCalDialog::~AmplitudeCalDialog() +{ + delete ui; + activeMode->activate(); +} + +void AmplitudeCalDialog::reject() +{ + // TODO check for unsaved data + delete this; +} + +std::vector AmplitudeCalDialog::getPoints() const +{ + return points; +} + +void AmplitudeCalDialog::setAmplitude(double amplitude, unsigned int point, bool port2) +{ + if(point >= points.size()) { + return; + } + if(port2) { + points[point].amplitudePort2 = amplitude; + } else { + points[point].amplitudePort1 = amplitude; + } +} + +void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p) +{ + qDebug() << "Received a point"; + CorrectionPoint c; + c.frequency = p.freq * 10.0; + c.correctionPort1 = p.port1; + c.correctionPort2 = p.port2; + model.beginInsertRows(QModelIndex(), points.size(), points.size()); + points.push_back(c); + model.endInsertRows(); + emit pointsUpdated(); +} + +void AmplitudeCalDialog::LoadFromDevice() +{ + model.beginResetModel(); + points.clear(); + model.endResetModel(); + qDebug() << "Asking for amplitude calibration"; + dev->SendCommandWithoutPayload(requestCommand()); +} + +void AmplitudeCalDialog::SaveToDevice() +{ + for(unsigned int i=0;iSendPacket(info); + } +} + +void AmplitudeCalDialog::RemovePoint() +{ + unsigned int row = ui->view->currentIndex().row(); + if(row < points.size()) { + model.beginRemoveRows(QModelIndex(), row, row); + points.erase(points.begin() + row); + model.endInsertRows(); + } +} + +void AmplitudeCalDialog::AddPoint() +{ + +} + +AmplitudeModel::AmplitudeModel(AmplitudeCalDialog *c) : + QAbstractTableModel(), + c(c) +{ +} + +int AmplitudeModel::rowCount(const QModelIndex &parent) const +{ + return c->getPoints().size(); +} + +int AmplitudeModel::columnCount(const QModelIndex &parent) const +{ + return ColIndexLast; +} + +QVariant AmplitudeModel::data(const QModelIndex &index, int role) const +{ + if(role == Qt::DisplayRole && index.row() < c->getPoints().size()) { + auto p = c->getPoints()[index.row()]; + switch(index.column()) { + case ColIndexFreq: + return Unit::ToString(p.frequency, "Hz", " kMG", 6); + break; + case ColIndexCorrectionFactors: + return QString::number(p.correctionPort1) + ", " + QString::number(p.correctionPort2); + break; + case ColIndexPort1: + return Unit::ToString(p.amplitudePort1, "dbm", " ", 4); + break; + case ColIndexPort2: + return Unit::ToString(p.amplitudePort2, "dbm", " ", 4); + break; + } + } + return QVariant(); +} + +bool AmplitudeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if((unsigned int) index.row() >= c->getPoints().size()) { + return false; + } + switch(index.column()) { + case ColIndexPort1: + c->setAmplitude(value.toDouble(), index.row(), false); + return true; + case ColIndexPort2: + c->setAmplitude(value.toDouble(), index.row(), true); + return true; + } + + return false; +} + +QVariant AmplitudeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch(section) { + case ColIndexFreq: return "Frequency"; break; + case ColIndexCorrectionFactors: return "Correction Factors"; break; + case ColIndexPort1: return "Amplitude Port 1"; break; + case ColIndexPort2: return "Amplitude Port 2"; break; + default: return QVariant(); break; + } + } else { + return QVariant(); + } +} + +Qt::ItemFlags AmplitudeModel::flags(const QModelIndex &index) const +{ + int flags = Qt::NoItemFlags; + switch(index.column()) { + case ColIndexPort1: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break; + case ColIndexPort2: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break; + } + return (Qt::ItemFlags) flags; +} diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.h b/Software/PC_Application/Calibration/amplitudecaldialog.h new file mode 100644 index 0000000..9736fa3 --- /dev/null +++ b/Software/PC_Application/Calibration/amplitudecaldialog.h @@ -0,0 +1,77 @@ +#ifndef AMPLITUDECALDIALOG_H +#define AMPLITUDECALDIALOG_H + +#include +#include "mode.h" +#include "Device/device.h" + +namespace Ui { +class AmplitudeCalDialog; +} + +class AmplitudeCalDialog; + +class AmplitudeModel : public QAbstractTableModel +{ + friend AmplitudeCalDialog; + Q_OBJECT +public: + AmplitudeModel(AmplitudeCalDialog *c); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +private: + enum { + ColIndexFreq, + ColIndexCorrectionFactors, + ColIndexPort1, + ColIndexPort2, + ColIndexLast + }; + AmplitudeCalDialog *c; +}; + +class AmplitudeCalDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AmplitudeCalDialog(Device *dev, QWidget *parent = nullptr); + ~AmplitudeCalDialog(); + void reject() override; + + class CorrectionPoint { + public: + double frequency; + double correctionPort1; + double correctionPort2; + double amplitudePort1; + double amplitudePort2; + }; + std::vector getPoints() const; + void setAmplitude(double amplitude, unsigned int point, bool port2); + +protected slots: + void ReceivedPoint(Protocol::AmplitudeCorrectionPoint p); + void LoadFromDevice(); + void SaveToDevice(); + void RemovePoint(); + void AddPoint(); +signals: + void pointsUpdated(); +protected: + virtual Protocol::PacketType requestCommand() = 0; + virtual Protocol::PacketType pointType() = 0; + std::vector points; + Ui::AmplitudeCalDialog *ui; + Device *dev; + Mode *activeMode; + AmplitudeModel model; +}; + +#endif // SOURCECALDIALOG_H diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.ui b/Software/PC_Application/Calibration/amplitudecaldialog.ui new file mode 100644 index 0000000..53a1e4b --- /dev/null +++ b/Software/PC_Application/Calibration/amplitudecaldialog.ui @@ -0,0 +1,107 @@ + + + AmplitudeCalDialog + + + + 0 + 0 + 766 + 599 + + + + Dialog + + + + + + QAbstractItemView::SelectItems + + + true + + + false + + + + + + + + + Add Point + + + + :/icons/add.png:/icons/add.png + + + + + + + Delete Point + + + + :/icons/remove.png:/icons/remove.png + + + + + + + Help + + + + + + + + + + Load from Device + + + + :/icons/open.png:/icons/open.png + + + + + + + Save to Device + + + + :/icons/save.png:/icons/save.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + diff --git a/Software/PC_Application/Calibration/sourcecaldialog.cpp b/Software/PC_Application/Calibration/sourcecaldialog.cpp new file mode 100644 index 0000000..1247bfd --- /dev/null +++ b/Software/PC_Application/Calibration/sourcecaldialog.cpp @@ -0,0 +1,8 @@ +#include "sourcecaldialog.h" + +SourceCalDialog::SourceCalDialog(Device *dev) + : AmplitudeCalDialog(dev) +{ + setWindowTitle("Source Calibration Dialog"); + LoadFromDevice(); +} diff --git a/Software/PC_Application/Calibration/sourcecaldialog.h b/Software/PC_Application/Calibration/sourcecaldialog.h new file mode 100644 index 0000000..cc6813f --- /dev/null +++ b/Software/PC_Application/Calibration/sourcecaldialog.h @@ -0,0 +1,17 @@ +#ifndef SOURCECALDIALOG_H +#define SOURCECALDIALOG_H + +#include +#include "amplitudecaldialog.h" + +class SourceCalDialog : public AmplitudeCalDialog +{ + Q_OBJECT +public: + SourceCalDialog(Device *dev); +protected: + Protocol::PacketType requestCommand() override { return Protocol::PacketType::RequestSourceCal; } + Protocol::PacketType pointType() override { return Protocol::PacketType::SourceCalPoint; } +}; + +#endif // SOURCECALDIALOG_H diff --git a/Software/PC_Application/Device/device.cpp b/Software/PC_Application/Device/device.cpp index 86693ea..5e63852 100644 --- a/Software/PC_Application/Device/device.cpp +++ b/Software/PC_Application/Device/device.cpp @@ -434,6 +434,10 @@ void Device::ReceivedData() case Protocol::PacketType::SpectrumAnalyzerResult: emit SpectrumResultReceived(packet.spectrumResult); break; + case Protocol::PacketType::SourceCalPoint: + case Protocol::PacketType::ReceiverCalPoint: + emit AmplitudeCorrectionPointReceived(packet.amplitudePoint); + break; case Protocol::PacketType::DeviceInfo: if(packet.info.ProtocolVersion != Protocol::Version) { if(!lastInfoValid) { diff --git a/Software/PC_Application/Device/device.h b/Software/PC_Application/Device/device.h index c57e0df..e577f48 100644 --- a/Software/PC_Application/Device/device.h +++ b/Software/PC_Application/Device/device.h @@ -13,8 +13,8 @@ Q_DECLARE_METATYPE(Protocol::Datapoint); Q_DECLARE_METATYPE(Protocol::ManualStatus); -Q_DECLARE_METATYPE(Protocol::DeviceInfo); Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult); +Q_DECLARE_METATYPE(Protocol::AmplitudeCorrectionPoint); class USBInBuffer : public QObject { Q_OBJECT; @@ -74,6 +74,7 @@ signals: void DatapointReceived(Protocol::Datapoint); void ManualStatusReceived(Protocol::ManualStatus); void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult); + void AmplitudeCorrectionPointReceived(Protocol::AmplitudeCorrectionPoint); void DeviceInfoUpdated(); void ConnectionLost(); void AckReceived(); diff --git a/Software/PC_Application/Device/firmwareupdatedialog.cpp b/Software/PC_Application/Device/firmwareupdatedialog.cpp index 867ef07..5d0f23a 100644 --- a/Software/PC_Application/Device/firmwareupdatedialog.cpp +++ b/Software/PC_Application/Device/firmwareupdatedialog.cpp @@ -63,7 +63,7 @@ void FirmwareUpdateDialog::on_bStart_clicked() addStatus("Erasing device memory..."); dev->SendCommandWithoutPayload(Protocol::PacketType::ClearFlash); timer.setSingleShot(true); - timer.start(10000); + timer.start(20000); } void FirmwareUpdateDialog::addStatus(QString line) diff --git a/Software/PC_Application/Device/manualcontroldialog.cpp b/Software/PC_Application/Device/manualcontroldialog.cpp index 9807683..bdfd749 100644 --- a/Software/PC_Application/Device/manualcontroldialog.cpp +++ b/Software/PC_Application/Device/manualcontroldialog.cpp @@ -144,7 +144,6 @@ ManualControlDialog::ManualControlDialog(Device &dev, QWidget *parent) : MakeReadOnly(ui->refmag); MakeReadOnly(ui->refphase); - qRegisterMetaType("Status"); connect(&dev, &Device::ManualStatusReceived, this, &ManualControlDialog::NewStatus); connect(ui->SourceCE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); }); diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 94c80d5..a20ab65 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -192,8 +192,6 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window) window->addDockWidget(Qt::BottomDockWidgetArea, markerDock); docks.insert(markerDock); - qRegisterMetaType("SpectrumResult"); - // Set initial sweep settings auto pref = Preferences::getInstance(); if(pref.Startup.RememberSweepSettings) { diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 979665d..cbc0776 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -360,8 +360,6 @@ VNA::VNA(AppWindow *window) window->addDockWidget(Qt::BottomDockWidgetArea, markerDock); docks.insert(markerDock); - qRegisterMetaType("Datapoint"); - // Set initial sweep settings auto pref = Preferences::getInstance(); if(pref.Acquisition.alwaysExciteBothPorts) { diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index 115eb9b..a007bbb 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -44,6 +44,7 @@ #include "VNA/vna.h" #include "Generator/generator.h" #include "SpectrumAnalyzer/spectrumanalyzer.h" +#include "Calibration/sourcecaldialog.h" using namespace std; @@ -100,6 +101,7 @@ AppWindow::AppWindow(QWidget *parent) connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close); connect(ui->actionManual_Control, &QAction::triggered, this, &AppWindow::StartManualControl); connect(ui->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog); + connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog); connect(ui->actionPreferences, &QAction::triggered, [=](){ Preferences::getInstance().edit(); // settings might have changed, update necessary stuff @@ -129,6 +131,9 @@ AppWindow::AppWindow(QWidget *parent) vna->activate(); qRegisterMetaType("Datapoint"); + qRegisterMetaType("Manual"); + qRegisterMetaType("SpectrumAnalyzerResult"); + qRegisterMetaType("AmplitudeCorrection"); // List available devices if(UpdateDeviceList() && Preferences::getInstance().Startup.ConnectToFirstDevice) { @@ -181,6 +186,7 @@ void AppWindow::ConnectToDevice(QString serial) ui->actionDisconnect->setEnabled(true); ui->actionManual_Control->setEnabled(true); ui->actionFirmware_Update->setEnabled(true); + ui->actionSource_Calibration->setEnabled(true); Mode::getActiveMode()->initializeDevice(); UpdateReference(); @@ -207,6 +213,7 @@ void AppWindow::DisconnectDevice() ui->actionDisconnect->setEnabled(false); ui->actionManual_Control->setEnabled(false); ui->actionFirmware_Update->setEnabled(false); + ui->actionSource_Calibration->setEnabled(false); for(auto a : deviceActionGroup->actions()) { a->setChecked(false); } @@ -349,6 +356,12 @@ void AppWindow::DeviceNeedsUpdate(int reported, int expected) } } +void AppWindow::SourceCalibrationDialog() +{ + auto d = new SourceCalDialog(device); + d->exec(); +} + Device *AppWindow::getDevice() const { return device; diff --git a/Software/PC_Application/appwindow.h b/Software/PC_Application/appwindow.h index 5d9f42c..1d32ee4 100644 --- a/Software/PC_Application/appwindow.h +++ b/Software/PC_Application/appwindow.h @@ -44,6 +44,7 @@ private slots: void UpdateReference(); void StartFirmwareUpdateDialog(); void DeviceNeedsUpdate(int reported, int expected); + void SourceCalibrationDialog(); private: void DeviceConnectionLost(); void CreateToolbars(); diff --git a/Software/PC_Application/main.ui b/Software/PC_Application/main.ui index 8217b38..2f50b04 100644 --- a/Software/PC_Application/main.ui +++ b/Software/PC_Application/main.ui @@ -48,6 +48,8 @@ + + @@ -151,6 +153,14 @@ About + + + false + + + Source Calibration + + diff --git a/Software/VNA_embedded/Application/AmplitudeCal.cpp b/Software/VNA_embedded/Application/AmplitudeCal.cpp new file mode 100644 index 0000000..a181805 --- /dev/null +++ b/Software/VNA_embedded/Application/AmplitudeCal.cpp @@ -0,0 +1,136 @@ +#include "AmplitudeCal.hpp" +#include +#include "Communication.h" +#include "HW_HAL.hpp" + +#define LOG_LEVEL LOG_LEVEL_INFO +#define LOG_MODULE "CAL" +#include "Log.h" + + +// increment when Calibration struct format changed. If a wrong version is found in flash, it will revert to default values +static constexpr uint16_t version = 0x0000; + +using CorrectionTable = struct { + uint8_t usedPoints; + uint32_t freq[AmplitudeCal::maxPoints]; // LSB = 10Hz + int16_t port1Correction[AmplitudeCal::maxPoints]; // LSB = 0.01db + int16_t port2Correction[AmplitudeCal::maxPoints]; // LSB = 0.01db +}; + +using Calibration = struct _calibration { + uint16_t version; + CorrectionTable Source; + CorrectionTable Receiver; +}; + +static Calibration cal; + +static_assert(sizeof(cal) <= AmplitudeCal::flash_size, "Reserved flash size is too small"); + +bool AmplitudeCal::Load() { + HWHAL::flash.read(flash_address, sizeof(cal), &cal); + if(cal.version != version) { + LOG_WARN("Invalid version in flash, expected %u, got %u", version, cal.version); + SetDefault(); + return false; + } else { + LOG_INFO("Loaded from flash"); + return true; + } +} + +bool AmplitudeCal::Save() { + if(!HWHAL::flash.eraseRange(flash_address, flash_size)) { + return false; + } + return HWHAL::flash.write(flash_address, sizeof(cal), &cal); +} + +void AmplitudeCal::SetDefault() { + memset(&cal, 0, sizeof(cal)); + cal.version = version; + cal.Source.usedPoints = 1; + cal.Source.freq[0] = 100000000; + cal.Receiver.usedPoints = 1; + cal.Receiver.freq[0] = 100000000; + LOG_INFO("Set to default"); +} + +static AmplitudeCal::Correction InterpolateCorrection(const CorrectionTable& table, uint64_t freq) { + // adjust LSB to match table + freq /= 10; + AmplitudeCal::Correction ret; + // find first valid index that is higher than the given frequency + uint8_t i = 0; + for (; i < table.usedPoints; i++) { + if (table.freq[i] >= freq) { + break; + } + } + if (i == 0) { + // no previous index, nothing to interpolate + ret.port1 = table.port1Correction[0]; + ret.port2 = table.port2Correction[0]; + } else if (i >= table.usedPoints) { + // went beyond last point, nothing to interpolate + ret.port1 = table.port1Correction[table.usedPoints - 1]; + ret.port2 = table.port2Correction[table.usedPoints - 1]; + } else { + // frequency is between i and i-1, interpolate + float alpha = (freq - table.freq[i - 1]) / (table.freq[i] - table.freq[i - 1]); + ret.port1 = table.port1Correction[i - 1] * (1 - alpha) + table.port1Correction[i] * alpha; + ret.port2 = table.port2Correction[i - 1] * (1 - alpha) + table.port2Correction[i] * alpha; + } + return ret; +} + +AmplitudeCal::Correction AmplitudeCal::SourceCorrection(uint64_t freq) { + return InterpolateCorrection(cal.Source, freq); +} + +AmplitudeCal::Correction AmplitudeCal::ReceiverCorrection(uint64_t freq) { + return InterpolateCorrection(cal.Receiver, freq); +} + +static void SendCorrectionTable(const CorrectionTable& table, Protocol::PacketType type) { + for(uint8_t i=0;i +#include "Flash.hpp" +#include "Firmware.hpp" +#include "Protocol.hpp" + +namespace AmplitudeCal { + +constexpr uint8_t maxPoints = 64; +constexpr uint32_t flash_address = Firmware::maxSize; // stored directly behind firmware in flash +constexpr uint32_t flash_size = 8192; // reserve two sectors for now + +bool Load(); +bool Save(); +void SetDefault(); + +using Correction = struct _correction { + int16_t port1; + int16_t port2; +}; + +Correction SourceCorrection(uint64_t freq); +Correction ReceiverCorrection(uint64_t freq); +void SendSource(); +void SendReceiver(); +void AddSourcePoint(const Protocol::AmplitudeCorrectionPoint& p); +void AddReceiverPoint(const Protocol::AmplitudeCorrectionPoint& p); + +} diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp index a02424d..fa199c4 100644 --- a/Software/VNA_embedded/Application/App.cpp +++ b/Software/VNA_embedded/Application/App.cpp @@ -17,6 +17,8 @@ #include "Manual.hpp" #include "Generator.hpp" #include "SpectrumAnalyzer.hpp" +#include "HW_HAL.hpp" +#include "AmplitudeCal.hpp" #define LOG_LEVEL LOG_LEVEL_INFO #define LOG_MODULE "App" @@ -32,8 +34,6 @@ static TaskHandle_t handle; // has MCU controllable flash chip, firmware update supported #define HAS_FLASH #include "Firmware.hpp" -extern SPI_HandleTypeDef hspi1; -static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin); #endif extern ADC_HandleTypeDef hadc1; @@ -72,17 +72,17 @@ void App_Start() { LOG_INFO("Start"); Exti::Init(); #ifdef HAS_FLASH - if(!flash.isPresent()) { + if(!HWHAL::flash.isPresent()) { LOG_CRIT("Failed to detect onboard FLASH"); LED::Error(1); } - auto fw_info = Firmware::GetFlashContentInfo(&flash); + auto fw_info = Firmware::GetFlashContentInfo(); if(fw_info.valid) { if(fw_info.CPU_need_update) { // Function will not return, the device will reboot with the new firmware instead // Firmware::PerformUpdate(&flash, fw_info); } - if(!FPGA::Configure(&flash, fw_info.FPGA_bitstream_address, fw_info.FPGA_bitstream_size)) { + if(!FPGA::Configure(fw_info.FPGA_bitstream_address, fw_info.FPGA_bitstream_size)) { LOG_CRIT("FPGA configuration failed"); LED::Error(3); } @@ -99,6 +99,8 @@ void App_Start() { EN_6V_GPIO_Port->BSRR = EN_6V_Pin; #endif + AmplitudeCal::Load(); + if (!HW::Init()) { LOG_CRIT("Initialization failed, unable to start"); LED::Error(4); @@ -166,7 +168,7 @@ void App_Start() { HW::SetMode(HW::Mode::Idle); sweepActive = false; LOG_DEBUG("Erasing FLASH in preparation for firmware update..."); - if(flash.eraseChip()) { + if(HWHAL::flash.eraseRange(0, Firmware::maxSize)) { LOG_DEBUG("...FLASH erased") Communication::SendWithoutPayload(Protocol::PacketType::Ack); } else { @@ -176,7 +178,7 @@ void App_Start() { break; case Protocol::PacketType::FirmwarePacket: LOG_INFO("Writing firmware packet at address %u", recv_packet.firmware.address); - if(flash.write(recv_packet.firmware.address, sizeof(recv_packet.firmware.data), recv_packet.firmware.data)) { + if(HWHAL::flash.write(recv_packet.firmware.address, sizeof(recv_packet.firmware.data), recv_packet.firmware.data)) { Communication::SendWithoutPayload(Protocol::PacketType::Ack); } else { LOG_ERR("Failed to write FLASH"); @@ -185,18 +187,30 @@ void App_Start() { break; case Protocol::PacketType::PerformFirmwareUpdate: { LOG_INFO("Firmware update process triggered"); - auto fw_info = Firmware::GetFlashContentInfo(&flash); + auto fw_info = Firmware::GetFlashContentInfo(); if(fw_info.valid) { Communication::SendWithoutPayload(Protocol::PacketType::Ack); // Some delay to allow communication to finish vTaskDelay(100); - Firmware::PerformUpdate(&flash, fw_info); + Firmware::PerformUpdate(fw_info); // should never get here Communication::SendWithoutPayload(Protocol::PacketType::Nack); } } break; #endif + case Protocol::PacketType::RequestSourceCal: + AmplitudeCal::SendSource(); + break; + case Protocol::PacketType::RequestReceiverCal: + AmplitudeCal::SendReceiver(); + break; + case Protocol::PacketType::SourceCalPoint: + AmplitudeCal::AddSourcePoint(recv_packet.amplitudePoint); + break; + case Protocol::PacketType::ReceiverCalPoint: + AmplitudeCal::AddReceiverPoint(recv_packet.amplitudePoint); + break; default: // this packet type is not supported Communication::SendWithoutPayload(Protocol::PacketType::Nack); diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp index 758babd..0fc5bf7 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.cpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp @@ -457,6 +457,27 @@ static int16_t EncodeFirmwarePacket(const Protocol::FirmwarePacket &d, uint8_t * return 4 + Protocol::FirmwareChunkSize; } +static int16_t EncodeAmplitudeCorrectionPoint( + Protocol::AmplitudeCorrectionPoint d, uint8_t *buf, uint16_t bufSize) { + Encoder e(buf, bufSize); + e.add(d.totalPoints); + e.add(d.pointNum); + e.add(d.freq); + e.add(d.port1); + e.add(d.port2); + return e.getSize(); +} +static Protocol::AmplitudeCorrectionPoint DecodeAmplitudeCorrectionPoint(uint8_t *buf) { + Protocol::AmplitudeCorrectionPoint d; + Decoder e(buf); + e.get(d.totalPoints); + e.get(d.pointNum); + e.get(d.freq); + e.get(d.port1); + e.get(d.port2); + return d; +} + uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) { if (!info || !len) { info->type = PacketType::None; @@ -541,11 +562,17 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) { case PacketType::SpectrumAnalyzerResult: info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]); break; + case PacketType::SourceCalPoint: + case PacketType::ReceiverCalPoint: + info->amplitudePoint = DecodeAmplitudeCorrectionPoint(&data[4]); + break; case PacketType::Ack: case PacketType::PerformFirmwareUpdate: case PacketType::ClearFlash: case PacketType::Nack: case PacketType::RequestDeviceInfo: + case PacketType::RequestReceiverCal: + case PacketType::RequestSourceCal: // no payload, nothing to do break; case PacketType::None: @@ -588,11 +615,17 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_ case PacketType::SpectrumAnalyzerResult: payload_size = EncodeSpectrumAnalyzerResult(packet.spectrumResult, &dest[4], destsize - 8); break; + case PacketType::SourceCalPoint: + case PacketType::ReceiverCalPoint: + payload_size = EncodeAmplitudeCorrectionPoint(packet.amplitudePoint, &dest[4], destsize - 8); + break; case PacketType::Ack: case PacketType::PerformFirmwareUpdate: case PacketType::ClearFlash: case PacketType::Nack: case PacketType::RequestDeviceInfo: + case PacketType::RequestSourceCal: + case PacketType::RequestReceiverCal: // no payload, nothing to do break; case PacketType::None: diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index 39e1203..12618d8 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -134,6 +134,14 @@ using FirmwarePacket = struct _firmwarePacket { uint8_t data[FirmwareChunkSize]; }; +using AmplitudeCorrectionPoint = struct _amplitudecorrectionpoint { + uint8_t totalPoints; + uint8_t pointNum; + uint32_t freq; + int16_t port1; + int16_t port2; +}; + enum class PacketType : uint8_t { None = 0, Datapoint = 1, @@ -151,6 +159,10 @@ enum class PacketType : uint8_t { SpectrumAnalyzerSettings = 13, SpectrumAnalyzerResult = 14, RequestDeviceInfo = 15, + RequestSourceCal = 16, + RequestReceiverCal = 17, + SourceCalPoint = 18, + ReceiverCalPoint = 19, }; using PacketInfo = struct _packetinfo { @@ -166,6 +178,7 @@ using PacketInfo = struct _packetinfo { ManualStatus status; SpectrumAnalyzerSettings spectrumSettings; SpectrumAnalyzerResult spectrumResult; + AmplitudeCorrectionPoint amplitudePoint; }; }; diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp index 7997810..7d3db0e 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp @@ -4,6 +4,7 @@ #include "main.h" #include "FPGA_HAL.hpp" #include +#include "HW_HAL.hpp" #define LOG_LEVEL LOG_LEVEL_DEBUG #define LOG_MODULE "FPGA" @@ -33,7 +34,7 @@ void FPGA::WriteRegister(FPGA::Reg reg, uint16_t value) { } } -bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size) { +bool FPGA::Configure(uint32_t start_address, uint32_t bitstream_size) { if(!PROGRAM_B.gpio) { LOG_WARN("PROGRAM_B not defined, assuming FPGA configures itself in master configuration"); // wait too allow enough time for FPGA configuration @@ -64,7 +65,7 @@ bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size) } // TODO this part might be doable with the DMA instead of the buffer // get chunk of bitstream from flash... - f->read(start_address, size, buf); + HWHAL::flash.read(start_address, size, buf); // ... and pass it on to FPGA HAL_SPI_Transmit(&CONFIGURATION_SPI, buf, size, 100); bitstream_size -= size; diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp index b6624ad..a10a58b 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp @@ -105,7 +105,7 @@ enum class Window { Flattop = 0x03, }; -bool Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size); +bool Configure(uint32_t start_address, uint32_t bitstream_size); using HaltedCallback = void(*)(void); bool Init(HaltedCallback cb = nullptr); diff --git a/Software/VNA_embedded/Application/Drivers/Flash.cpp b/Software/VNA_embedded/Application/Drivers/Flash.cpp index 308b14a..ac9571f 100644 --- a/Software/VNA_embedded/Application/Drivers/Flash.cpp +++ b/Software/VNA_embedded/Application/Drivers/Flash.cpp @@ -29,7 +29,7 @@ void Flash::read(uint32_t address, uint16_t length, void *dest) { CS(true); } -bool Flash::write(uint32_t address, uint16_t length, uint8_t *src) { +bool Flash::write(uint32_t address, uint16_t length, void *src) { if((address & 0xFF) != 0 || length%256 != 0) { // only writes to complete pages allowed LOG_ERR("Invalid write address/size: %lu/%u", address, length); @@ -49,7 +49,7 @@ bool Flash::write(uint32_t address, uint16_t length, uint8_t *src) { // issue read command HAL_SPI_Transmit(spi, cmd, 4, 100); // write data - HAL_SPI_Transmit(spi, src, 256, 1000); + HAL_SPI_Transmit(spi, (uint8_t*) src, 256, 1000); CS(true); if(!WaitBusy(20)) { LOG_ERR("Write timed out"); @@ -78,16 +78,66 @@ void Flash::EnableWrite() { } bool Flash::eraseChip() { - LOG_INFO("Erasing..."); + LOG_INFO("Erasing chip..."); EnableWrite(); CS(false); - // enable write latch uint8_t chip_erase = 0x60; HAL_SPI_Transmit(spi, &chip_erase, 1, 100); CS(true); return WaitBusy(25000); } +bool Flash::eraseSector(uint32_t address) { + // align address with sector address + address -= address % SectorSize; + LOG_INFO("Erasing sector at %lu", address); + EnableWrite(); + CS(false); + uint8_t cmd[4] = { + 0x20, + (uint8_t) (address >> 16) & 0xFF, + (uint8_t) (address >> 8) & 0xFF, + (uint8_t) (address & 0xFF), + }; + HAL_SPI_Transmit(spi, cmd, 4, 100); + CS(true); + return WaitBusy(25000); +} + +bool Flash::erase32Block(uint32_t address) { + // align address with block address + address -= address % Block32Size; + LOG_INFO("Erasing 32kB block at %lu", address); + EnableWrite(); + CS(false); + uint8_t cmd[4] = { + 0x52, + (uint8_t) (address >> 16) & 0xFF, + (uint8_t) (address >> 8) & 0xFF, + (uint8_t) (address & 0xFF), + }; + HAL_SPI_Transmit(spi, cmd, 4, 100); + CS(true); + return WaitBusy(25000); +} + +bool Flash::erase64Block(uint32_t address) { + // align address with block address + address -= address % Block64Size; + LOG_INFO("Erasing 64kB block at %lu", address); + EnableWrite(); + CS(false); + uint8_t cmd[4] = { + 0xD8, + (uint8_t) (address >> 16) & 0xFF, + (uint8_t) (address >> 8) & 0xFF, + (uint8_t) (address & 0xFF), + }; + HAL_SPI_Transmit(spi, cmd, 4, 100); + CS(true); + return WaitBusy(25000); +} + void Flash::initiateRead(uint32_t address) { address &= 0x00FFFFFF; CS(false); @@ -120,3 +170,38 @@ bool Flash::WaitBusy(uint32_t timeout) { LOG_ERR("Timeout occured"); return false; } + +bool Flash::eraseRange(uint32_t start, uint32_t len) { + if(start % SectorSize != 0) { + LOG_ERR("Start address of range has to be sector aligned (is %lu)", start); + return false; + } + if(len % SectorSize != 0) { + LOG_ERR("Length of range has to be multiple of sector size (is %lu)", len); + return false; + } + uint32_t erased_len = 0; + while(erased_len < len) { + uint32_t remaining = len - erased_len; + if(remaining >= Block64Size && start % Block64Size == 0) { + erase64Block(start); + erased_len += Block64Size; + start += Block64Size; + continue; + } + if(remaining >= Block32Size && start % Block32Size == 0) { + erase32Block(start); + erased_len += Block32Size; + start += Block32Size; + continue; + } + if(remaining >= SectorSize && start % SectorSize == 0) { + eraseSector(start); + erased_len += SectorSize; + start += SectorSize; + continue; + } + // Should never get here + } + return true; +} diff --git a/Software/VNA_embedded/Application/Drivers/Flash.hpp b/Software/VNA_embedded/Application/Drivers/Flash.hpp index f315610..a011d15 100644 --- a/Software/VNA_embedded/Application/Drivers/Flash.hpp +++ b/Software/VNA_embedded/Application/Drivers/Flash.hpp @@ -17,8 +17,12 @@ public: bool isPresent(); void read(uint32_t address, uint16_t length, void *dest); - bool write(uint32_t address, uint16_t length, uint8_t *src); + bool write(uint32_t address, uint16_t length, void *src); bool eraseChip(); + bool eraseSector(uint32_t address); + bool erase32Block(uint32_t address); + bool erase64Block(uint32_t address); + bool eraseRange(uint32_t start, uint32_t len); // Starts the reading process without actually reading any bytes void initiateRead(uint32_t address); const SPI_HandleTypeDef* const getSpi() const { @@ -26,6 +30,9 @@ public: } private: + static constexpr uint32_t SectorSize = 4096; + static constexpr uint32_t Block32Size = 32768; + static constexpr uint32_t Block64Size = 65536; void CS(bool high) { if(high) { CS_gpio->BSRR = CS_pin; diff --git a/Software/VNA_embedded/Application/Firmware.cpp b/Software/VNA_embedded/Application/Firmware.cpp index cfc79c1..7732d42 100644 --- a/Software/VNA_embedded/Application/Firmware.cpp +++ b/Software/VNA_embedded/Application/Firmware.cpp @@ -2,6 +2,7 @@ #include "Protocol.hpp" #include +#include "HW_HAL.hpp" #define LOG_LEVEL LOG_LEVEL_INFO #define LOG_MODULE "FW" @@ -19,11 +20,11 @@ using Header = struct { uint32_t crc; } __attribute__((packed)); -Firmware::Info Firmware::GetFlashContentInfo(Flash *f) { +Firmware::Info Firmware::GetFlashContentInfo() { Info ret; memset(&ret, 0, sizeof(ret)); Header h; - f->read(0, sizeof(h), &h); + HWHAL::flash.read(0, sizeof(h), &h); // sanity check values if (memcmp(&h.magic, "VNA!", 4) || h.FPGA_start == UINT32_MAX || h.FPGA_size > FPGA_MAXSIZE @@ -40,7 +41,7 @@ Firmware::Info Firmware::GetFlashContentInfo(Flash *f) { if (h.FPGA_size + h.CPU_size - checked_size < read_size) { read_size = h.FPGA_size + h.CPU_size - checked_size; } - f->read(h.FPGA_start + checked_size, read_size, buf); + HWHAL::flash.read(h.FPGA_start + checked_size, read_size, buf); crc = Protocol::CRC32(crc, buf, read_size); checked_size += read_size; } @@ -55,7 +56,7 @@ Firmware::Info Firmware::GetFlashContentInfo(Flash *f) { if (h.CPU_size - checked_size < read_size) { read_size = h.CPU_size - checked_size; } - f->read(h.CPU_start + checked_size, read_size, buf); + HWHAL::flash.read(h.CPU_start + checked_size, read_size, buf); if(memcmp(buf, (void*)(0x8000000+checked_size), read_size)) { LOG_INFO("Difference to CPU firmware in external FLASH detected, update required"); ret.CPU_need_update = true; @@ -158,7 +159,7 @@ static void copy_flash(uint32_t size, SPI_TypeDef *spi) { } } -void Firmware::PerformUpdate(Flash *f, Info info) { +void Firmware::PerformUpdate(Info info) { if(!info.valid) { LOG_ERR("Invalid firmware data, not performing update"); return; @@ -174,8 +175,8 @@ void Firmware::PerformUpdate(Flash *f, Info info) { FLASH_WaitForLastOperation(50000); // Initiate readback from flash at CPU firmware start address - f->initiateRead(info.CPU_image_address); + HWHAL::flash.initiateRead(info.CPU_image_address); - copy_flash(info.CPU_image_size, f->getSpi()->Instance); + copy_flash(info.CPU_image_size, HWHAL::flash.getSpi()->Instance); __builtin_unreachable(); } diff --git a/Software/VNA_embedded/Application/Firmware.hpp b/Software/VNA_embedded/Application/Firmware.hpp index d5776bd..5e62762 100644 --- a/Software/VNA_embedded/Application/Firmware.hpp +++ b/Software/VNA_embedded/Application/Firmware.hpp @@ -12,6 +12,8 @@ namespace Firmware { +static constexpr uint32_t maxSize = 1048576; + using Info = struct info { uint32_t FPGA_bitstream_address; uint32_t FPGA_bitstream_size; @@ -21,8 +23,8 @@ using Info = struct info { bool CPU_need_update; }; -Info GetFlashContentInfo(Flash *f); -void PerformUpdate(Flash *f, Info info); +Info GetFlashContentInfo(); +void PerformUpdate(Info info); } diff --git a/Software/VNA_embedded/Application/HW_HAL.cpp b/Software/VNA_embedded/Application/HW_HAL.cpp index d736b5c..8db068b 100644 --- a/Software/VNA_embedded/Application/HW_HAL.cpp +++ b/Software/VNA_embedded/Application/HW_HAL.cpp @@ -3,3 +3,5 @@ Si5351C HWHAL::Si5351 = Si5351C(&hi2c2, 26000000); MAX2871 HWHAL::Source = MAX2871(&hspi1, FPGA_CS_GPIO_Port, FPGA_CS_Pin, nullptr, 0, nullptr, 0, nullptr, 0, GPIOA, GPIO_PIN_6); MAX2871 HWHAL::LO1 = MAX2871(&hspi1, FPGA_CS_GPIO_Port, FPGA_CS_Pin, nullptr, 0, nullptr, 0, nullptr, 0, GPIOA, GPIO_PIN_6); +extern SPI_HandleTypeDef hspi1; +Flash HWHAL::flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin); diff --git a/Software/VNA_embedded/Application/HW_HAL.hpp b/Software/VNA_embedded/Application/HW_HAL.hpp index 0150894..3f1eb6f 100644 --- a/Software/VNA_embedded/Application/HW_HAL.hpp +++ b/Software/VNA_embedded/Application/HW_HAL.hpp @@ -4,6 +4,7 @@ #include "Si5351C.hpp" #include "max2871.hpp" #include "main.h" +#include "Flash.hpp" extern I2C_HandleTypeDef hi2c2; extern SPI_HandleTypeDef hspi1; @@ -13,6 +14,7 @@ namespace HWHAL { extern Si5351C Si5351; extern MAX2871 Source; extern MAX2871 LO1; +extern Flash flash; // Mapping of the Si5351 channels to PLLs/Mixers namespace SiChannel {