diff --git a/software/usbflashprog/CMakeLists.txt b/software/usbflashprog/CMakeLists.txt index d3efda3b..e900e266 100644 --- a/software/usbflashprog/CMakeLists.txt +++ b/software/usbflashprog/CMakeLists.txt @@ -70,8 +70,6 @@ elseif(NORMAL_BUILD) backend/epromfile/qatmelfile.hpp backend/epromfile/qepromfile.cpp backend/epromfile/qepromfile.hpp - ui/qhexeditor.cpp - ui/qhexeditor.hpp backend/devices/device.cpp backend/devices/device.hpp backend/devices/parallel/dummy.cpp @@ -80,10 +78,16 @@ elseif(NORMAL_BUILD) backend/devices/parallel/sram.hpp backend/devices/parallel/eprom.cpp backend/devices/parallel/eprom.hpp - main/mainwindow.cpp - main/mainwindow.hpp - main/mainwindow.ui + ui/qhexeditor.cpp + ui/qhexeditor.hpp + ui/mainwindow.cpp + ui/mainwindow.hpp + ui/mainwindow.ui + ui/settings.cpp + ui/settings.hpp + ui/settings.ui main.cpp + main.hpp ) set(PROJECT_RESOURCES diff --git a/software/usbflashprog/backend/devices/device.cpp b/software/usbflashprog/backend/devices/device.cpp index 9f8862be..5333fc09 100644 --- a/software/usbflashprog/backend/devices/device.cpp +++ b/software/usbflashprog/backend/devices/device.cpp @@ -15,8 +15,22 @@ */ // --------------------------------------------------------------------------- +#include +#include + #include "backend/devices/device.hpp" +// --------------------------------------------------------------------------- +// Logging + +Q_LOGGING_CATEGORY(device, "device") + +#define DEBUG qCDebug(device) +#define INFO qCInfo(device) +#define WARNING qCWarning(device) +#define CRITICAL qCCritical(device) +#define FATAL qCFatal(device) + // --------------------------------------------------------------------------- TDeviceID::TDeviceID() : manufacturer(0), device(0) {} @@ -102,6 +116,39 @@ TDeviceCapabilities::TDeviceCapabilities() TDeviceInformation::TDeviceInformation() : deviceType(kDeviceParallelMemory), name("") {} +QString TDeviceInformation::toString() const { + QString result; + QTextStream ts(&result); + ts << "Name: " << name; + ts << "; Type: "; + switch (deviceType) { + case kDeviceParallelMemory: + ts << "Parallel Memory"; + break; + case kDeviceSerialMemory: + ts << "Serial Memory"; + break; + default: + ts << "Unknown"; + break; + } + ts << "; Capabilities: ["; + ts << "hasProgram=" << (capability.hasProgram ? 1 : 0); + ts << ", hasVerify=" << (capability.hasVerify ? 1 : 0); + ts << ", hasErase=" << (capability.hasErase ? 1 : 0); + ts << ", hasGetId=" << (capability.hasGetId ? 1 : 0); + ts << ", hasRead=" << (capability.hasRead ? 1 : 0); + ts << ", hasBlankCheck=" << (capability.hasBlankCheck ? 1 : 0); + ts << ", hasUnprotect=" << (capability.hasUnprotect ? 1 : 0); + ts << ", hasSectorSize=" << (capability.hasSectorSize ? 1 : 0); + ts << ", hasFastProg=" << (capability.hasFastProg ? 1 : 0); + ts << ", hasSkipFF=" << (capability.hasSkipFF ? 1 : 0); + ts << ", hasVDD=" << (capability.hasVDD ? 1 : 0); + ts << ", hasVPP=" << (capability.hasVPP ? 1 : 0); + ts << "]"; + return result; +} + // --------------------------------------------------------------------------- Device::Device(QObject *parent) @@ -146,8 +193,10 @@ QString Device::getPort() const { } void Device::setSize(uint32_t value) { - if (size_ == value) return; - if (value) size_ = value; + if (size_ != value) { + if (value) size_ = value; + } + DEBUG << "Size: " << QString("%1").arg(size_); } uint32_t Device::getSize() const { @@ -155,8 +204,10 @@ uint32_t Device::getSize() const { } void Device::setTwp(uint32_t us) { - if (twp_ == us) return; - if (us) twp_ = us; + if (twp_ != us) { + if (us) twp_ = us; + } + DEBUG << "tWP: " << QString("%1").arg(twp_); } uint32_t Device::getTwp() const { @@ -164,8 +215,10 @@ uint32_t Device::getTwp() const { } void Device::setTwc(uint32_t us) { - if (twc_ == us) return; - if (us) twc_ = us; + if (twc_ != us) { + if (us) twc_ = us; + } + DEBUG << "tWC: " << QString("%1").arg(twc_); } uint32_t Device::getTwc() const { @@ -173,8 +226,10 @@ uint32_t Device::getTwc() const { } void Device::setVddRd(float value) { - if (vddRd_ == value) return; - if (value >= 0.0f) vddRd_ = value; + if (vddRd_ != value) { + if (value >= 0.0f) vddRd_ = value; + } + DEBUG << "VDD Read: " << QString("%1").arg(vddRd_, 4, 'f', 2); } float Device::getVddRd() const { @@ -182,8 +237,10 @@ float Device::getVddRd() const { } void Device::setVddWr(float value) { - if (vddWr_ == value) return; - if (value >= 0.0f) vddWr_ = value; + if (vddWr_ != value) { + if (value >= 0.0f) vddWr_ = value; + } + DEBUG << "VDD Prog: " << QString("%1").arg(vddWr_, 4, 'f', 2); } float Device::getVddWr() const { @@ -191,8 +248,10 @@ float Device::getVddWr() const { } void Device::setVpp(float value) { - if (vpp_ == value) return; - if (value >= 0.0f) vpp_ = value; + if (vpp_ != value) { + if (value >= 0.0f) vpp_ = value; + } + DEBUG << "VPP: " << QString("%1").arg(vpp_, 4, 'f', 2); } float Device::getVpp() const { @@ -200,8 +259,10 @@ float Device::getVpp() const { } void Device::setVee(float value) { - if (vee_ == value) return; - if (value >= 0.0f) vee_ = value; + if (vee_ != value) { + if (value >= 0.0f) vee_ = value; + } + DEBUG << "VEE: " << QString("%1").arg(vee_, 4, 'f', 2); } float Device::getVee() const { @@ -209,8 +270,8 @@ float Device::getVee() const { } void Device::setSkipFF(bool value) { - if (skipFF_ == value) return; - skipFF_ = value; + if (skipFF_ != value) skipFF_ = value; + DEBUG << "Skip 0xFF: " << QString("%1").arg(skipFF_ ? 1 : 0); } bool Device::getSkipFF() const { @@ -218,8 +279,8 @@ bool Device::getSkipFF() const { } void Device::setFastProg(bool value) { - if (fastProg_ == value) return; - fastProg_ = value; + if (fastProg_ != value) fastProg_ = value; + DEBUG << "Fast Prog/Erase: " << QString("%1").arg(fastProg_ ? 1 : 0); } bool Device::getFastProg() const { @@ -227,8 +288,8 @@ bool Device::getFastProg() const { } void Device::setSectorSize(uint16_t value) { - if (sectorSize_ == value) return; - if (value) sectorSize_ = value; + if (sectorSize_ != value) sectorSize_ = value; + DEBUG << "Sector Size: " << QString("%1").arg(sectorSize_); } uint16_t Device::getSectorSize() const { diff --git a/software/usbflashprog/backend/devices/device.hpp b/software/usbflashprog/backend/devices/device.hpp index da8ed8c5..84ad83b7 100644 --- a/software/usbflashprog/backend/devices/device.hpp +++ b/software/usbflashprog/backend/devices/device.hpp @@ -113,6 +113,11 @@ typedef struct TDeviceInformation { TDeviceCapabilities capability; /** @brief Constructor. */ TDeviceInformation(); + /** + * @brief Converts contents to string. + * @return Device Info as QString. + */ + QString toString() const; } TDeviceInformation; // --------------------------------------------------------------------------- diff --git a/software/usbflashprog/backend/devices/parallel/dummy.cpp b/software/usbflashprog/backend/devices/parallel/dummy.cpp index 7ceaf98d..7c716705 100644 --- a/software/usbflashprog/backend/devices/parallel/dummy.cpp +++ b/software/usbflashprog/backend/devices/parallel/dummy.cpp @@ -15,9 +15,22 @@ */ // --------------------------------------------------------------------------- +#include #include + #include "backend/devices/parallel/dummy.hpp" +// --------------------------------------------------------------------------- +// Logging + +Q_LOGGING_CATEGORY(deviceParDummy, "device.parallel.dummy") + +#define DEBUG qCDebug(deviceParDummy) +#define INFO qCInfo(deviceParDummy) +#define WARNING qCWarning(deviceParDummy) +#define CRITICAL qCCritical(deviceParDummy) +#define FATAL qCFatal(deviceParDummy) + // --------------------------------------------------------------------------- Dummy::Dummy(QObject *parent) : Device(parent), protected_(true) { @@ -41,6 +54,7 @@ Dummy::Dummy(QObject *parent) : Device(parent), protected_(true) { vddWr_ = 5.0f; vpp_ = 12.0f; vee_ = 12.0f; + DEBUG << info_.toString(); setSize(2048); } @@ -55,14 +69,17 @@ void Dummy::setSize(uint32_t value) { } bool Dummy::getId(TDeviceID &result) { + INFO << "Getting ID from device..."; canceling_ = false; result.manufacturer = 0x01; result.device = 0x01; emit onProgress(100, 100, true); + INFO << "Getting ID from device OK"; return true; } bool Dummy::read(QByteArray &buffer) { + INFO << "Reading device..."; canceling_ = false; int end = buffer_.size(); buffer.clear(); @@ -70,6 +87,9 @@ bool Dummy::read(QByteArray &buffer) { emit onProgress(i, end); if (canceling_) { emit onProgress(end, end, true, false, true); + INFO << QString("Read canceled at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); return false; } buffer.append(buffer_[i]); @@ -77,10 +97,12 @@ bool Dummy::read(QByteArray &buffer) { } Runner::usDelay(twc_); emit onProgress(end, end, true); + INFO << "Reading device OK"; return true; } bool Dummy::program(const QByteArray &buffer, bool verify) { + INFO << "Programming device..."; canceling_ = false; int end = qMin(buffer.size(), buffer_.size()); if (protected_) { @@ -91,6 +113,9 @@ bool Dummy::program(const QByteArray &buffer, bool verify) { emit onProgress(i, end); if (canceling_) { emit onProgress(end, end, true, false, true); + INFO << QString("Program canceled at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); return false; } if (skipFF_ && buffer[i] == (char)0xFF) continue; @@ -104,30 +129,43 @@ bool Dummy::program(const QByteArray &buffer, bool verify) { } else { emit onProgress(end, end, true); } + INFO << "Programming device OK"; return true; } bool Dummy::verify(const QByteArray &buffer) { + INFO << "Verifying device..."; canceling_ = false; int end = qMin(buffer.size(), buffer_.size()); for (int i = 0; i < end; ++i) { emit onProgress(i, end); if (canceling_) { emit onProgress(end, end, true, false, true); + INFO << QString("Verify canceled at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); return false; } if (buffer[i] != buffer_[i]) { emit onProgress(i, end, true, false); + WARNING << QString("Verify error at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(buffer[i], 2, 16, QChar('0')) + .arg(buffer_[i], 2, 16, QChar('0')); return false; } Runner::usDelay(twp_); } Runner::usDelay(twc_); emit onProgress(end, end, true); + INFO << "Verifying device OK"; return true; } bool Dummy::erase(bool check) { + INFO << "Erasing device..."; canceling_ = false; int end = buffer_.size(); if (protected_) { @@ -138,6 +176,9 @@ bool Dummy::erase(bool check) { emit onProgress(i, end); if (canceling_) { emit onProgress(end, end, true, false, true); + INFO << QString("Erase canceled at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); return false; } if (fastProg_ && buffer_[i] == (char)0xFF) continue; @@ -150,38 +191,53 @@ bool Dummy::erase(bool check) { } else { emit onProgress(end, end, true); } + INFO << "Erasing device OK"; return true; } bool Dummy::blankCheck() { + INFO << "Checking device..."; canceling_ = false; int end = buffer_.size(); for (int i = 0; i < end; ++i) { emit onProgress(i, end); if (canceling_) { emit onProgress(end, end, true, false, true); + INFO << QString("Blank Check canceled at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); return false; } if (buffer_[i] != (char)0xFF) { emit onProgress(i, end, true, false); + WARNING << QString("Blank Check error at 0x%1 of 0x%2") + .arg(i, 6, 16, QChar('0')) + .arg(end, 6, 16, QChar('0')); + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(0xFF, 2, 16, QChar('0')) + .arg(buffer_[i], 2, 16, QChar('0')); return false; } Runner::usDelay(twp_); } Runner::usDelay(twc_); emit onProgress(end, end, true); + INFO << "Checking device OK"; return true; } bool Dummy::unprotect() { + INFO << "Unprotecting device..."; canceling_ = false; if (!protected_) { emit onProgress(0, 100, true, false); + WARNING << "Error on Unprotect: Already unprotected"; return false; } protected_ = false; Runner::usDelay(twc_); emit onProgress(100, 100, true); + INFO << "Unprotecting device OK"; return true; } @@ -189,6 +245,7 @@ bool Dummy::unprotect() { Dummy16Bit::Dummy16Bit(QObject *parent) : Dummy(parent) { info_.name = "Dummy (16 bits)"; + DEBUG << info_.toString(); } Dummy16Bit::~Dummy16Bit() {} diff --git a/software/usbflashprog/backend/devices/parallel/eprom.cpp b/software/usbflashprog/backend/devices/parallel/eprom.cpp index d7cd9d08..77303a92 100644 --- a/software/usbflashprog/backend/devices/parallel/eprom.cpp +++ b/software/usbflashprog/backend/devices/parallel/eprom.cpp @@ -15,8 +15,20 @@ */ // --------------------------------------------------------------------------- +#include #include "backend/devices/parallel/eprom.hpp" +// --------------------------------------------------------------------------- +// Logging + +Q_LOGGING_CATEGORY(deviceParEprom, "device.parallel.eprom") + +#define DEBUG qCDebug(deviceParEprom) +#define INFO qCInfo(deviceParEprom) +#define WARNING qCWarning(deviceParEprom) +#define CRITICAL qCCritical(deviceParEprom) +#define FATAL qCFatal(deviceParEprom) + // --------------------------------------------------------------------------- EPROM::EPROM(QObject *parent) : SRAM(parent), mode16bit_(false) { @@ -39,15 +51,20 @@ EPROM::EPROM(QObject *parent) : SRAM(parent), mode16bit_(false) { size_ = 2048; maxAttemptsProg_ = 25; vppPulseDelay_ = 150; + DEBUG << info_.toString(); } EPROM::~EPROM() {} bool EPROM::read(QByteArray &buffer) { + INFO << "Reading device..."; canceling_ = false; int total = static_cast(size_); // Init pins/bus to Read operation - if (!initialize_(kDeviceOpRead)) return false; + if (!initialize_(kDeviceOpRead)) { + WARNING << "Error reading device"; + return false; + } buffer.clear(); uint16_t data = 0xFFFF; // Read entire device @@ -55,8 +72,10 @@ bool EPROM::read(QByteArray &buffer) { emit onProgress(current, total); if (canceling_) return finalize_(current, total, true, false, true); // Read byte - if (!read_(data) || runner_.hasError()) + if (!read_(data) || runner_.hasError()) { + WARNING << "Error reading device"; return finalize_(current, total, true, false); + } if (mode16bit_) { uint8_t msb = (data & 0xFF00) >> 8; uint8_t lsb = data & 0xFF; @@ -68,15 +87,20 @@ bool EPROM::read(QByteArray &buffer) { // Increment Address runner_.addrInc(); } + INFO << "Reading device OK"; // Close resources return finalize_(total, total, true); } bool EPROM::program(const QByteArray &buffer, bool verify) { + INFO << "Programming device..."; canceling_ = false; int total = qMin(static_cast(size_), buffer.size()); // Init pins/bus to Prog operation - if (!initialize_(kDeviceOpProg)) return false; + if (!initialize_(kDeviceOpProg)) { + return false; + WARNING << "Error programming device"; + } uint32_t current = 0; bool error = false; // Program the device @@ -95,7 +119,10 @@ bool EPROM::program(const QByteArray &buffer, bool verify) { runner_.addrClr(); initControlPins_(true); runner_.usDelay(initDelay_); - if (runner_.hasError()) return finalize_(0, total, true, false); + if (runner_.hasError()) { + WARNING << "Error programming device"; + return finalize_(0, total, true, false); + } current = 0; // Read the device and verify (compare) buffer if (!verify_(buffer, current, total)) error = true; @@ -103,14 +130,23 @@ bool EPROM::program(const QByteArray &buffer, bool verify) { if (!error) emit onProgress(total, total, true); // Close resources finalize_(); + if (error) { + WARNING << "Error programming device"; + } else { + INFO << "Programming device OK"; + } return !error; } bool EPROM::verify(const QByteArray &buffer) { + INFO << "Verifying device..."; canceling_ = false; int total = static_cast(size_); // Init pins/bus to Read operation - if (!initialize_(kDeviceOpRead)) return false; + if (!initialize_(kDeviceOpRead)) { + WARNING << "Error verifying device"; + return false; + } uint32_t current = 0; bool error = false; // Read the device and verify (compare) buffer @@ -118,6 +154,11 @@ bool EPROM::verify(const QByteArray &buffer) { if (!error) emit onProgress(total, total, true); // Close resources finalize_(); + if (error) { + WARNING << "Error verifying device"; + } else { + INFO << "Verifying device OK"; + } return !error; } @@ -129,10 +170,14 @@ bool EPROM::blankCheck() { } bool EPROM::getId(TDeviceID &result) { + INFO << "Getting ID from device..."; canceling_ = false; int total = static_cast(size_); // Init pins/bus to GetId operation - if (!initialize_(kDeviceOpGetId)) return false; + if (!initialize_(kDeviceOpGetId)) { + WARNING << "Error getting ID from device"; + return false; + } bool error = false; // Get manufacturer data (byte) result.manufacturer = runner_.dataGet(); @@ -142,11 +187,19 @@ bool EPROM::getId(TDeviceID &result) { // Get device data (byte) result.device = runner_.dataGet(); // Error - if (runner_.hasError()) return finalize_(0, total, true, false); + if (runner_.hasError()) { + WARNING << "Error getting ID from device"; + return finalize_(0, total, true, false); + } // Success if (!error) emit onProgress(total, total, true); // Close resources finalize_(); + if (error) { + WARNING << "Error getting ID from device"; + } else { + INFO << "Getting ID from device OK"; + } return (!error); } @@ -189,6 +242,7 @@ bool EPROM::isPgmCePin_() { } bool EPROM::resetBus_() { + DEBUG << "Reseting Bus..."; // VDD off and VPP off runner_.vddCtrl(false); runner_.vppCtrl(false); @@ -214,6 +268,11 @@ bool EPROM::resetBus_() { // Clear DataBus runner_.dataClr(); runner_.usDelay(resetBusDelay_); + if (runner_.hasError()) { + WARNING << "Error in Bus Reset"; + } else { + DEBUG << "Bus Reset OK"; + } return !runner_.hasError(); } @@ -274,8 +333,10 @@ bool EPROM::write_(uint16_t data) { } bool EPROM::initialize_(kDeviceOperation operation) { + DEBUG << "Initializing..."; if (!runner_.open(port_)) { emit onProgress(0, size_, true, false); + WARNING << "Error opening Serial Port."; return false; } // Init bus and pins @@ -317,6 +378,7 @@ bool EPROM::initialize_(kDeviceOperation operation) { } runner_.usDelay(initDelay_); if (runner_.hasError()) return finalize_(0, size_, true, false); + DEBUG << "Initialize OK"; return true; } @@ -348,6 +410,7 @@ void EPROM::initControlPins_(bool read) { bool EPROM::program_(const QByteArray &buffer, uint32_t ¤t, uint32_t total) { + DEBUG << "Programming data..."; uint16_t data, read = 0xFFFF; int increment = mode16bit_ ? 2 : 1; uint16_t empty = mode16bit_ ? 0xFFFF : 0xFF; @@ -356,6 +419,9 @@ bool EPROM::program_(const QByteArray &buffer, uint32_t ¤t, emit onProgress(current, total); if (canceling_) { emit onProgress(current, total, true, false, true); + DEBUG << QString("Program canceled at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); return false; } data = buffer[i] & 0xFF; @@ -376,6 +442,16 @@ bool EPROM::program_(const QByteArray &buffer, uint32_t ¤t, if (runner_.hasError() || j == maxAttemptsProg_ || data == empty) { // Error emit onProgress(current, total, true, false); + WARNING << QString("Program error at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + if (runner_.hasError()) { + WARNING << "Runner status is Error"; + } else { + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(data, 2, 16, QChar('0')) + .arg(read, 2, 16, QChar('0')); + } return false; } } @@ -383,17 +459,22 @@ bool EPROM::program_(const QByteArray &buffer, uint32_t ¤t, runner_.addrInc(); current += increment; } + DEBUG << "Program OK"; return true; } bool EPROM::verify_(const QByteArray &buffer, uint32_t ¤t, uint32_t total) { + DEBUG << "Verifying data..."; uint16_t data, read = 0xFFFF; int increment = mode16bit_ ? 2 : 1; for (int i = 0; i < buffer.size(); i += increment) { emit onProgress(current, total); if (canceling_) { emit onProgress(current, total, true, false, true); + DEBUG << QString("Verify canceled at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); return false; } data = buffer[i] & 0xFF; @@ -402,13 +483,24 @@ bool EPROM::verify_(const QByteArray &buffer, uint32_t ¤t, data |= (buffer[i + 1] & 0xFF); // LSB } read_(read); - runner_.addrInc(); if (runner_.hasError() || read != data) { emit onProgress(current, total, true, false); + WARNING << QString("Verify error at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + if (runner_.hasError()) { + WARNING << "Runner status is Error"; + } else { + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(data, 2, 16, QChar('0')) + .arg(read, 2, 16, QChar('0')); + } return false; } + runner_.addrInc(); current += increment; } + DEBUG << "Verify OK"; return true; } @@ -418,7 +510,7 @@ bool EPROM::unprotect() { // --------------------------------------------------------------------------- -M27xxx::M27xxx(QObject *parent) : EPROM(parent) { +EPROM27::EPROM27(QObject *parent) : EPROM(parent) { info_.name = "EPROM 27xxx"; twp_ = 50000; twc_ = 50; @@ -427,13 +519,14 @@ M27xxx::M27xxx(QObject *parent) : EPROM(parent) { vpp_ = 25.0f; vee_ = 12.0f; size_ = 2048; + DEBUG << info_.toString(); } -M27xxx::~M27xxx() {} +EPROM27::~EPROM27() {} // --------------------------------------------------------------------------- -M27Cxxx::M27Cxxx(QObject *parent) : EPROM(parent) { +EPROM27C::EPROM27C(QObject *parent) : EPROM(parent) { info_.name = "EPROM 27Cxxx"; twp_ = 500; twc_ = 8; @@ -442,22 +535,24 @@ M27Cxxx::M27Cxxx(QObject *parent) : EPROM(parent) { vpp_ = 13.0f; vee_ = 12.0f; size_ = 2048; + DEBUG << info_.toString(); } -M27Cxxx::~M27Cxxx() {} +EPROM27C::~EPROM27C() {} // --------------------------------------------------------------------------- -M27C16Bit::M27C16Bit(QObject *parent) : M27Cxxx(parent) { +EPROM27C16Bit::EPROM27C16Bit(QObject *parent) : EPROM27C(parent) { info_.name = "EPROM 27Cxxx (16Bit)"; mode16bit_ = true; + DEBUG << info_.toString(); } -M27C16Bit::~M27C16Bit() {} +EPROM27C16Bit::~EPROM27C16Bit() {} // --------------------------------------------------------------------------- -W27Exxx::W27Exxx(QObject *parent) : M27Cxxx(parent) { +EPROM27E::EPROM27E(QObject *parent) : EPROM27C(parent) { info_.name = "EPROM W27C/27E/27SF"; info_.capability.hasErase = true; info_.capability.hasBlankCheck = true; @@ -469,15 +564,20 @@ W27Exxx::W27Exxx(QObject *parent) : M27Cxxx(parent) { vee_ = 14.0f; size_ = 2048; erasePulseDelay_ = 100; // 100 ms + DEBUG << info_.toString(); } -W27Exxx::~W27Exxx() {} +EPROM27E::~EPROM27E() {} -bool W27Exxx::erase(bool check) { +bool EPROM27E::erase(bool check) { + INFO << "Erasing device..."; canceling_ = false; int total = static_cast(size_); // Init pins/bus to Prog operation - if (!initialize_(kDeviceOpErase)) return false; + if (!initialize_(kDeviceOpErase)) { + WARNING << "Error erasing device"; + return false; + } uint32_t current = 0; bool error = false; // Erase the device @@ -496,7 +596,10 @@ bool W27Exxx::erase(bool check) { runner_.addrClr(); initControlPins_(true); runner_.usDelay(initDelay_); - if (runner_.hasError()) return finalize_(0, total, true, false); + if (runner_.hasError()) { + WARNING << "Error erasing device"; + return finalize_(0, total, true, false); + } current = 0; // Creates a blank (0xFF) buffer QByteArray buffer(size_, 0xFF); @@ -506,14 +609,23 @@ bool W27Exxx::erase(bool check) { if (!error) emit onProgress(total, total, true); // Close resources finalize_(); + if (error) { + WARNING << "Error erasing device"; + } else { + INFO << "Erasing device OK"; + } return !error; } -bool W27Exxx::blankCheck() { +bool EPROM27E::blankCheck() { + INFO << "Checking device..."; canceling_ = false; int total = static_cast(size_); // Init pins/bus to Read operation - if (!initialize_(kDeviceOpRead)) return false; + if (!initialize_(kDeviceOpRead)) { + WARNING << "Error checking device"; + return false; + } uint32_t current = 0; bool error = false; // Creates a blank (0xFF) buffer @@ -523,16 +635,23 @@ bool W27Exxx::blankCheck() { if (!error) emit onProgress(total, total, true); // Close resources finalize_(); + if (error) { + WARNING << "Error checking device"; + } else { + INFO << "Checking device OK"; + } return !error; } -bool W27Exxx::initialize_(kDeviceOperation operation) { +bool EPROM27E::initialize_(kDeviceOperation operation) { if (operation != kDeviceOpErase) { - return M27Cxxx::initialize_(operation); + return EPROM27C::initialize_(operation); } // Operation == Erase + DEBUG << "Initializing..."; if (!runner_.open(port_)) { emit onProgress(0, size_, true, false); + WARNING << "Error opening Serial Port."; return false; } // Init bus and pins @@ -555,10 +674,12 @@ bool W27Exxx::initialize_(kDeviceOperation operation) { } runner_.usDelay(initDelay_); if (runner_.hasError()) return finalize_(0, size_, true, false); + DEBUG << "Initialize OK"; return true; } -bool W27Exxx::erase_(uint32_t ¤t, uint32_t total) { +bool EPROM27E::erase_(uint32_t ¤t, uint32_t total) { + DEBUG << "Erasing data..."; uint16_t read = 0xFFFF; int increment = mode16bit_ ? 2 : 1; uint16_t empty = mode16bit_ ? 0xFFFF : 0xFF; @@ -599,6 +720,13 @@ bool W27Exxx::erase_(uint32_t ¤t, uint32_t total) { if (isPgmCePin_()) runner_.setWE(); bool success = true; for (int i = 0; i < size_; i += increment) { + if (canceling_) { + emit onProgress(current, total, true, false, true); + DEBUG << QString("Erase canceled at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + return false; + } // Read byte read_(read); // Verify @@ -609,6 +737,16 @@ bool W27Exxx::erase_(uint32_t ¤t, uint32_t total) { if (runner_.hasError() || j == maxAttemptsProg_) { // Error emit onProgress(current, total, true, false); + WARNING << QString("Erase error at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + if (runner_.hasError()) { + WARNING << "Runner status is Error"; + } else { + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(0xFF, 2, 16, QChar('0')) + .arg(read, 2, 16, QChar('0')); + } return false; } // Increment Address @@ -617,5 +755,6 @@ bool W27Exxx::erase_(uint32_t ¤t, uint32_t total) { } if (success) break; } + DEBUG << "Erase OK"; return true; } diff --git a/software/usbflashprog/backend/devices/parallel/eprom.hpp b/software/usbflashprog/backend/devices/parallel/eprom.hpp index 6f7de487..38fcc548 100644 --- a/software/usbflashprog/backend/devices/parallel/eprom.hpp +++ b/software/usbflashprog/backend/devices/parallel/eprom.hpp @@ -107,7 +107,7 @@ class EPROM : public SRAM { * @details The purpose of this class is to program EPROM 27xxx Memories (NMOS). * @nosubgrouping */ -class M27xxx : public EPROM { +class EPROM27 : public EPROM { Q_OBJECT public: @@ -115,9 +115,9 @@ class M27xxx : public EPROM { * @brief Constructor. * @param parent Pointer to parent object. Default is nullptr. */ - explicit M27xxx(QObject *parent = nullptr); + explicit EPROM27(QObject *parent = nullptr); /** @brief Destructor. */ - virtual ~M27xxx(); + virtual ~EPROM27(); }; // --------------------------------------------------------------------------- @@ -129,7 +129,7 @@ class M27xxx : public EPROM { * (CMOS). * @nosubgrouping */ -class M27Cxxx : public EPROM { +class EPROM27C : public EPROM { Q_OBJECT public: @@ -137,9 +137,9 @@ class M27Cxxx : public EPROM { * @brief Constructor. * @param parent Pointer to parent object. Default is nullptr. */ - explicit M27Cxxx(QObject *parent = nullptr); + explicit EPROM27C(QObject *parent = nullptr); /** @brief Destructor. */ - virtual ~M27Cxxx(); + virtual ~EPROM27C(); }; // --------------------------------------------------------------------------- @@ -151,7 +151,7 @@ class M27Cxxx : public EPROM { * (CMOS) 16Bit. * @nosubgrouping */ -class M27C16Bit : public M27Cxxx { +class EPROM27C16Bit : public EPROM27C { Q_OBJECT public: @@ -159,9 +159,9 @@ class M27C16Bit : public M27Cxxx { * @brief Constructor. * @param parent Pointer to parent object. Default is nullptr. */ - explicit M27C16Bit(QObject *parent = nullptr); + explicit EPROM27C16Bit(QObject *parent = nullptr); /** @brief Destructor. */ - virtual ~M27C16Bit(); + virtual ~EPROM27C16Bit(); }; // --------------------------------------------------------------------------- @@ -173,7 +173,7 @@ class M27C16Bit : public M27Cxxx { * (Electrical Erasable). * @nosubgrouping */ -class W27Exxx : public M27Cxxx { +class EPROM27E : public EPROM27C { Q_OBJECT public: @@ -181,9 +181,9 @@ class W27Exxx : public M27Cxxx { * @brief Constructor. * @param parent Pointer to parent object. Default is nullptr. */ - explicit W27Exxx(QObject *parent = nullptr); + explicit EPROM27E(QObject *parent = nullptr); /** @brief Destructor. */ - virtual ~W27Exxx(); + virtual ~EPROM27E(); /* Reimplemented */ virtual bool erase(bool check = false); /* Reimplemented */ diff --git a/software/usbflashprog/backend/devices/parallel/sram.cpp b/software/usbflashprog/backend/devices/parallel/sram.cpp index 59580ee2..0cd7df5c 100644 --- a/software/usbflashprog/backend/devices/parallel/sram.cpp +++ b/software/usbflashprog/backend/devices/parallel/sram.cpp @@ -15,10 +15,22 @@ */ // --------------------------------------------------------------------------- -#include "backend/devices/parallel/sram.hpp" - #include #include +#include + +#include "backend/devices/parallel/sram.hpp" + +// --------------------------------------------------------------------------- +// Logging + +Q_LOGGING_CATEGORY(deviceParSram, "device.parallel.sram") + +#define DEBUG qCDebug(deviceParSram) +#define INFO qCInfo(deviceParSram) +#define WARNING qCWarning(deviceParSram) +#define CRITICAL qCCritical(deviceParSram) +#define FATAL qCFatal(deviceParSram) // --------------------------------------------------------------------------- @@ -34,6 +46,7 @@ SRAM::SRAM(QObject *parent) : Device(parent) { size_ = 2048; resetBusDelay_ = 25; // 25 uS initDelay_ = 30; // 30 uS + DEBUG << info_.toString(); } SRAM::~SRAM() {} @@ -41,9 +54,13 @@ SRAM::~SRAM() {} bool SRAM::program(const QByteArray &buffer, bool verify) { (void)buffer; (void)verify; + INFO << "Programming device..."; canceling_ = false; uint32_t current = 0, total = size_; - if (!initialize_(kDeviceOpRead)) return false; + if (!initialize_(kDeviceOpRead)) { + WARNING << "Error programming device"; + return false; + } bool error = false; if (!doPatternTest_() || !doRandomTest_()) error = true; if (!error && runner_.hasError()) { @@ -51,6 +68,11 @@ bool SRAM::program(const QByteArray &buffer, bool verify) { } if (!error) emit onProgress(total, total, true); finalize_(); + if (error) { + WARNING << "Error programming device"; + } else { + INFO << "Programming device OK"; + } return !error; } @@ -77,6 +99,7 @@ bool SRAM::doRandomTest_() { } bool SRAM::resetBus_() { + DEBUG << "Reseting Bus..."; runner_.vddCtrl(false); runner_.vppCtrl(false); runner_.addrClr(); @@ -90,6 +113,11 @@ bool SRAM::resetBus_() { runner_.vppOnWE(false); runner_.dataClr(); runner_.usDelay(resetBusDelay_); + if (runner_.hasError()) { + WARNING << "Error in Bus Reset"; + } else { + DEBUG << "Bus Reset OK"; + } return !runner_.hasError(); } @@ -111,48 +139,80 @@ bool SRAM::read_(uint8_t &data) { bool SRAM::program_(const QByteArray &buffer, uint32_t ¤t, uint32_t total) { + DEBUG << "Programming data..."; for (const uint8_t &data : buffer) { emit onProgress(current, total); if (canceling_) { emit onProgress(current, total, true, false, true); + DEBUG << QString("Program canceled at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); return false; } uint8_t read; write_(data); read_(read); - runner_.addrInc(); if (runner_.hasError() || read != data) { emit onProgress(current, total, true, false); + WARNING << QString("Program error at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + if (runner_.hasError()) { + WARNING << "Runner status is Error"; + } else { + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(data, 2, 16, QChar('0')) + .arg(read, 2, 16, QChar('0')); + } return false; } + runner_.addrInc(); current++; } + DEBUG << "Program OK"; return true; } bool SRAM::verify_(const QByteArray &buffer, uint32_t ¤t, uint32_t total) { + DEBUG << "Verifying data..."; for (const uint8_t &data : buffer) { emit onProgress(current, total); if (canceling_) { emit onProgress(current, total, true, false, true); + DEBUG << QString("Verify canceled at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); return false; } uint8_t read; read_(read); - runner_.addrInc(); if (runner_.hasError() || read != data) { emit onProgress(current, total, true, false); + WARNING << QString("Verify error at 0x%1 of 0x%2") + .arg(current, 6, 16, QChar('0')) + .arg(total, 6, 16, QChar('0')); + if (runner_.hasError()) { + WARNING << "Runner status is Error"; + } else { + WARNING << QString("Data to write 0x%1. Data read 0x%2") + .arg(data, 2, 16, QChar('0')) + .arg(read, 2, 16, QChar('0')); + } return false; } current++; + runner_.addrInc(); } + DEBUG << "Verify OK"; return true; } bool SRAM::initialize_(kDeviceOperation operation) { + DEBUG << "Initializing..."; if (!runner_.open(port_)) { emit onProgress(0, size_, true, false); + WARNING << "Error opening Serial Port."; return false; } resetBus_(); @@ -175,12 +235,15 @@ bool SRAM::initialize_(kDeviceOperation operation) { } runner_.usDelay(initDelay_); if (runner_.hasError()) return finalize_(0, size_, true, false); + DEBUG << "Initialize OK"; return true; } void SRAM::finalize_() { + DEBUG << "Finalizing..."; resetBus_(); runner_.close(); + DEBUG << "Finalize OK"; } bool SRAM::finalize_(uint32_t current, uint32_t total, bool done, bool success, @@ -191,6 +254,7 @@ bool SRAM::finalize_(uint32_t current, uint32_t total, bool done, bool success, } QByteArray SRAM::generateRandomData_() { + DEBUG << "Generating Random Data..."; QByteArray buffer(size_, 0); for (int i = 0; i < size_; ++i) { buffer[i] = static_cast(QRandomGenerator::global()->generate()); @@ -199,6 +263,7 @@ QByteArray SRAM::generateRandomData_() { } QByteArray SRAM::generatePatternData_() { + DEBUG << "Generating Pattern Data..."; QByteArray buffer(size_, 0); uint8_t data = 0x55; for (int i = 0; i < size_; ++i) { diff --git a/software/usbflashprog/backend/runner.cpp b/software/usbflashprog/backend/runner.cpp index 0bd45ae0..49dc0e1f 100644 --- a/software/usbflashprog/backend/runner.cpp +++ b/software/usbflashprog/backend/runner.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -24,6 +25,17 @@ #include "backend/runner.hpp" #include "config.hpp" +// --------------------------------------------------------------------------- +// Logging + +Q_LOGGING_CATEGORY(backendRunner, "backend.runner") + +#define DEBUG qCDebug(backendRunner) +#define INFO qCInfo(backendRunner) +#define WARNING qCWarning(backendRunner) +#define CRITICAL qCCritical(backendRunner) +#define FATAL qCFatal(backendRunner) + // --------------------------------------------------------------------------- #ifdef TEST_BUILD @@ -32,8 +44,10 @@ bool QSerialPort::connected = false; // --------------------------------------------------------------------------- -/* @brief Timeout (in milliseconds) to consider timeout. */ -constexpr int kReadTimeOut = 1000; +/* @brief Timeout (in milliseconds) to consider disconnect. */ +constexpr int kDisconnectTimeOut = 5000; +/* @brief Timeout (in milliseconds) to read byte. */ +constexpr int kReadTimeOut = 200; // --------------------------------------------------------------------------- @@ -119,7 +133,7 @@ bool operator==(const TRunnerCommand& a, const TRunnerCommand& b) { Runner::Runner(QObject* parent) : QObject(parent), serial_(this), - timeout_(200), + timeout_(kReadTimeOut), running_(false), error_(false), address_(0) {} @@ -152,6 +166,7 @@ TSerialPortList Runner::list() const { bool Runner::open(const QString& path) { if (running_) close(); if (path.isNull() || path.isEmpty()) return false; + DEBUG << "Opening serial port: " << path << "..."; serial_.setPortName(path); bool result = serial_.open(QIODevice::ReadWrite); if (result) { @@ -160,12 +175,19 @@ bool Runner::open(const QString& path) { // https://community.platformio.org/t/ // serial-communication-micro-usb-on-pi-pico-c/27512/5 serial_.setDataTerminalReady(true); + DEBUG << "Open serial port OK"; + DEBUG << "Setting timeout: " << QString("%1").arg(timeout_); + } else { + WARNING << "Error opening serial port"; } error_ = !result; return result; } void Runner::close() { + if (serial_.isOpen()) { + DEBUG << "Closing serial port..."; + } serial_.close(); running_ = false; error_ = false; @@ -190,421 +212,196 @@ uint32_t Runner::getTimeOut() const { void Runner::setTimeOut(uint32_t value) { if (timeout_ == value) return; timeout_ = value; + DEBUG << "Setting timeout: " << QString("%1").arg(value); } bool Runner::nop() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdNop); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vddCtrl(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVddCtrl, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vddSet(float value) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setFloat(kCmdVddSetV, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } float Runner::vddGet() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVddGetV); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } float Runner::vddGetDuty() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVddGetDuty); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } bool Runner::vddInitCal() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdVddInitCal); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vddSaveCal(float value) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setFloat(kCmdVddSaveCal, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } float Runner::vddGetCal() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVddGetCal); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } bool Runner::vppCtrl(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppCtrl, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppSet(float value) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setFloat(kCmdVppSetV, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } float Runner::vppGet() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVppGetV); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } float Runner::vppGetDuty() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVppGetDuty); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } bool Runner::vppInitCal() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdVppInitCal); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppSaveCal(float value) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setFloat(kCmdVppSaveCal, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } float Runner::vppGetCal() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return -1.0f; - } TRunnerCommand cmd; cmd.set(kCmdVppGetCal); - if (!write_(cmd.params)) return -1.0f; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return -1.0f; - if (!cmd.responseIsOk()) { - error_ = true; - return -1.0f; - } + if (!sendCommand_(cmd)) return -1.0f; return cmd.responseAsFloat(); } bool Runner::vddOnVpp(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVddOnVpp, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppOnA9(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppOnA9, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppOnA18(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppOnA18, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppOnCE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppOnCE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppOnOE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppOnOE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::vppOnWE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdVppOnWE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::setCE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdBusCE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::setOE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdBusOE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::setWE(bool on) { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setBool(kCmdBusWE, on); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::addrClr() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdBusAddrClr); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; address_ = 0; return true; } bool Runner::addrInc() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdBusAddrInc); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; + // no retry + if (!sendCommand_(cmd, 0)) { + DEBUG << "Error in addrInc(). Last address: " + << QString("0x%1").arg(address_, 6, 16, QChar('0')); + DEBUG << "Trying use addrSet(). New address: " + << QString("0x%1").arg(address_ + 1, 6, 16, QChar('0')); + // error, use addrSet + return addrSet(address_ + 1); } address_++; return true; @@ -612,54 +409,27 @@ bool Runner::addrInc() { bool Runner::addrSet(uint32_t value) { if (!value) return addrClr(); - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setDWord(kCmdBusAddrSet, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; address_ = value; return true; } bool Runner::addrSetB(uint8_t value) { if (!value) return addrClr(); - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setByte(kCmdBusAddrSetB, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; address_ = value; return true; } bool Runner::addrSetW(uint16_t value) { if (!value) return addrClr(); - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setWord(kCmdBusAddrSetW, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; address_ = value; return true; } @@ -669,84 +439,39 @@ uint32_t Runner::addrGet() const { } bool Runner::dataClr() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.set(kCmdBusDataClr); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::dataSet(uint8_t value) { if (!value) return dataClr(); - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setByte(kCmdBusDataSetB, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } bool Runner::dataSetW(uint16_t value) { if (!value) return dataClr(); - if (error_ || !serial_.isOpen()) { - error_ = true; - return false; - } TRunnerCommand cmd; cmd.setWord(kCmdBusDataSet, value); - if (!write_(cmd.params)) return false; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return false; - if (!cmd.responseIsOk()) { - error_ = true; - return false; - } + if (!sendCommand_(cmd)) return false; return true; } uint8_t Runner::dataGet() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return 0xff; - } TRunnerCommand cmd; cmd.set(kCmdBusDataGetB); - if (!write_(cmd.params)) return 0xff; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return 0xff; - if (!cmd.responseIsOk()) { - error_ = true; - return 0xff; - } + if (!sendCommand_(cmd)) return 0xFF; return cmd.responseAsByte(); } uint16_t Runner::dataGetW() { - if (error_ || !serial_.isOpen()) { - error_ = true; - return 0xffff; - } TRunnerCommand cmd; cmd.set(kCmdBusDataGet); - if (!write_(cmd.params)) return 0xffff; - if (!read_(&cmd.response, cmd.opcode.result + 1)) return 0xffff; - if (!cmd.responseIsOk()) { - error_ = true; - return 0xffff; - } + if (!sendCommand_(cmd)) return 0xFFFF; return cmd.responseAsWord(); } @@ -783,28 +508,54 @@ void Runner::msDelay(uint32_t value) { } while (elapsed < value); } -bool Runner::write_(const QByteArray& data) { - if (error_) return false; - if (data.isEmpty()) return true; - serial_.clear(); - serial_.write(data); - serial_.flush(); - if (!serial_.waitForBytesWritten(timeout_)) { - checkAlive_(); +bool Runner::sendCommand_(TRunnerCommand& cmd, int retry) { + if (!serial_.isOpen()) { + WARNING << "Error running command " << cmd.opcode.descr.c_str(); + error_ = true; + return false; + } + bool success = false; + for (int i = 0; i < (retry + 1); i++) { + if (!write_(cmd.params) || + !read_(&cmd.response, cmd.opcode.result + 1)) { + DEBUG << "Retrying. " + << "Command " << cmd.opcode.descr.c_str(); + continue; + } + error_ = false; + success = true; + break; + } + if (!success) { + if (cmd.opcode.code != kCmdBusAddrInc) { + WARNING << "Error writing to or reading from serial port. " + << "Command " << cmd.opcode.descr.c_str(); + } + error_ = true; + return false; + } + if (!cmd.responseIsOk()) { + WARNING << "Response NOK. " + << "Command " << cmd.opcode.descr.c_str(); error_ = true; return false; } return true; } +bool Runner::write_(const QByteArray& data) { + if (data.isEmpty()) return true; + serial_.clear(); + return serial_.write(data) == data.size(); +} + bool Runner::read_(QByteArray* data, uint32_t size) { - if (error_) return false; if (data == nullptr || !size) return true; data->clear(); while (data->size() < size) { if (!serial_.waitForReadyRead(timeout_)) { + DEBUG << "Error reading serial port: timeout"; checkAlive_(); - error_ = true; return false; } data->append(serial_.readAll()); @@ -812,7 +563,7 @@ bool Runner::read_(QByteArray* data, uint32_t size) { } if (data->size() > size) data->resize(size); if (data->size() != size) { - error_ = true; + DEBUG << "Error reading serial port: sizes are different"; return false; } return true; @@ -820,7 +571,8 @@ bool Runner::read_(QByteArray* data, uint32_t size) { void Runner::checkAlive_() { if (!running_) return; - if (QDateTime::currentMSecsSinceEpoch() - aliveTick_ > kReadTimeOut) { + if (QDateTime::currentMSecsSinceEpoch() - aliveTick_ > kDisconnectTimeOut) { + WARNING << "Serial port: disconnected by timeout"; running_ = false; if (serial_.isOpen()) serial_.close(); } diff --git a/software/usbflashprog/backend/runner.hpp b/software/usbflashprog/backend/runner.hpp index 89f769e8..7bdf4bf3 100644 --- a/software/usbflashprog/backend/runner.hpp +++ b/software/usbflashprog/backend/runner.hpp @@ -416,6 +416,11 @@ class Runner : public QObject { uint32_t address_; /* @brief Indicates if an error occurred in the last operation. */ bool error_; + /* @brief Sends the command. + * @param cmd Command to send (and receive response). + * @param retry Number of retry (default is 2). + * @return True if success, false otherwise. */ + bool sendCommand_(TRunnerCommand& cmd, int retry = 2); /* @brief Sends data via serial port. * @param data Data to send. * @return True if success, false otherwise. */ diff --git a/software/usbflashprog/config.hpp b/software/usbflashprog/config.hpp index 1483adb8..fe687c7e 100644 --- a/software/usbflashprog/config.hpp +++ b/software/usbflashprog/config.hpp @@ -20,8 +20,9 @@ // --------------------------------------------------------------------------- -#include #include +#include +#include // --------------------------------------------------------------------------- @@ -42,6 +43,12 @@ constexpr const char *kProjectHomePage = constexpr const char *kAuthorHomePage = "https://robsonmartins.com/"; /** @brief GENERAL : Author Name. */ constexpr const char *kAuthorName = "Robson Martins"; +/** @brief GENERAL : Organization Name. */ +constexpr const char *kOrganizationName = "RobsonMartins"; +/** @brief GENERAL : Organization Domain. */ +constexpr const char *kOrganizationDomain = "robsonmartins.com"; +/** @brief GENERAL : App Name. */ +constexpr const char *kApplicationName = "UsbFlashProg"; /** @brief COMM/USB : Vendor ID. */ constexpr quint16 kUsbVendorId = 0x2E8A; @@ -53,4 +60,98 @@ constexpr int kUsbEnumerateInterval = 2000; /** @brief COMM/USB : Interval to refresh communication, in milliseconds. */ constexpr int kUsbRefreshInterval = 500; +/** @brief GENERAL : Log filename. */ +constexpr const char *kLogFileName = "ufprog.log"; + +/** @brief SETTING : General / LogLevel. */ +constexpr const char *kSettingGeneralLogLevel = "LogLevel"; +/** @brief SETTING : General / Window Position. */ +constexpr const char *kSettingGeneralWindowPos = "WindowPos"; +/** @brief SETTING : General / Window Size. */ +constexpr const char *kSettingGeneralWindowSize = "WindowSize"; +/** @brief SETTING : General / Language. */ +constexpr const char *kSettingGeneralLanguage = "Language"; + +/** @brief SETTING : Programmer / Selected Device. */ +constexpr const char *kSettingProgDevice = "Prog/Device"; +/** @brief SETTING : Programmer / Device Size. */ +constexpr const char *kSettingProgDeviceSize = "Prog/DeviceSize"; +/** @brief SETTING : Programmer / tWP. */ +constexpr const char *kSettingProgTwp = "Prog/tWP"; +/** @brief SETTING : Programmer / tWC. */ +constexpr const char *kSettingProgTwc = "Prog/tWC"; +/** @brief SETTING : Programmer / VDD to Read. */ +constexpr const char *kSettingProgVddRd = "Prog/VDDToRead"; +/** @brief SETTING : Programmer / VDD to Prog. */ +constexpr const char *kSettingProgVddWr = "Prog/VDDToProg"; +/** @brief SETTING : Programmer / VPP. */ +constexpr const char *kSettingProgVpp = "Prog/VPP"; +/** @brief SETTING : Programmer / VEE. */ +constexpr const char *kSettingProgVee = "Prog/VEE"; +/** @brief SETTING : Programmer / Skip Prog 0xFF. */ +constexpr const char *kSettingProgSkipFF = "Prog/SkipFF"; +/** @brief SETTING : Programmer / Fast Prog/Erase. */ +constexpr const char *kSettingProgFast = "Prog/FastProg"; +/** @brief SETTING : Programmer / Sector Size. */ +constexpr const char *kSettingProgSectorSize = "Prog/SectorSize"; + +// --------------------------------------------------------------------------- +/** + * @ingroup Software + * @brief Stores the programmer settings. + */ +typedef struct TProgrammerSettings { + /** @brief Device name. */ + QString device; + /** @brief Device size in bytes. */ + uint32_t size; + /** @brief tWP value in microseconds. */ + uint32_t twp; + /** @brief tWC value in microseconds. */ + uint32_t twc; + /** @brief VDD to Read value in volts. */ + float vddRd; + /** @brief VDD to Prog value in volts. */ + float vddWr; + /** @brief VPP value in volts. */ + float vpp; + /** @brief VEE value in volts. */ + float vee; + /** @brief Skip Prog 0xFF. */ + bool skipFF; + /** @brief Fast Prog/Erase. */ + bool fastProg; + /** @brief Sector Size in bytes (0 is byte prog). */ + uint16_t sectorSize; +} TProgrammerSettings; + +/** + * @ingroup Software + * @brief Stores the application settings. + */ +typedef struct TApplicationSettings { + /** @brief The Log Level. + *
    + *
  • 0: Disable (or \< 0) - Default
  • + *
  • 1: Fatal
  • + *
  • 2: Critical
  • + *
  • 3: Warning
  • + *
  • 4: Info
  • + *
  • 5: Debug (or \> 5)
  • + *
+ */ + int logLevel; + /** @brief The coordinates of the main window. */ + QPoint windowPos; + /** @brief The size of the main window. */ + QSize windowSize; + /** @brief The language of the application. + * Empty is autodetect (default). */ + QString language; + /** @brief Programmer settings. */ + TProgrammerSettings prog; +} TApplicationSettings; + +// --------------------------------------------------------------------------- + #endif // CONFIG_HPP_ diff --git a/software/usbflashprog/i18n/ufprog_en_US.ts b/software/usbflashprog/i18n/ufprog_en_US.ts index 93ce3bf2..197e4870 100644 --- a/software/usbflashprog/i18n/ufprog_en_US.ts +++ b/software/usbflashprog/i18n/ufprog_en_US.ts @@ -525,5 +525,81 @@ Caution! Check the VDD, VPP and VEE voltages and the size of the device before running, otherwise you will damage it! Caution! Check the VDD, VPP and VEE voltages and the size of the device before running, otherwise you will damage it! + + Settings + Settings + + + Data rate: %1 + Data rate: %1 + + + Estimated remaining time: %1 + Estimated remaining time: %1 + + + %1 hour(s) + %1 hour(s) + + + + %1 minute(s) + %1 minute(s) + + + %1 second(s) + %1 second(s) + + + + SettingsDialog + + USB Flash/EPROM Programmer + USB Flash/EPROM Programmer + + + General + General + + + Language + Language + + + Autodetect + Autodetect + + + Log Level + Log Level + + + Disabled + Disabled + + + Fatal + Fatal + + + Critical + Critical + + + Warning + Warning + + + Info + Info + + + Debug + Debug + + + Note: The log file can be found at %1 + Note: The log file can be found at %1 + diff --git a/software/usbflashprog/i18n/ufprog_pt_BR.ts b/software/usbflashprog/i18n/ufprog_pt_BR.ts index 2b654a84..7a0fda06 100644 --- a/software/usbflashprog/i18n/ufprog_pt_BR.ts +++ b/software/usbflashprog/i18n/ufprog_pt_BR.ts @@ -525,5 +525,80 @@ Caution! Check the VDD, VPP and VEE voltages and the size of the device before running, otherwise you will damage it! Cuidado! Verifique as tensões de VDD, VPP e VEE e o tamanho do dispositivo antes de executar, sob pena de danificá-lo! + + Settings + Configurações + + + Data rate: %1 + Taxa de dados: %1 + + + Estimated remaining time: %1 + Tempo restante estimado: %1 + + + %1 hour(s) + %1 hora(s) + + + %1 minute(s) + %1 minuto(s) + + + %1 second(s) + %1 segundo(s) + + + + SettingsDialog + + USB Flash/EPROM Programmer + USB Flash/EPROM Programmer + + + General + Geral + + + Language + Idioma + + + Autodetect + Autodetectar + + + Log Level + Nível de Log + + + Disabled + Desabilitado + + + Fatal + Fatal + + + Critical + Crítico + + + Warning + Alerta + + + Info + Informação + + + Debug + Depuração + + + Note: The log file can be found at %1 + Nota: O arquivo de log pode ser encontrado em %1 + diff --git a/software/usbflashprog/main.cpp b/software/usbflashprog/main.cpp index 909f264a..87cb508d 100644 --- a/software/usbflashprog/main.cpp +++ b/software/usbflashprog/main.cpp @@ -1,16 +1,11 @@ // --------------------------------------------------------------------------- // USB EPROM/Flash Programmer // -// Copyright (2022) Robson Martins +// Copyright (2024) Robson Martins // // This work is licensed under a Creative Commons Attribution-NonCommercial- // ShareAlike 4.0 International License. // --------------------------------------------------------------------------- -/** - * @defgroup Software Software Project - * @brief Software project for USB EPROM/Flash Programmer. - */ -// --------------------------------------------------------------------------- /** * @ingroup Software * @file main.cpp @@ -24,13 +19,19 @@ #include #include #include +#include +#include +#include +#include +#include +#include + #include -#include "config.hpp" -#include "main/mainwindow.hpp" +#include "main.hpp" +#include "ui/mainwindow.hpp" // --------------------------------------------------------------------------- - /** * @brief Main routine. * @param argc Number of arguments. @@ -48,6 +49,14 @@ int main(int argc, char *argv[]); */ QMainWindow *createWindow(const int &argc, char *argv[]); +// --------------------------------------------------------------------------- +// Global + +/* @brief Qt Base Translator. */ +QTranslator *baseTranslator = nullptr; +/* @brief Custom Translator. */ +QTranslator *customTranslator = nullptr; + // --------------------------------------------------------------------------- QMainWindow *createWindow(const int &argc, char *argv[]) { @@ -56,35 +65,169 @@ QMainWindow *createWindow(const int &argc, char *argv[]) { return new MainWindow(); } -int main(int argc, char *argv[]) { -#ifdef Q_OS_LINUX - setenv("XDG_SESSION_TYPE", "x11", 1); -#endif - QApplication a(argc, argv); - a.setOrganizationName("RobsonMartins"); - a.setOrganizationDomain("robsonmartins.com"); - a.setApplicationName("UsbFlashProg"); - QTranslator translator, baseTranslator; - const QStringList uiLanguages = QLocale::system().uiLanguages(); - // translate Qt base strings - for (const QString &locale : uiLanguages) { - const QString baseName = "qtbase_" + QLocale(locale).name(); - if (baseTranslator.load( - baseName, - QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { - a.installTranslator(&baseTranslator); +TApplicationSettings loadSettings() { + TApplicationSettings result; + QSettings settings; + result.logLevel = + settings.value(kSettingGeneralLogLevel).toString().toInt(); + result.language = settings.value(kSettingGeneralLanguage).toString(); + result.windowPos = settings.value(kSettingGeneralWindowPos).toPoint(); + result.windowSize = settings.value(kSettingGeneralWindowSize).toSize(); + return result; +} + +void initLogging(int level) { + // logging + QString rules; + QString strType; + switch (level) { + case 1: // Fatal + rules = "*.fatal=true"; + strType = "FATAL"; + break; + case 2: // Critical + rules = "*.critical=true"; + strType = "CRITICAL"; + break; + case 3: // Warning + rules = "*.warning=true"; + strType = "WARNING"; + break; + case 4: // Info + rules = "*.warning=true\nbackend.*.info=true\ndevice.*.info=true"; + strType = "INFO"; break; + case 5: // Debug + default: + if (level >= 5) { + rules = + "*.info=true\nbackend.*.debug=true\ndevice.*.debug=true"; + strType = "DEBUG"; + } + break; + } + QFile logFile(QDir::homePath() + "/" + QString(kLogFileName)); + logFile.remove(); + // Disabled + if (level <= 0) return; + QLoggingCategory::setFilterRules(rules); + qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &context, + const QString &msg) { + QString localMsg = QString(msg.toLocal8Bit()); + localMsg.remove('"'); + QString strType; + switch (type) { + case QtFatalMsg: + strType = "FATAL"; + break; + case QtCriticalMsg: + strType = "CRITICAL"; + break; + case QtWarningMsg: + strType = "WARNING"; + break; + case QtInfoMsg: + strType = "INFO"; + break; + case QtDebugMsg: + default: + strType = "DEBUG"; + break; } + QString timeStamp = + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); + QString txt = QString("%1 %2 %3 - %4") + .arg(timeStamp) + .arg("[" + QString(context.category) + "]", -25) + .arg(strType, -8) + .arg(localMsg); + QFile logFile(QDir::homePath() + "/" + QString(kLogFileName)); + logFile.open(QIODevice::WriteOnly | QIODevice::Append); + QTextStream ts(&logFile); + ts << txt << endl; + }); + qInfo() << "Setting Log Level: " << strType; +} + +void loadLanguage(const QString &language, QApplication *app) { + // remove current translators + if (customTranslator) { + app->removeTranslator(customTranslator); + delete customTranslator; } - // translate application custom strings - for (const QString &locale : uiLanguages) { - const QString baseName = "ufprog_" + QLocale(locale).name(); - if (translator.load(":/i18n/" + baseName)) { - a.installTranslator(&translator); - break; + if (baseTranslator) { + app->removeTranslator(baseTranslator); + delete baseTranslator; + } + customTranslator = new QTranslator(app); + baseTranslator = new QTranslator(app); + bool baseOk = false, customOk = false; + QString localeName = language; + if (localeName.isEmpty()) { + // autodetect + const QStringList uiLanguages = QLocale::system().uiLanguages(); + // translate Qt base strings + for (const QString &locale : uiLanguages) { + localeName = QLocale(locale).name(); + const QString baseName = "qtbase_" + localeName; + if (baseTranslator->load( + baseName, + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + baseOk = true; + break; + } + } + // translate application custom strings + for (const QString &locale : uiLanguages) { + localeName = QLocale(locale).name(); + const QString customName = "ufprog_" + localeName; + if (customTranslator->load(":/i18n/" + customName)) { + customOk = true; + break; + } + } + } else { + // load specific language + // translate Qt base strings + const QString baseName = "qtbase_" + localeName; + if (baseTranslator->load( + baseName, + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + baseOk = true; } + // translate application custom strings + const QString customName = "ufprog_" + localeName; + if (customTranslator->load(":/i18n/" + customName)) { + customOk = true; + } + } + if (baseOk) { + app->installTranslator(baseTranslator); + qInfo() << "Installed Qt Base translator for language: " << localeName; + } + if (customOk) { + app->installTranslator(customTranslator); + qInfo() << "Installed Custom translator for language: " << localeName; } - QScopedPointer w(createWindow(argc, argv)); - w->show(); - return a.exec(); +} + +int main(int argc, char *argv[]) { +#ifdef Q_OS_LINUX + setenv("XDG_SESSION_TYPE", "x11", 1); +#endif + // create app + QApplication app(argc, argv); + app.setOrganizationName(kOrganizationName); + app.setOrganizationDomain(kOrganizationDomain); + app.setApplicationName(kApplicationName); + // load settings + TApplicationSettings settings = loadSettings(); + // setup the logger + initLogging(settings.logLevel); + // setup translator + loadLanguage(settings.language, &app); + // main window + QScopedPointer window(createWindow(argc, argv)); + window->show(); + return app.exec(); } diff --git a/software/usbflashprog/main.hpp b/software/usbflashprog/main.hpp new file mode 100644 index 00000000..e21744af --- /dev/null +++ b/software/usbflashprog/main.hpp @@ -0,0 +1,52 @@ +// --------------------------------------------------------------------------- +// USB EPROM/Flash Programmer +// +// Copyright (2024) Robson Martins +// +// This work is licensed under a Creative Commons Attribution-NonCommercial- +// ShareAlike 4.0 International License. +// --------------------------------------------------------------------------- +/** + * @defgroup Software Software Project + * @brief Software project for USB EPROM/Flash Programmer. + */ +// --------------------------------------------------------------------------- +/** + * @ingroup Software + * @file main.hpp + * @brief Header of the Main Routine. + * + * @author Robson Martins (https://www.robsonmartins.com) + */ +// --------------------------------------------------------------------------- + +#ifndef MAIN_HPP_ +#define MAIN_HPP_ + +#include "config.hpp" + +// --------------------------------------------------------------------------- + +/** + * @brief Loads application settings. + * @return Application settings. + */ +TApplicationSettings loadSettings(); + +/** + * @brief Initializes the logging feature. + * @param level The log level (\<= 0 is disabled, 1 is FATAL, 2 is CRITICAL, + * etc). + */ +void initLogging(int level); + +/** + * @brief Loads the language strings. + * @param language The language (en_US, or another). + * @param app Pointer to the QApplication instance. + */ +void loadLanguage(const QString &language, QApplication *app); + +// --------------------------------------------------------------------------- + +#endif // MAIN_HPP_ diff --git a/software/usbflashprog/test/backend/chip_test.cpp b/software/usbflashprog/test/backend/chip_test.cpp index bd931b46..65cf01f2 100644 --- a/software/usbflashprog/test/backend/chip_test.cpp +++ b/software/usbflashprog/test/backend/chip_test.cpp @@ -163,7 +163,7 @@ TEST_F(ChipTest, sram_test) { TEST_F(ChipTest, eprom27_test) { ChipEPROM *emuChip = new ChipEPROM(); Emulator::setChip(emuChip); - M27xxx *device = new M27xxx(); + EPROM27 *device = new EPROM27(); runChipTests(emuChip, device, 0x000800); // 2KB runChipTests(emuChip, device, 0x001000); // 4KB runChipTests(emuChip, device, 0x002000); // 8KB @@ -174,7 +174,7 @@ TEST_F(ChipTest, eprom27_test) { TEST_F(ChipTest, eprom27C_test) { ChipEPROM *emuChip = new ChipEPROM(); Emulator::setChip(emuChip); - M27Cxxx *device = new M27Cxxx(); + EPROM27C *device = new EPROM27C(); runChipTests(emuChip, device, 0x000800); // 2KB runChipTests(emuChip, device, 0x001000); // 4KB runChipTests(emuChip, device, 0x002000); // 8KB @@ -185,7 +185,7 @@ TEST_F(ChipTest, eprom27C_test) { TEST_F(ChipTest, eprom27C16Bit_test) { ChipEPROM *emuChip = new ChipEPROM(); Emulator::setChip(emuChip); - M27C16Bit *device = new M27C16Bit(); + EPROM27C16Bit *device = new EPROM27C16Bit(); runChipTests(emuChip, device, 0x000800); // 2KB runChipTests(emuChip, device, 0x001000); // 4KB runChipTests(emuChip, device, 0x002000); // 8KB @@ -197,7 +197,7 @@ TEST_F(ChipTest, eprom27C16Bit_test) { TEST_F(ChipTest, epromW27E_test) { ChipEPROM *emuChip = new ChipEPROM(); Emulator::setChip(emuChip); - W27Exxx *device = new W27Exxx(); + EPROM27E *device = new EPROM27E(); runChipTests(emuChip, device, 0x000800); // 2KB runChipTests(emuChip, device, 0x001000); // 4KB runChipTests(emuChip, device, 0x002000); // 8KB diff --git a/software/usbflashprog/main/mainwindow.cpp b/software/usbflashprog/ui/mainwindow.cpp similarity index 90% rename from software/usbflashprog/main/mainwindow.cpp rename to software/usbflashprog/ui/mainwindow.cpp index 6b7acedc..4f1f46ad 100644 --- a/software/usbflashprog/main/mainwindow.cpp +++ b/software/usbflashprog/ui/mainwindow.cpp @@ -8,7 +8,7 @@ // --------------------------------------------------------------------------- /** * @ingroup Software - * @file main/mainwindow.cpp + * @file ui/mainwindow.cpp * @brief Implementation of the Main Window Class. * * @author Robson Martins (https://www.robsonmartins.com) @@ -39,10 +39,11 @@ #include #include "mainwindow.hpp" -#include "config.hpp" #include "./ui_mainwindow.h" -#include "backend/opcodes.hpp" +#include "config.hpp" +#include "settings.hpp" +#include "backend/opcodes.hpp" #include "backend/devices/parallel/dummy.hpp" #include "backend/devices/parallel/sram.hpp" #include "backend/devices/parallel/eprom.hpp" @@ -52,29 +53,6 @@ /* @brief Minimum length of dialog labels, in characters. */ constexpr int kDialogLabelMinLength = 80; -/* @brief Setting: Programmer / Selected Device. */ -constexpr const char *kSettingProgDevice = "Prog/Device"; -/* @brief Setting: Programmer / Device Size. */ -constexpr const char *kSettingProgDeviceSize = "Prog/DeviceSize"; -/* @brief Setting: Programmer / tWP. */ -constexpr const char *kSettingProgTwp = "Prog/tWP"; -/* @brief Setting: Programmer / tWC. */ -constexpr const char *kSettingProgTwc = "Prog/tWC"; -/* @brief Setting: Programmer / VDD to Read. */ -constexpr const char *kSettingProgVddRd = "Prog/VDDToRead"; -/* @brief Setting: Programmer / VDD to Prog. */ -constexpr const char *kSettingProgVddWr = "Prog/VDDToProg"; -/* @brief Setting: Programmer / VPP. */ -constexpr const char *kSettingProgVpp = "Prog/VPP"; -/* @brief Setting: Programmer / VEE. */ -constexpr const char *kSettingProgVee = "Prog/VEE"; -/* @brief Setting: Programmer / Skip Prog 0xFF. */ -constexpr const char *kSettingProgSkipFF = "Prog/SkipFF"; -/* @brief Setting: Programmer / Fast Prog/Erase. */ -constexpr const char *kSettingProgFast = "Prog/FastProg"; -/* @brief Setting: Programmer / Sector Size. */ -constexpr const char *kSettingProgSectorSize = "Prog/SectorSize"; - /* @brief CRC32 table for calculation. */ constexpr uint32_t kCRC32Table[] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, @@ -165,6 +143,7 @@ MainWindow::MainWindow(QWidget *parent) ui_->frameEditor->layout()->setAlignment(Qt::AlignLeft); ui_->frameEditor->layout()->addWidget(hexeditor_); ui_->frameEditor->layout()->setContentsMargins(0, 0, 20, 20); + hexeditor_->setSize(2048); ui_->actionSave->setEnabled(false); connectSignals_(); @@ -174,11 +153,35 @@ MainWindow::MainWindow(QWidget *parent) checksumLabel_ = new QLabel(); ui_->statusbar->addPermanentWidget(checksumLabel_); - loadSettings_(); + // restore window position and size + QSettings settings; + TApplicationSettings app; + app.windowPos = settings.value(kSettingGeneralWindowPos).toPoint(); + app.windowSize = settings.value(kSettingGeneralWindowSize).toSize(); + if (app.windowPos.x() > 0 || app.windowPos.y() > 0 || + app.windowSize.width() > 0 || app.windowSize.height() > 0) { + qDebug() << "Moving and resizing main window (" + << "left = " << app.windowPos.x() + << ", top = " << app.windowPos.y() + << ", width = " << app.windowSize.width() + << ", height = " << app.windowSize.height() << ")"; + move(app.windowPos); + resize(app.windowSize); + } + + loadProgSettings_(); updateCheckSum_(); } MainWindow::~MainWindow() { + // save window position and size + QSettings settings; + TApplicationSettings app; + app.windowPos = pos(); + app.windowSize = size(); + settings.setValue(kSettingGeneralWindowPos, app.windowPos); + settings.setValue(kSettingGeneralWindowSize, app.windowSize); + delete progress_; if (device_) { device_->disconnect(); @@ -279,6 +282,14 @@ void MainWindow::on_actionAuthorHome_triggered(bool checked) { QDesktopServices::openUrl(QUrl(kAuthorHomePage)); } +void MainWindow::on_actionSettings_triggered(bool checked) { + SettingsDialog dialog(this); + if (dialog.exec() == QDialog::Accepted) { + ui_->retranslateUi(this); + loadProgSettings_(); + } +} + void MainWindow::closeEvent(QCloseEvent *event) { static bool readyToClose = false; static bool closing = false; @@ -323,14 +334,20 @@ void MainWindow::onSelectDeviceTriggered(bool checked) { ui_->btnProgDevice->setText(action->text()); createDevice_(); configureProgControls_(); - saveSettings_(); + saveProgSettings_(); hexeditor_->fill(0xFF); noWarningDevice_ = false; } +#include + void MainWindow::onActionProgress(uint32_t current, uint32_t total, bool done, bool success, bool canceled) { - if (!current) progress_->setRange(current, total); + static qint64 startTime = 0; + if (!current) { + progress_->setRange(current, total); + startTime = QDateTime::currentMSecsSinceEpoch(); + } if (canceled) { progress_->setValue(total); QMessageBox::warning( @@ -349,12 +366,18 @@ void MainWindow::onActionProgress(uint32_t current, uint32_t total, bool done, .arg(QString("%1").arg(current, 6, 16, QChar('0')).toUpper()) .leftJustified(kDialogLabelMinLength)); } else { + qint64 elapsedTime = QDateTime::currentMSecsSinceEpoch() - startTime; progress_->setValue(current); progress_->setLabelText( tr("Processing address 0x%1 of 0x%2") .arg(QString("%1").arg(current, 6, 16, QChar('0')).toUpper()) .arg( - QString("%1").arg(total - 1, 6, 16, QChar('0')).toUpper())); + QString("%1").arg(total - 1, 6, 16, QChar('0')).toUpper()) + + "\n\n" + + tr("Data rate: %1").arg(calculateDataRate_(elapsedTime, current)) + + " | " + + tr("Estimated remaining time: %1") + .arg(calculateRemainingTime_(elapsedTime, current, total))); } } @@ -492,39 +515,39 @@ void MainWindow::on_btnUnprotect_clicked() { } void MainWindow::on_spinBoxProgTWP_valueChanged(int value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_spinBoxProgTWC_valueChanged(int value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_spinBoxProgVDDrd_valueChanged(double value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_spinBoxProgVDDwr_valueChanged(double value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_spinBoxProgVPP_valueChanged(double value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_spinBoxProgVEE_valueChanged(double value) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_checkBoxProgSkipFF_toggled(bool checked) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_checkBoxProgFast_toggled(bool checked) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_comboBoxProgSectorSize_currentIndexChanged(int index) { - saveSettings_(); + saveProgSettings_(); } void MainWindow::on_comboBoxProgSize_currentIndexChanged(int index) { @@ -537,71 +560,77 @@ void MainWindow::on_comboBoxProgSize_currentIndexChanged(int index) { device_->setSize(size); configureProgControls_(); hexeditor_->fill(0xFF); - saveSettings_(); + saveProgSettings_(); } } -void MainWindow::loadSettings_() { +void MainWindow::loadProgSettings_() { QSettings settings; - QString device = settings.value(kSettingProgDevice).toString(); - uint32_t size = settings.value(kSettingProgDeviceSize).toString().toUInt(); - uint32_t twp = settings.value(kSettingProgTwp).toString().toUInt(); - uint32_t twc = settings.value(kSettingProgTwc).toString().toUInt(); - float vddRd = settings.value(kSettingProgVddRd).toString().toFloat(); - float vddWr = settings.value(kSettingProgVddWr).toString().toFloat(); - float vpp = settings.value(kSettingProgVpp).toString().toFloat(); - float vee = settings.value(kSettingProgVee).toString().toFloat(); - bool skipFF = settings.value(kSettingProgSkipFF).toString().toInt() != 0; - bool fastProg = settings.value(kSettingProgFast).toString().toInt() != 0; - uint16_t sectorSize = + TProgrammerSettings prog; + + prog.device = settings.value(kSettingProgDevice).toString(); + prog.size = settings.value(kSettingProgDeviceSize).toString().toUInt(); + prog.twp = settings.value(kSettingProgTwp).toString().toUInt(); + prog.twc = settings.value(kSettingProgTwc).toString().toUInt(); + prog.vddRd = settings.value(kSettingProgVddRd).toString().toFloat(); + prog.vddWr = settings.value(kSettingProgVddWr).toString().toFloat(); + prog.vpp = settings.value(kSettingProgVpp).toString().toFloat(); + prog.vee = settings.value(kSettingProgVee).toString().toFloat(); + prog.skipFF = settings.value(kSettingProgSkipFF).toString().toInt() != 0; + prog.fastProg = settings.value(kSettingProgFast).toString().toInt() != 0; + prog.sectorSize = settings.value(kSettingProgSectorSize).toString().toUInt(); - if (!device.isEmpty()) { - ui_->btnProgDevice->setText(device); + + if (!prog.device.isEmpty()) { + ui_->btnProgDevice->setText(prog.device); createDevice_(); - device_->setSize(size); - device_->setTwp(twp); - device_->setTwc(twc); - device_->setVddRd(vddRd); - device_->setVddWr(vddWr); - device_->setVpp(vpp); - device_->setVee(vee); - device_->setSkipFF(skipFF); - device_->setFastProg(fastProg); - device_->setSectorSize(sectorSize); + device_->setSize(prog.size); + device_->setTwp(prog.twp); + device_->setTwc(prog.twc); + device_->setVddRd(prog.vddRd); + device_->setVddWr(prog.vddWr); + device_->setVpp(prog.vpp); + device_->setVee(prog.vee); + device_->setSkipFF(prog.skipFF); + device_->setFastProg(prog.fastProg); + device_->setSectorSize(prog.sectorSize); } configureProgControls_(); } -void MainWindow::saveSettings_() { +void MainWindow::saveProgSettings_() { QSettings settings; - QString device = ui_->btnProgDevice->text(); - settings.setValue(kSettingProgDevice, device); - uint32_t size = static_cast( + TProgrammerSettings prog; + + prog.device = ui_->btnProgDevice->text(); + prog.size = static_cast( ceil(powf(2, ui_->comboBoxProgSize->currentIndex()) * 2048.0f)); - settings.setValue(kSettingProgDeviceSize, QString::number(size)); - uint32_t twp = ui_->spinBoxProgTWP->value(); - if (ui_->comboBoxProgTWPUnit->currentIndex() == 1) twp *= 1000; - settings.setValue(kSettingProgTwp, QString::number(twp)); - uint32_t twc = ui_->spinBoxProgTWC->value(); - if (ui_->comboBoxProgTWCUnit->currentIndex() == 1) twc *= 1000; - settings.setValue(kSettingProgTwc, QString::number(twc)); - float vddRd = ui_->spinBoxProgVDDrd->value(); - settings.setValue(kSettingProgVddRd, QString::number(vddRd)); - float vddWr = ui_->spinBoxProgVDDwr->value(); - settings.setValue(kSettingProgVddWr, QString::number(vddWr)); - float vpp = ui_->spinBoxProgVPP->value(); - settings.setValue(kSettingProgVpp, QString::number(vpp)); - float vee = ui_->spinBoxProgVEE->value(); - settings.setValue(kSettingProgVee, QString::number(vee)); - int skipFF = ui_->checkBoxProgSkipFF->isChecked() ? 1 : 0; - settings.setValue(kSettingProgSkipFF, QString::number(skipFF)); - int fastProg = ui_->checkBoxProgFast->isChecked() ? 1 : 0; - settings.setValue(kSettingProgFast, QString::number(fastProg)); - uint16_t sectorSize = 0; + prog.twp = ui_->spinBoxProgTWP->value(); + if (ui_->comboBoxProgTWPUnit->currentIndex() == 1) prog.twp *= 1000; + prog.twc = ui_->spinBoxProgTWC->value(); + if (ui_->comboBoxProgTWCUnit->currentIndex() == 1) prog.twc *= 1000; + prog.vddRd = ui_->spinBoxProgVDDrd->value(); + prog.vddWr = ui_->spinBoxProgVDDwr->value(); + prog.vpp = ui_->spinBoxProgVPP->value(); + prog.vee = ui_->spinBoxProgVEE->value(); + prog.skipFF = ui_->checkBoxProgSkipFF->isChecked(); + prog.fastProg = ui_->checkBoxProgFast->isChecked(); + prog.sectorSize = 0; if (ui_->comboBoxProgSectorSize->currentIndex() != 0) { - sectorSize = ui_->comboBoxProgSectorSize->currentText().toUInt(); + prog.sectorSize = ui_->comboBoxProgSectorSize->currentText().toUInt(); } - settings.setValue(kSettingProgSectorSize, QString::number(sectorSize)); + + settings.setValue(kSettingProgDevice, prog.device); + settings.setValue(kSettingProgDeviceSize, QString::number(prog.size)); + settings.setValue(kSettingProgTwp, QString::number(prog.twp)); + settings.setValue(kSettingProgTwc, QString::number(prog.twc)); + settings.setValue(kSettingProgVddRd, QString::number(prog.vddRd)); + settings.setValue(kSettingProgVddWr, QString::number(prog.vddWr)); + settings.setValue(kSettingProgVpp, QString::number(prog.vpp)); + settings.setValue(kSettingProgVee, QString::number(prog.vee)); + settings.setValue(kSettingProgSkipFF, QString::number(prog.skipFF ? 1 : 0)); + settings.setValue(kSettingProgFast, QString::number(prog.fastProg ? 1 : 0)); + settings.setValue(kSettingProgSectorSize, QString::number(prog.sectorSize)); } void MainWindow::createDevice_() { @@ -654,6 +683,7 @@ void MainWindow::createDeviceIfSRAM_(const QString &label) { device_->setSize(size); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(custom); + ui_->labelProgSize->setEnabled(custom); } } @@ -695,10 +725,11 @@ void MainWindow::createDeviceIfEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new M27xxx(this); + device_ = new EPROM27(this); device_->setSize(size); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(custom); + ui_->labelProgSize->setEnabled(custom); } size = 0x800; found = false; @@ -741,10 +772,11 @@ void MainWindow::createDeviceIfEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new M27Cxxx(this); + device_ = new EPROM27C(this); device_->setSize(size); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(custom); + ui_->labelProgSize->setEnabled(custom); } size = 0x800; found = false; @@ -775,10 +807,11 @@ void MainWindow::createDeviceIfEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new M27C16Bit(this); + device_ = new EPROM27C16Bit(this); device_->setSize(size); hexeditor_->setMode(QHexEditor::Mode16Bits); ui_->comboBoxProgSize->setEnabled(custom); + ui_->labelProgSize->setEnabled(custom); } } @@ -814,13 +847,14 @@ void MainWindow::createDeviceIfErasableEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new W27Exxx(this); + device_ = new EPROM27E(this); device_->setSize(size); device_->setVpp(vpp); device_->setVee(vee); device_->setVddWr(vdd); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(custom); + ui_->labelProgSize->setEnabled(custom); } found = false; vpp = 12.0f; @@ -846,13 +880,14 @@ void MainWindow::createDeviceIfErasableEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new W27Exxx(this); + device_ = new EPROM27E(this); device_->setSize(size); device_->setVpp(vpp); device_->setVee(vee); device_->setVddWr(vdd); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(false); + ui_->labelProgSize->setEnabled(false); } found = false; vpp = 12.75f; @@ -873,13 +908,14 @@ void MainWindow::createDeviceIfErasableEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new W27Exxx(this); + device_ = new EPROM27E(this); device_->setSize(size); device_->setVpp(vpp); device_->setVee(vee); device_->setVddWr(vdd); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(false); + ui_->labelProgSize->setEnabled(false); } found = false; vpp = 12.0f; @@ -902,7 +938,7 @@ void MainWindow::createDeviceIfErasableEPROM_(const QString &label) { if (found) { ui_->actionDoProgram->setText(tr("Program")); ui_->btnProgram->setToolTip(ui_->actionDoProgram->text()); - device_ = new W27Exxx(this); + device_ = new EPROM27E(this); device_->setSize(size); device_->setVpp(vpp); device_->setVee(vee); @@ -910,6 +946,7 @@ void MainWindow::createDeviceIfErasableEPROM_(const QString &label) { device_->setVddWr(vdd); hexeditor_->setMode(QHexEditor::Mode8Bits); ui_->comboBoxProgSize->setEnabled(false); + ui_->labelProgSize->setEnabled(false); } } @@ -1135,6 +1172,33 @@ void MainWindow::refreshPortComboBox_() { ui_->pushButtonConnect->setEnabled(!paths.empty()); } +QString MainWindow::calculateDataRate_(int64_t elapsed, uint32_t amount) { + double value = (elapsed ? (amount * 1.0 / elapsed) : 0.0) * 1000.0; + if (value >= 1024 * 1024) { // MB/s + return QString("%1 MB/s").arg( + static_cast(round(value / 1024.0 / 1024.0))); + } else if (value >= 1024) { // KB/s + return QString("%1 KB/s").arg(static_cast(round(value / 1024.0))); + } else { // Byte/s + return QString("%1 byte/s").arg(static_cast(round(value))); + } +} + +QString MainWindow::calculateRemainingTime_(int64_t elapsed, uint32_t current, + uint32_t total) { + double value = + (current ? (elapsed * 1.0 * (total - current) / current) : 0.0) / + 1000.0; + if (value >= 60 * 60) { // hr + return tr("%1 hour(s)") + .arg(static_cast(round(value / 60.0 / 60.0))); + } else if (value >= 60) { // min + return tr("%1 minute(s)").arg(static_cast(round(value / 60.0))); + } else { // sec + return tr("%1 second(s)").arg(static_cast(round(value))); + } +} + // --------------------------------------------------------------------------- // Diagnostics diff --git a/software/usbflashprog/main/mainwindow.hpp b/software/usbflashprog/ui/mainwindow.hpp similarity index 91% rename from software/usbflashprog/main/mainwindow.hpp rename to software/usbflashprog/ui/mainwindow.hpp index fd3b2e11..d5796c62 100644 --- a/software/usbflashprog/main/mainwindow.hpp +++ b/software/usbflashprog/ui/mainwindow.hpp @@ -8,15 +8,15 @@ // --------------------------------------------------------------------------- /** * @ingroup Software - * @file main/mainwindow.hpp + * @file ui/mainwindow.hpp * @brief Header of the Main Window Class. * * @author Robson Martins (https://www.robsonmartins.com) */ // --------------------------------------------------------------------------- -#ifndef MAIN_MAINWINDOW_HPP_ -#define MAIN_MAINWINDOW_HPP_ +#ifndef UI_MAINWINDOW_HPP_ +#define UI_MAINWINDOW_HPP_ #include #include @@ -71,6 +71,7 @@ class MainWindow : public QMainWindow { void on_actionAboutQt_triggered(bool checked = false); void on_actionProjectHome_triggered(bool checked = false); void on_actionAuthorHome_triggered(bool checked = false); + void on_actionSettings_triggered(bool checked = false); /* programmer */ void on_comboBoxProgPort_currentIndexChanged(int index); void on_actionRead_triggered(bool checked = false); @@ -178,9 +179,9 @@ class MainWindow : public QMainWindow { /* @brief Connects signals of the widgets. */ void connectSignals_(); /* @brief Loads configuration settings. */ - void loadSettings_(); + void loadProgSettings_(); /* @brief Saves configuration settings. */ - void saveSettings_(); + void saveProgSettings_(); /* @brief Creates device (Prog). */ void createDevice_(); /* @@ -218,6 +219,23 @@ class MainWindow : public QMainWindow { * @return True if it can continue, false otherwise. */ bool showActionWarningDialog_(); + /* + * @brief Calculates the Data Rate (Prog). + * @param elapsed Elapsed time, in milliseconds. + * @param amount Amount of data, in bytes. + * @return String describing the Data Rate, in bytes/sec or KB/s or MB/s. + */ + QString calculateDataRate_(int64_t elapsed, uint32_t amount); + /* + * @brief Calculates the Remaining Time (Prog). + * @param elapsed Elapsed time, in milliseconds. + * @param current Current address. + * @param total Total addresses. + * @return String describing the Remaining Time, in hours + * or minutes or seconds. + */ + QString calculateRemainingTime_(int64_t elapsed, uint32_t current, + uint32_t total); /* @brief Refreshes the port comboboxes (Prog/Diag). */ void refreshPortComboBox_(); /* @@ -256,4 +274,4 @@ class MainWindow : public QMainWindow { void dataHexToBin_(); }; -#endif // MAIN_MAINWINDOW_HPP_ +#endif // UI_MAINWINDOW_HPP_ diff --git a/software/usbflashprog/main/mainwindow.ui b/software/usbflashprog/ui/mainwindow.ui similarity index 99% rename from software/usbflashprog/main/mainwindow.ui rename to software/usbflashprog/ui/mainwindow.ui index a5cbbd19..ed110221 100644 --- a/software/usbflashprog/main/mainwindow.ui +++ b/software/usbflashprog/ui/mainwindow.ui @@ -499,6 +499,9 @@ + + false + 0 @@ -512,6 +515,9 @@ + + false + 0 @@ -3423,6 +3429,8 @@ Wr + + @@ -4465,6 +4473,14 @@ Wr EPROM 37VF040 + + + Settings + + + Ctrl+0 + + btnSave diff --git a/software/usbflashprog/ui/settings.cpp b/software/usbflashprog/ui/settings.cpp new file mode 100644 index 00000000..c480eeba --- /dev/null +++ b/software/usbflashprog/ui/settings.cpp @@ -0,0 +1,100 @@ +// --------------------------------------------------------------------------- +// USB EPROM/Flash Programmer +// +// Copyright (2024) Robson Martins +// +// This work is licensed under a Creative Commons Attribution-NonCommercial- +// ShareAlike 4.0 International License. +// --------------------------------------------------------------------------- +/** + * @ingroup Software + * @file ui/settings.cpp + * @brief Implementation of the Settings Dialog Class. + * + * @author Robson Martins (https://www.robsonmartins.com) + */ +// --------------------------------------------------------------------------- + +#include +#include +#include + +#include "settings.hpp" +#include "main.hpp" +#include "./ui_settings.h" + +// --------------------------------------------------------------------------- + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent), ui_(new Ui::SettingsDialog) { + ui_->setupUi(this); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint); + setWindowFlag(Qt::WindowContextHelpButtonHint, false); + QString logFileInfo = ui_->labelLogFileInfo->text(); + logFileInfo = + logFileInfo.arg("\n" + QDir::toNativeSeparators(QDir::homePath() + "/" + + QString(kLogFileName))); + ui_->labelLogFileInfo->setText(logFileInfo); + loadSettings_(); +} + +SettingsDialog::~SettingsDialog() { + delete ui_; +} + +void SettingsDialog::on_buttonBox_accepted() { + TApplicationSettings settings = saveSettings_(); + initLogging(settings.logLevel); + loadLanguage(settings.language, qApp); + ui_->retranslateUi(this); +} + +void SettingsDialog::on_comboBoxLogLevel_currentIndexChanged(int index) { + ui_->labelLogFileInfo->setVisible(index != 0); +} + +TApplicationSettings SettingsDialog::loadSettings_() { + QSettings settings; + TApplicationSettings app = loadSettings(); + + if (app.logLevel >= 0 && app.logLevel <= 5) { + ui_->comboBoxLogLevel->setCurrentIndex(app.logLevel); + ui_->labelLogFileInfo->setVisible(app.logLevel != 0); + } else if (app.logLevel > 5) { + ui_->comboBoxLogLevel->setCurrentIndex(5); + ui_->labelLogFileInfo->setVisible(true); + } else { + ui_->comboBoxLogLevel->setCurrentIndex(0); + ui_->labelLogFileInfo->setVisible(false); + } + if (app.language.toLower() == "pt_br") { + ui_->comboBoxLanguage->setCurrentIndex(2); + } else if (app.language.toLower() == "en_us") { + ui_->comboBoxLanguage->setCurrentIndex(1); + } else { + ui_->comboBoxLanguage->setCurrentIndex(0); + } + return app; +} + +TApplicationSettings SettingsDialog::saveSettings_() { + QSettings settings; + TApplicationSettings app; + + app.logLevel = ui_->comboBoxLogLevel->currentIndex(); + switch (ui_->comboBoxLanguage->currentIndex()) { + case 2: + app.language = "pt_BR"; + break; + case 1: + app.language = "en_US"; + break; + default: + app.language = ""; + break; + } + + settings.setValue(kSettingGeneralLogLevel, QString::number(app.logLevel)); + settings.setValue(kSettingGeneralLanguage, app.language); + return app; +} diff --git a/software/usbflashprog/ui/settings.hpp b/software/usbflashprog/ui/settings.hpp new file mode 100644 index 00000000..c512f887 --- /dev/null +++ b/software/usbflashprog/ui/settings.hpp @@ -0,0 +1,72 @@ +// --------------------------------------------------------------------------- +// USB EPROM/Flash Programmer +// +// Copyright (2024) Robson Martins +// +// This work is licensed under a Creative Commons Attribution-NonCommercial- +// ShareAlike 4.0 International License. +// --------------------------------------------------------------------------- +/** + * @ingroup Software + * @file ui/settings.hpp + * @brief Header of the Settings Dialog Class. + * + * @author Robson Martins (https://www.robsonmartins.com) + */ +// --------------------------------------------------------------------------- + +#ifndef UI_SETTINGSDIALOG_HPP_ +#define UI_SETTINGSDIALOG_HPP_ + +#include +#include "config.hpp" + +// --------------------------------------------------------------------------- + +// clang-format off +QT_BEGIN_NAMESPACE +namespace Ui { class SettingsDialog; } +QT_END_NAMESPACE +// clang-format on + +// --------------------------------------------------------------------------- + +/** + * @ingroup Software + * @brief Settings Dialog GUI Class + * @details The Settings Dialog. + * @nosubgrouping + */ +class SettingsDialog : public QDialog { + Q_OBJECT + + public: + /** + * @brief Constructor. + * @param parent Pointer to parent object. Default is nullptr. + */ + explicit SettingsDialog(QWidget *parent = nullptr); + /** @brief Destructor. */ + ~SettingsDialog(); + + private slots: + /* auto slots */ + void on_buttonBox_accepted(); + void on_comboBoxLogLevel_currentIndexChanged(int index); + + private: + /* @brief Pointer to UI object. */ + Ui::SettingsDialog *ui_; + /* + * @brief Load the settings and update the controls. + * @return Application Settings. + */ + TApplicationSettings loadSettings_(); + /* + * @brief Save the settings. + * @return Application Settings. + */ + TApplicationSettings saveSettings_(); +}; + +#endif // UI_SETTINGSDIALOG_HPP_ diff --git a/software/usbflashprog/ui/settings.ui b/software/usbflashprog/ui/settings.ui new file mode 100644 index 00000000..e8c762b5 --- /dev/null +++ b/software/usbflashprog/ui/settings.ui @@ -0,0 +1,239 @@ + + + SettingsDialog + + + Qt::WindowModal + + + + 0 + 0 + 400 + 220 + + + + + 0 + 0 + + + + USB Flash/EPROM Programmer + + + + :/icon/ufprog.png:/icon/ufprog.png + + + true + + + + + + + + General + + + + + + + + + + + 0 + 0 + + + + Language + + + + + + + + 0 + 0 + + + + + 180 + 25 + + + + + Autodetect + + + + + English (en_US) + + + + + Português Brasileiro (pt_BR) + + + + + + + + + + + + + 0 + 0 + + + + Log Level + + + + + + + + 0 + 0 + + + + + 180 + 25 + + + + + Disabled + + + + + Fatal + + + + + Critical + + + + + Warning + + + + + Info + + + + + Debug + + + + + + + + + + + 0 + 0 + + + + Note: The log file can be found at %1 + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + + + + + buttonBox + accepted() + SettingsDialog + accept() + + + 258 + 192 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 326 + 192 + + + 286 + 274 + + + + +