Partial source calibration dialog
This commit is contained in:
parent
6f717de0f1
commit
875f3b0170
@ -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 \
|
||||
|
187
Software/PC_Application/Calibration/amplitudecaldialog.cpp
Normal file
187
Software/PC_Application/Calibration/amplitudecaldialog.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include "amplitudecaldialog.h"
|
||||
#include "ui_amplitudecaldialog.h"
|
||||
#include "mode.h"
|
||||
#include "unit.h"
|
||||
#include <QDebug>
|
||||
|
||||
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::CorrectionPoint> 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;i<points.size();i++) {
|
||||
auto p = points[i];
|
||||
Protocol::PacketInfo info;
|
||||
info.type = pointType();
|
||||
info.amplitudePoint.freq = p.frequency / 10.0;
|
||||
info.amplitudePoint.port1 = p.correctionPort1;
|
||||
info.amplitudePoint.port2 = p.correctionPort2;
|
||||
info.amplitudePoint.totalPoints = points.size();
|
||||
info.amplitudePoint.pointNum = i;
|
||||
dev->SendPacket(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;
|
||||
}
|
77
Software/PC_Application/Calibration/amplitudecaldialog.h
Normal file
77
Software/PC_Application/Calibration/amplitudecaldialog.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef AMPLITUDECALDIALOG_H
|
||||
#define AMPLITUDECALDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#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<CorrectionPoint> 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<CorrectionPoint> points;
|
||||
Ui::AmplitudeCalDialog *ui;
|
||||
Device *dev;
|
||||
Mode *activeMode;
|
||||
AmplitudeModel model;
|
||||
};
|
||||
|
||||
#endif // SOURCECALDIALOG_H
|
107
Software/PC_Application/Calibration/amplitudecaldialog.ui
Normal file
107
Software/PC_Application/Calibration/amplitudecaldialog.ui
Normal file
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AmplitudeCalDialog</class>
|
||||
<widget class="QDialog" name="AmplitudeCalDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>766</width>
|
||||
<height>599</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="view">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="add">
|
||||
<property name="text">
|
||||
<string>Add Point</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../icons.qrc">
|
||||
<normaloff>:/icons/add.png</normaloff>:/icons/add.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove">
|
||||
<property name="text">
|
||||
<string>Delete Point</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../icons.qrc">
|
||||
<normaloff>:/icons/remove.png</normaloff>:/icons/remove.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="help">
|
||||
<property name="text">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="help"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="load">
|
||||
<property name="text">
|
||||
<string>Load from Device</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-open" resource="../icons.qrc">
|
||||
<normaloff>:/icons/open.png</normaloff>:/icons/open.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="save">
|
||||
<property name="text">
|
||||
<string>Save to Device</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../icons.qrc">
|
||||
<normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
8
Software/PC_Application/Calibration/sourcecaldialog.cpp
Normal file
8
Software/PC_Application/Calibration/sourcecaldialog.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include "sourcecaldialog.h"
|
||||
|
||||
SourceCalDialog::SourceCalDialog(Device *dev)
|
||||
: AmplitudeCalDialog(dev)
|
||||
{
|
||||
setWindowTitle("Source Calibration Dialog");
|
||||
LoadFromDevice();
|
||||
}
|
17
Software/PC_Application/Calibration/sourcecaldialog.h
Normal file
17
Software/PC_Application/Calibration/sourcecaldialog.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef SOURCECALDIALOG_H
|
||||
#define SOURCECALDIALOG_H
|
||||
|
||||
#include <QObject>
|
||||
#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
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -144,7 +144,6 @@ ManualControlDialog::ManualControlDialog(Device &dev, QWidget *parent) :
|
||||
MakeReadOnly(ui->refmag);
|
||||
MakeReadOnly(ui->refphase);
|
||||
|
||||
qRegisterMetaType<Protocol::ManualStatus>("Status");
|
||||
connect(&dev, &Device::ManualStatusReceived, this, &ManualControlDialog::NewStatus);
|
||||
|
||||
connect(ui->SourceCE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
|
||||
|
@ -192,8 +192,6 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
|
||||
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
|
||||
docks.insert(markerDock);
|
||||
|
||||
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumResult");
|
||||
|
||||
// Set initial sweep settings
|
||||
auto pref = Preferences::getInstance();
|
||||
if(pref.Startup.RememberSweepSettings) {
|
||||
|
@ -360,8 +360,6 @@ VNA::VNA(AppWindow *window)
|
||||
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
|
||||
docks.insert(markerDock);
|
||||
|
||||
qRegisterMetaType<Protocol::Datapoint>("Datapoint");
|
||||
|
||||
// Set initial sweep settings
|
||||
auto pref = Preferences::getInstance();
|
||||
if(pref.Acquisition.alwaysExciteBothPorts) {
|
||||
|
@ -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<Protocol::Datapoint>("Datapoint");
|
||||
qRegisterMetaType<Protocol::ManualStatus>("Manual");
|
||||
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumAnalyzerResult");
|
||||
qRegisterMetaType<Protocol::AmplitudeCorrectionPoint>("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;
|
||||
|
@ -44,6 +44,7 @@ private slots:
|
||||
void UpdateReference();
|
||||
void StartFirmwareUpdateDialog();
|
||||
void DeviceNeedsUpdate(int reported, int expected);
|
||||
void SourceCalibrationDialog();
|
||||
private:
|
||||
void DeviceConnectionLost();
|
||||
void CreateToolbars();
|
||||
|
@ -48,6 +48,8 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionManual_Control"/>
|
||||
<addaction name="actionFirmware_Update"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSource_Calibration"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
@ -151,6 +153,14 @@
|
||||
<string>About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSource_Calibration">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Source Calibration</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
|
136
Software/VNA_embedded/Application/AmplitudeCal.cpp
Normal file
136
Software/VNA_embedded/Application/AmplitudeCal.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "AmplitudeCal.hpp"
|
||||
#include <cstring>
|
||||
#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<table.usedPoints;i++) {
|
||||
// assemble packet
|
||||
Protocol::PacketInfo p;
|
||||
p.type = type;
|
||||
p.amplitudePoint.totalPoints = table.usedPoints;
|
||||
p.amplitudePoint.pointNum = i;
|
||||
p.amplitudePoint.freq = table.freq[i];
|
||||
p.amplitudePoint.port1 = table.port1Correction[i];
|
||||
p.amplitudePoint.port2 = table.port2Correction[i];
|
||||
Communication::Send(p);
|
||||
}
|
||||
}
|
||||
|
||||
void AmplitudeCal::SendSource() {
|
||||
SendCorrectionTable(cal.Source, Protocol::PacketType::SourceCalPoint);
|
||||
}
|
||||
|
||||
void AmplitudeCal::SendReceiver() {
|
||||
SendCorrectionTable(cal.Receiver, Protocol::PacketType::ReceiverCalPoint);
|
||||
}
|
||||
|
||||
static void addPoint(CorrectionTable& table, const Protocol::AmplitudeCorrectionPoint& p) {
|
||||
table.freq[p.pointNum] = p.freq;
|
||||
table.port1Correction[p.pointNum] = p.port1;
|
||||
table.port2Correction[p.pointNum] = p.port2;
|
||||
if(p.pointNum == p.totalPoints - 1) {
|
||||
// this was the last point, update used points and save
|
||||
table.usedPoints = p.totalPoints;
|
||||
LOG_INFO("Last point received, saving to flash");
|
||||
AmplitudeCal::Save();
|
||||
}
|
||||
}
|
||||
|
||||
void AmplitudeCal::AddSourcePoint(const Protocol::AmplitudeCorrectionPoint& p) {
|
||||
addPoint(cal.Source, p);
|
||||
}
|
||||
|
||||
void AmplitudeCal::AddReceiverPoint(const Protocol::AmplitudeCorrectionPoint& p) {
|
||||
addPoint(cal.Receiver, p);
|
||||
}
|
30
Software/VNA_embedded/Application/AmplitudeCal.hpp
Normal file
30
Software/VNA_embedded/Application/AmplitudeCal.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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);
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "main.h"
|
||||
#include "FPGA_HAL.hpp"
|
||||
#include <complex>
|
||||
#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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Protocol.hpp"
|
||||
#include <cstring>
|
||||
#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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user