Split register fields + save/load register values

This commit is contained in:
Jan Käberich 2021-05-27 20:13:02 +02:00
parent 9f33d47da0
commit b99f443673
9 changed files with 1188 additions and 830 deletions

View file

@ -10,12 +10,11 @@ MAX2871::MAX2871()
addRegister(new Register("Register 3", 3, 32));
addRegister(new Register("Register 4", 4, 32));
addRegister(new Register("Register 5", 5, 32));
addRegister(new Register("Register 6", 6, 32));
ui = new Ui::MAX2871Widget;
ui->setupUi(widget);
regs[0]->assignUI(ui->FracNEnable, 31, true);
regs[0]->assignUI(ui->INT, 31);
regs[0]->assignUI(ui->N, 15, 16);
regs[0]->assignUI(ui->frac, 3, 12);
@ -26,7 +25,7 @@ MAX2871::MAX2871()
regs[2]->assignUI(ui->LDS, 31, 1);
regs[2]->assignUI(ui->SDN, 29, 2);
// regs[2]->assignUI(ui->MUX, 26, 3); // TODO MSB in reg 5
regs[2]->assignUI(ui->MUX, 26, 3);
regs[2]->assignUI(ui->DBR, 25);
regs[2]->assignUI(ui->RDIV2, 24);
regs[2]->assignUI(ui->R, 14, 10);
@ -50,9 +49,10 @@ MAX2871::MAX2871()
regs[4]->assignUI(ui->SDLDO, 28);
regs[4]->assignUI(ui->SDDIV, 27);
regs[4]->assignUI(ui->SDREF, 26);
// regs[4]->assignUI(ui->BS, , ); // TODO value split in register
regs[4]->assignUI(ui->BS, 24, 2, 8);
regs[4]->assignUI(ui->FB, 23);
regs[4]->assignUI(ui->DIVA, 20, 3);
regs[4]->assignUI(ui->BS, 12, 8);
regs[4]->assignUI(ui->SDVCO, 11);
regs[4]->assignUI(ui->MTLD, 10);
regs[4]->assignUI(ui->BDIV, 9, 1);
@ -61,17 +61,98 @@ MAX2871::MAX2871()
regs[4]->assignUI(ui->RFA_EN, 5);
regs[4]->assignUI(ui->APWR, 3, 2);
// regs[5]->assignUI(ui->VAS_DLY, , ); // TODO connect with VAS_SHDN
regs[5]->assignUI(ui->VAS_DLY, 29, 2);
regs[5]->assignUI(ui->SDPLL, 25);
regs[5]->assignUI(ui->F01, 24);
regs[5]->assignUI(ui->LD, 22, 2);
// regs[5]->assignUI(ui->MUX, , ); // TODO LSB in reg 2
regs[5]->assignUI(ui->MUX, 18, 1, 3);
regs[5]->assignUI(ui->ADCS, 6);
regs[5]->assignUI(ui->ADCM, 3, 3);
regs[6]->assignUI(ui->ADC, 16, 7);
Register::fillTableWidget(ui->table, regs);
ui->ref->setPrefixes(" kMG");
ui->ref->setUnit("Hz");
ui->freqRef->setPrefixes(" kMG");
ui->freqRef->setUnit("Hz");
ui->freqPFD->setPrefixes(" kMG");
ui->freqPFD->setUnit("Hz");
ui->freqVCO->setPrefixes(" kMG");
ui->freqVCO->setUnit("Hz");
ui->freqOutA->setPrefixes(" kMG");
ui->freqOutA->setUnit("Hz");
ui->freqOutB->setPrefixes(" kMG");
ui->freqOutB->setUnit("Hz");
// user friendly frequency calculation connections
QObject::connect(ui->ref, &SIUnitEdit::valueChanged, ui->freqRef, &SIUnitEdit::setValue);
auto updatePFD = [=]() {
auto pfd = ui->ref->value();
if(ui->DBR->isChecked()) {
pfd *= 2;
}
pfd /= ui->R->value();
if(ui->RDIV2->isChecked()) {
pfd /= 2;
}
ui->freqPFD->setValue(pfd);
bool valid = pfd <= 125000000 || (pfd <= 140000000 && ui->INT->isChecked());
// check value and set background
QPalette palette;
palette.setColor(QPalette::Base,valid ? Qt::white : Qt::red);
ui->freqPFD->setPalette(palette);
};
QObject::connect(ui->ref, &SIUnitEdit::valueChanged, updatePFD);
QObject::connect(ui->INT, &QCheckBox::toggled, updatePFD);
QObject::connect(ui->DBR, &QCheckBox::toggled, updatePFD);
QObject::connect(ui->RDIV2, &QCheckBox::toggled, updatePFD);
QObject::connect(ui->R, qOverload<int>(&QSpinBox::valueChanged), updatePFD);
auto updateVCO = [=]() {
auto pfd = ui->freqPFD->value();
auto vco = pfd * ui->N->value();
if(!ui->INT->isChecked()){
vco += pfd * (double) ui->frac->value() / ui->M->value();
}
if(!ui->FB->isChecked()) {
// using divided down vco output
// multiply by divider ratio (but only up to 16)
auto mult = 1UL << ui->DIVA->currentIndex();
if(mult > 16) {
mult = 16;
}
vco *= mult;
}
ui->freqVCO->setValue(vco);
bool valid = vco >= 3000000000 && vco <= 6000000000;
// check value and set background
QPalette palette;
palette.setColor(QPalette::Base,valid ? Qt::white : Qt::red);
ui->freqVCO->setPalette(palette);
};
QObject::connect(ui->freqPFD, &SIUnitEdit::valueChanged, updateVCO);
QObject::connect(ui->N, qOverload<int>(&QSpinBox::valueChanged), updateVCO);
QObject::connect(ui->frac, qOverload<int>(&QSpinBox::valueChanged), updateVCO);
QObject::connect(ui->FB, &QCheckBox::toggled, updateVCO);
QObject::connect(ui->INT, &QCheckBox::toggled, updateVCO);
QObject::connect(ui->M, qOverload<int>(&QSpinBox::valueChanged), updateVCO);
auto updateOutA = [=]() {
auto out_a = ui->freqVCO->value() / (1 << ui->DIVA->currentIndex());
ui->freqOutA->setValue(out_a);
};
QObject::connect(ui->freqVCO, &SIUnitEdit::valueChanged, updateOutA);
QObject::connect(ui->DIVA, qOverload<int>(&QComboBox::currentIndexChanged), updateOutA);
auto updateOutB = [=]() {
auto out_b = ui->freqVCO->value();
if(ui->BDIV->currentIndex() == 0) {
out_b /= (1 << ui->DIVA->currentIndex());
}
ui->freqOutB->setValue(out_b);
};
QObject::connect(ui->freqVCO, &SIUnitEdit::valueChanged, updateOutB);
QObject::connect(ui->DIVA, qOverload<int>(&QComboBox::currentIndexChanged), updateOutB);
QObject::connect(ui->BDIV, qOverload<int>(&QComboBox::currentIndexChanged), updateOutB);
ui->ref->setValue(100000000);
}
MAX2871::~MAX2871()
@ -79,3 +160,23 @@ MAX2871::~MAX2871()
delete ui;
}
void MAX2871::fromJSON(nlohmann::json j)
{
registersFromJSON(j["registers"]);
ui->cbRef->setCurrentText(QString::fromStdString(j["reference"]));
if(ui->cbRef->currentText() == "Manual") {
ui->ref->setValue(j["reference_frequency"]);
}
}
nlohmann::json MAX2871::toJSON()
{
nlohmann::json j;
j["registers"] = registersToJSON();
j["reference"] = ui->cbRef->currentText().toStdString();
if(ui->cbRef->currentText() == "Manual") {
j["reference_frequency"] = ui->ref->value();
}
return j;
}

View file

@ -14,6 +14,9 @@ public:
MAX2871();
~MAX2871();
void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override;
private:
Ui::MAX2871Widget *ui;
};

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,13 @@
#include "register.h"
#include "max2871.h"
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
#include <fstream>
#include <iomanip>
using namespace std;
RawRegisterDialog::RawRegisterDialog(Device *dev, QWidget *parent) :
QDialog(parent),
@ -20,6 +26,60 @@ RawRegisterDialog::RawRegisterDialog(Device *dev, QWidget *parent) :
// trigger extraction of device information, this will trigger the receivedDirectRegisterInfo slot which will further populate the dialog
dev->SendCommandWithoutPayload(Protocol::PacketType::RequestDirectRegisterInfo);
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save register settigns", "", "Raw register file (*.regs)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
if(!filename.endsWith(".regs")) {
filename.append(".regs");
}
nlohmann::json j;
for(auto dev : devices) {
nlohmann::json jdev;
jdev["partnumber"] = dev->getPartnumber().toStdString();
jdev["settings"] = dev->toJSON();
j[dev->getName().toStdString()] = jdev;
}
ofstream file;
file.open(filename.toStdString());
file << setw(4) << j << endl;
file.close();
}
});
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Load register settigns", "", "Raw register file (*.regs)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
ifstream file;
file.open(filename.toStdString());
if(!file.is_open()) {
throw runtime_error("Unable to open file");
}
nlohmann::json j;
file >> j;
for(auto jdev : j.items()) {
auto name = QString::fromStdString(jdev.key());
bool found = false;
for(auto dev : devices) {
if(dev->getName() == name) {
// potential match
auto part = QString::fromStdString(jdev.value()["partnumber"]);
if(part == dev->getPartnumber()) {
dev->fromJSON(jdev.value()["settings"]);
} else {
qWarning() << "Got registers for device" << name <<", but partnumber does not match";
}
found = true;
break;
}
}
if(!found) {
qWarning() << "Got registers for device" << name <<", but got no device with that name";
}
}
}
});
}
RawRegisterDialog::~RawRegisterDialog()
@ -33,7 +93,7 @@ void RawRegisterDialog::receivedDirectRegisterInfo(Protocol::DirectRegisterInfo
qWarning() << "Received invalid register device:" << info.num;
return;
}
auto regdev = RegisterDevice::create(dev, info.num, info.type);
auto regdev = RegisterDevice::create(dev, info.num, info.type, info.name);
if(!regdev) {
qWarning() << "Unable to create register device" << info.type <<", unknown type";
return;

View file

@ -27,45 +27,12 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Open|QDialogButtonBox::Save</set>
<set>QDialogButtonBox::Open|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RawRegisterDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RawRegisterDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

View file

@ -1,12 +1,14 @@
#include "register.h"
#include <exception>
#include <QHeaderView>
#include <QDebug>
Register::Register(QString name, int address, int width)
: QObject(nullptr),
name(name),
address(address),
width(width)
width(width),
updating(false)
{
value = 0;
}
@ -28,27 +30,49 @@ void Register::assignUI(QCheckBox *cb, int bitpos, bool inverted)
});
}
void Register::assignUI(QComboBox *cb, int pos, int width)
void Register::assignUI(QComboBox *cb, int pos, int width, int ui_bitoffset)
{
connect(this, &Register::valueChanged, [=]() {
auto value = getValue(pos, width);
if(cb->count() > static_cast<int>(value)) {
cb->setCurrentIndex(value);
auto mask = (1UL << width) - 1;
mask <<= ui_bitoffset;
value <<= ui_bitoffset;
auto old_gui = cb->currentIndex();
old_gui &= ~mask;
old_gui |= value;
if(cb->count() > static_cast<int>(old_gui)) {
updating = true;
cb->setCurrentIndex(old_gui);
updating = false;
}
});
connect(cb, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index){
setValue(index, pos, width);
if(!updating) {
index >>= ui_bitoffset;
setValue(index, pos, width);
}
});
}
void Register::assignUI(QSpinBox *sb, int pos, int width)
void Register::assignUI(QSpinBox *sb, int pos, int width, int ui_bitoffset)
{
connect(this, &Register::valueChanged, [=]() {
auto value = getValue(pos, width);
sb->setValue(value);
auto mask = (1UL << width) - 1;
mask <<= ui_bitoffset;
value <<= ui_bitoffset;
auto old_gui = sb->value();
old_gui &= ~mask;
old_gui |= value;
updating = true;
sb->setValue(old_gui);
updating = false;
});
connect(sb, qOverload<int>(&QSpinBox::valueChanged), [=](int index){
setValue(index, pos, width);
if(!updating) {
index >>= ui_bitoffset;
setValue(index, pos, width);
}
});
}
@ -74,10 +98,7 @@ unsigned long Register::getValue()
unsigned long Register::getValue(int pos, int width)
{
unsigned long mask = 0;
for(int i=0;i<width;i++) {
mask |= (1UL << i);
}
unsigned long mask = (1UL << width) - 1;
mask <<= pos;
auto masked = value & mask;
masked >>= pos;
@ -91,10 +112,7 @@ void Register::setValue(unsigned long newval)
void Register::setValue(unsigned long newval, int pos, int width)
{
unsigned long mask = 0;
for(int i=0;i<width;i++) {
mask |= (1UL << i);
}
unsigned long mask = (1UL << width) - 1;
newval &= mask;
newval <<= pos;
mask <<= pos;

View file

@ -14,8 +14,12 @@ public:
Register(QString name, int address, int width);
void assignUI(QCheckBox *cb, int bitpos, bool inverted = false);
void assignUI(QComboBox *cb, int pos, int width);
void assignUI(QSpinBox *sb, int pos, int width);
// pos: bitpos at which the field in the register starts which is associated with the UI element
// width: amount of bits in the field
// ui_bitoffset: set to LSB of the ui element in this register field (only required for elements that
// are not represented as one field in a register, e.g. when splitting bits across multiple registers)
void assignUI(QComboBox *cb, int pos, int width, int ui_bitoffset = 0);
void assignUI(QSpinBox *sb, int pos, int width, int ui_bitoffset = 0);
QString hexString();
bool setFromString(QString hex);
@ -37,6 +41,7 @@ private:
int address;
int width;
unsigned long value;
bool updating; // for preventing endless recursion when updating register/its UI connections
};
#endif // REGISTER_H

View file

@ -3,7 +3,7 @@
#include "max2871.h"
#include "Device/device.h"
RegisterDevice *RegisterDevice::create(Device *dev, int number, QString partnumber)
RegisterDevice *RegisterDevice::create(Device *dev, int number, QString partnumber, QString name)
{
RegisterDevice *regdev = nullptr;
if(partnumber == "MAX2871") {
@ -13,6 +13,7 @@ RegisterDevice *RegisterDevice::create(Device *dev, int number, QString partnumb
regdev->dev = dev;
regdev->number = number;
regdev->partnumber = partnumber;
regdev->name = name;
// read initial register content
Protocol::PacketInfo p;
@ -45,6 +46,16 @@ RegisterDevice::RegisterDevice()
widget = new QWidget;
}
QString RegisterDevice::getName() const
{
return name;
}
QString RegisterDevice::getPartnumber() const
{
return partnumber;
}
QWidget *RegisterDevice::getWidget() const
{
return widget;
@ -64,3 +75,25 @@ void RegisterDevice::addRegister(Register *reg)
});
}
nlohmann::json RegisterDevice::registersToJSON()
{
nlohmann::json j;
for(auto r : regs) {
j[r->getAddress()] = r->getValue();
}
return j;
}
void RegisterDevice::registersFromJSON(nlohmann::json j)
{
for(auto val : j.items()) {
auto address = QString::fromStdString(val.key()).toInt();
for(auto r : regs) {
if(r->getAddress() == address) {
r->setValue(val.value());
break;
}
}
}
}

View file

@ -7,26 +7,30 @@
#include "savable.h"
#include "Device/device.h"
class RegisterDevice //: public Savable
class RegisterDevice : public Savable
{
public:
static RegisterDevice *create(Device *dev, int number, QString partnumber);
static RegisterDevice *create(Device *dev, int number, QString partnumber, QString name);
~RegisterDevice();
void setRegister(int address, unsigned long value);
// void fromJSON(nlohmann::json j) override;
// nlohmann::json toJSON() override;
QWidget *getWidget() const;
QString getPartnumber() const;
QString getName() const;
protected:
void addRegister(Register *reg);
nlohmann::json registersToJSON();
void registersFromJSON(nlohmann::json j);
RegisterDevice();
Device *dev;
int number;
QString partnumber;
QString name;
std::vector<Register*> regs;
QWidget *widget;
};