commit 348d6121f43c6aecefc96d520acd2330d82bcd58 Author: Doug Brown Date: Sun May 13 00:33:24 2012 -0700 Initial import of SIMM programmer code into Git diff --git a/ROMSIMMFlasher.pro b/ROMSIMMFlasher.pro new file mode 100644 index 0000000..aa5f56d --- /dev/null +++ b/ROMSIMMFlasher.pro @@ -0,0 +1,26 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-12-14T20:57:35 +# +#------------------------------------------------- + +QT += core gui + +TARGET = ROMSIMMFlasher +TEMPLATE = app + + +SOURCES += main.cpp\ + mainwindow.cpp \ + usbprogrammerfinder_win.cpp \ + programmer.cpp + +HEADERS += mainwindow.h \ + usbprogrammerfinder.h \ + programmer.h + +FORMS += mainwindow.ui + +include(../qextserialport/src/qextserialport.pri) + +QMAKE_CXXFLAGS_RELEASE += -DQT_NO_DEBUG_OUTPUT diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..d951345 --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..279b005 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,365 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "programmer.h" +#include +#include +#include + +static Programmer *p; + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + p = new Programmer(); + ui->chosenWriteFile->setText(""); + ui->chosenReadFile->setText(""); + writeFileValid = false; + readFileValid = false; + ui->writeToSIMMButton->setEnabled(false); + ui->readFromSIMMButton->setEnabled(false); + ui->progressBar->setValue(0); + ui->progressBar->setEnabled(false); + ui->statusLabel->setText(""); + ui->cancelButton->setEnabled(false); + + connect(p, SIGNAL(writeStatusChanged(WriteStatus)), SLOT(programmerWriteStatusChanged(WriteStatus))); + connect(p, SIGNAL(writeTotalLengthChanged(uint32_t)), SLOT(programmerWriteTotalLengthChanged(uint32_t))); + connect(p, SIGNAL(writeCompletionLengthChanged(uint32_t)), SLOT(programmerWriteCompletionLengthChanged(uint32_t))); + connect(p, SIGNAL(electricalTestStatusChanged(ElectricalTestStatus)), SLOT(programmerElectricalTestStatusChanged(ElectricalTestStatus))); + connect(p, SIGNAL(electricalTestFailLocation(uint8_t,uint8_t)), SLOT(programmerElectricalTestLocation(uint8_t,uint8_t))); + connect(p, SIGNAL(readStatusChanged(ReadStatus)), SLOT(programmerReadStatusChanged(ReadStatus))); + connect(p, SIGNAL(readTotalLengthChanged(uint32_t)), SLOT(programmerReadTotalLengthChanged(uint32_t))); + connect(p, SIGNAL(readCompletionLengthChanged(uint32_t)), SLOT(programmerReadCompletionLengthChanged(uint32_t))); + connect(p, SIGNAL(identificationStatusChanged(IdentificationStatus)), SLOT(programmerIdentifyStatusChanged(IdentificationStatus))); + connect(p, SIGNAL(firmwareFlashStatusChanged(FirmwareFlashStatus)), SLOT(programmerFirmwareFlashStatusChanged(FirmwareFlashStatus))); + connect(p, SIGNAL(firmwareFlashTotalLengthChanged(uint32_t)), SLOT(programmerFirmwareFlashTotalLengthChanged(uint32_t))); + connect(p, SIGNAL(firmwareFlashCompletionLengthChanged(uint32_t)), SLOT(programmerFirmwareFlashCompletionLengthChanged(uint32_t))); + + /*QList l = QextSerialEnumerator::getPorts(); + foreach (QextPortInfo p, l) + { + qDebug() << "Found port..."; + qDebug() << "Enum name:" << p.enumName; + qDebug() << "Friend name:" << p.friendName; + qDebug() << "Phys name:" << p.physName; + qDebug() << "Port name:" << p.portName; + qDebug() << "Product ID:" << hex << p.productID; + qDebug() << "Vendor ID:" << hex << p.vendorID; + qDebug() << ""; + +#ifdef Q_WS_WIN + if (p.portName.startsWith("COM")) +#endif + { + // TODO: Do checking for valid USB ID? + + if ((p.vendorID == 0x03EB) && (p.productID == 0x204B)) + { + ui->portList->addItem(QString("%1 (Programmer)").arg(p.portName), QVariant(p.portName)); + } + else + { + ui->portList->addItem(p.portName, QVariant(p.portName)); + } + } + }*/ + + QextSerialEnumerator *p = new QextSerialEnumerator(); + connect(p, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(portDiscovered(QextPortInfo))); + connect(p, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(portRemoved(QextPortInfo))); + p->setUpNotifications(); +} + +MainWindow::~MainWindow() +{ + delete p; + delete ui; +} + +void MainWindow::on_selectWriteFileButton_clicked() +{ + QString filename = QFileDialog::getOpenFileName(this, "Select a ROM image:"); + if (!filename.isNull()) + { + writeFileValid = true; + ui->chosenWriteFile->setText(filename); + ui->writeToSIMMButton->setEnabled(true); + } +} + +void MainWindow::on_selectReadFileButton_clicked() +{ + QString filename = QFileDialog::getSaveFileName(this, "Save ROM image as:"); + if (!filename.isNull()) + { + readFileValid = true; + ui->chosenReadFile->setText(filename); + ui->readFromSIMMButton->setEnabled(true); + } +} + +void MainWindow::on_readFromSIMMButton_clicked() +{ + p->ReadSIMMToFile(ui->chosenReadFile->text()); + qDebug() << "Reading from SIMM..."; +} + +void MainWindow::on_writeToSIMMButton_clicked() +{ + p->WriteFileToSIMM(ui->chosenWriteFile->text()); +} + +void MainWindow::on_chosenWriteFile_textEdited(const QString &newText) +{ + QFileInfo fi(newText); + if (fi.exists() && fi.isFile()) + { + ui->writeToSIMMButton->setEnabled(true); + writeFileValid = true; + } + else + { + ui->writeToSIMMButton->setEnabled(false); + writeFileValid = false; + } +} + +void MainWindow::programmerWriteStatusChanged(WriteStatus newStatus) +{ + switch (newStatus) + { + case WriteComplete: + QMessageBox::information(this, "Write complete", "The write operation finished."); + break; + case WriteError: + QMessageBox::warning(this, "Write error", "An error occurred writing to the SIMM."); + break; + case WriteCancelled: + QMessageBox::warning(this, "Write cancelled", "The write operation was cancelled."); + break; + case WriteEraseComplete: + // No message needed for this + break; + case WriteEraseFailed: + QMessageBox::warning(this, "Write error", "An error occurred erasing the SIMM."); + break; + case WriteTimedOut: + QMessageBox::warning(this, "Write timed out", "The write operation timed out."); + break; + } +} + +void MainWindow::programmerWriteTotalLengthChanged(uint32_t totalLen) +{ + ui->progressBar->setMaximum((int)totalLen); +} + +void MainWindow::programmerWriteCompletionLengthChanged(uint32_t len) +{ + ui->progressBar->setValue((int)len); +} + + + +void MainWindow::on_electricalTestButton_clicked() +{ + electricalTestString = ""; + p->RunElectricalTest(); +} + +void MainWindow::programmerElectricalTestStatusChanged(ElectricalTestStatus newStatus) +{ + switch (newStatus) + { + case ElectricalTestStarted: + qDebug() << "Electrical test started"; + break; + case ElectricalTestPassed: + QMessageBox::information(this, "Test passed", "The electrical test passed successfully."); + break; + case ElectricalTestFailed: + QMessageBox::warning(this, "Test failed", "The electrical test failed:\n\n" + electricalTestString); + break; + case ElectricalTestTimedOut: + QMessageBox::warning(this, "Test timed out", "The electrical test operation timed out."); + break; + case ElectricalTestCouldntStart: + QMessageBox::warning(this, "Communication error", "Unable to communicate with programmer board."); + break; + } +} + +void MainWindow::programmerElectricalTestLocation(uint8_t loc1, uint8_t loc2) +{ + //qDebug() << "Electrical test error at (" << p->electricalTestPinName(loc1) << "," << p->electricalTestPinName(loc2) << ")"; + if (electricalTestString != "") + { + electricalTestString.append("\n"); + } + + electricalTestString.append(p->electricalTestPinName(loc1)); + electricalTestString.append(" shorted to "); + electricalTestString.append(p->electricalTestPinName(loc2)); +} + +void MainWindow::programmerReadStatusChanged(ReadStatus newStatus) +{ + switch (newStatus) + { + case ReadComplete: + QMessageBox::information(this, "Read complete", "The read operation finished."); + break; + case ReadError: + QMessageBox::warning(this, "Read error", "An error occurred reading from the SIMM."); + break; + case ReadCancelled: + QMessageBox::warning(this, "Read cancelled", "The read operation was cancelled."); + break; + case ReadTimedOut: + QMessageBox::warning(this, "Read timed out", "The read operation timed out."); + break; + } +} + +void MainWindow::programmerReadTotalLengthChanged(uint32_t totalLen) +{ + ui->progressBar->setMaximum((int)totalLen); +} + +void MainWindow::programmerReadCompletionLengthChanged(uint32_t len) +{ + ui->progressBar->setValue((int)len); +} + +void MainWindow::programmerIdentifyStatusChanged(IdentificationStatus newStatus) +{ + switch (newStatus) + { + case IdentificationComplete: + { + QString identifyString = "The chips identified themselves as:"; + for (int x = 0; x < 4; x++) + { + QString thisString; + uint8_t manufacturer = 0; + uint8_t device = 0; + p->GetChipIdentity(x, &manufacturer, &device); + thisString.sprintf("\nIC%d: Manufacturer 0x%02X, Device 0x%02X", (x + 1), manufacturer, device); + identifyString.append(thisString); + } + + QMessageBox::information(this, "Identification complete", identifyString); + break; + } + case IdentificationError: + QMessageBox::warning(this, "Identification error", "An error occurred identifying the chips on the SIMM."); + break; + case IdentificationTimedOut: + QMessageBox::warning(this, "Identification timed out", "The identification operation timed out."); + break; + } +} + +void MainWindow::programmerFirmwareFlashStatusChanged(FirmwareFlashStatus newStatus) +{ + switch (newStatus) + { + case FirmwareFlashComplete: + QMessageBox::information(this, "Firmware update complete", "The firmware update operation finished."); + break; + case FirmwareFlashError: + QMessageBox::warning(this, "Firmware update error", "An error occurred writing firmware to the device."); + break; + case FirmwareFlashCancelled: + QMessageBox::warning(this, "Firmware update cancelled", "The firmware update was cancelled."); + break; + case FirmwareFlashTimedOut: + QMessageBox::warning(this, "Firmware update timed out", "The firmware update operation timed out."); + break; + } +} + +void MainWindow::programmerFirmwareFlashTotalLengthChanged(uint32_t totalLen) +{ + ui->progressBar->setMaximum((int)totalLen); +} + +void MainWindow::programmerFirmwareFlashCompletionLengthChanged(uint32_t len) +{ + ui->progressBar->setValue((int)len); +} + +void MainWindow::startOperation_updateButtons() +{ + ui->writeToSIMMButton->setEnabled(false); + ui->readFromSIMMButton->setEnabled(false); + ui->identifyButton->setEnabled(false); + ui->electricalTestButton->setEnabled(false); + ui->progressBar->setValue(0); + ui->progressBar->setEnabled(true); + ui->cancelButton->setEnabled(true); +} + +void MainWindow::endOperation_updateButtons() +{ + ui->writeToSIMMButton->setEnabled(writeFileValid); + ui->readFromSIMMButton->setEnabled(readFileValid); + + ui->identifyButton->setEnabled(true); + ui->electricalTestButton->setEnabled(true); + ui->progressBar->setValue(0); + ui->progressBar->setEnabled(false); + ui->cancelButton->setEnabled(false); +} + + +void MainWindow::on_actionUpdate_firmware_triggered() +{ + QString filename = QFileDialog::getOpenFileName(this, "Select a firmware image:"); + if (!filename.isNull()) + { + p->FlashFirmware(filename); + qDebug() << "Updating firmware..."; + } +} + +void MainWindow::on_identifyButton_clicked() +{ + p->IdentifySIMMChips(); +} + +void MainWindow::portDiscovered(const QextPortInfo & info) +{ + qDebug() << info.portName << "discovered"; + +#ifdef Q_WS_WIN + if (info.portName.startsWith("COM")) +#endif + { + // TODO: Do checking for valid USB ID? + + if ((info.vendorID == 0x03EB) && (info.productID == 0x204B)) + { + ui->portList->addItem(QString("%1 (Programmer)").arg(info.portName), QVariant(info.portName)); + } + else + { + ui->portList->addItem(info.portName, QVariant(info.portName)); + } + } +} + +void MainWindow::portRemoved(const QextPortInfo & info) +{ + qDebug() << info.portName << "removed"; + + for (int x = ui->portList->count() - 1; x >= 0; x--) + { + if (ui->portList->itemData(x) == info.portName) + { + ui->portList->removeItem(x); + } + } +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..19a7316 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,65 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "programmer.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void on_selectWriteFileButton_clicked(); + void on_selectReadFileButton_clicked(); + + void on_writeToSIMMButton_clicked(); + void on_readFromSIMMButton_clicked(); + + void on_chosenWriteFile_textEdited(const QString &newText); + + void programmerWriteStatusChanged(WriteStatus newStatus); + void programmerWriteTotalLengthChanged(uint32_t totalLen); + void programmerWriteCompletionLengthChanged(uint32_t len); + + void programmerElectricalTestStatusChanged(ElectricalTestStatus newStatus); + void programmerElectricalTestLocation(uint8_t loc1, uint8_t loc2); + + void programmerReadStatusChanged(ReadStatus newStatus); + void programmerReadTotalLengthChanged(uint32_t totalLen); + void programmerReadCompletionLengthChanged(uint32_t len); + + void programmerIdentifyStatusChanged(IdentificationStatus newStatus); + + void programmerFirmwareFlashStatusChanged(FirmwareFlashStatus newStatus); + void programmerFirmwareFlashTotalLengthChanged(uint32_t totalLen); + void programmerFirmwareFlashCompletionLengthChanged(uint32_t len); + + void on_electricalTestButton_clicked(); + + void startOperation_updateButtons(); + void endOperation_updateButtons(); + + void on_actionUpdate_firmware_triggered(); + + void on_identifyButton_clicked(); + + void portDiscovered(const QextPortInfo & info); + void portRemoved(const QextPortInfo & info); + +private: + Ui::MainWindow *ui; + bool writeFileValid; + bool readFileValid; + QString electricalTestString; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..4a03781 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,318 @@ + + + MainWindow + + + + 0 + 0 + 557 + 403 + + + + MainWindow + + + + + 0 + + + 0 + + + + + + + + + 5 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Port: + + + + + + + + + + + + Write file to SIMM + + + + + + + + true + + + + + + + Select file... + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Write to SIMM + + + + + + + + + + + + Read from SIMM to file + + + + + + + + true + + + + + + + Select file... + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Read from SIMM + + + + + + + + + + + + Miscellaneous + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Identify chips + + + + + + + Electrical test + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Status label: + + + + + + + 0 + + + + + 24 + + + + + + + Cancel + + + + + + + + + + + + + + + + 0 + 0 + 557 + 21 + + + + + File + + + + + + Advanced + + + + + + + + + TopToolBarArea + + + false + + + + + + Quit + + + Ctrl+Q + + + + + Update firmware... + + + + + + + + actionQuit + triggered() + MainWindow + close() + + + -1 + -1 + + + 278 + 201 + + + + + diff --git a/programmer.cpp b/programmer.cpp new file mode 100644 index 0000000..0354269 --- /dev/null +++ b/programmer.cpp @@ -0,0 +1,776 @@ +#include "programmer.h" +#include +#include +#include + +typedef enum ProgrammerCommandState +{ + WaitingForNextCommand = 0, + + WriteSIMMWaitingEraseReply, + WriteSIMMWaitingWriteReply, + WriteSIMMWaitingFinishReply, + WriteSIMMWaitingWriteMoreReply, + + ElectricalTestWaitingStartReply, + ElectricalTestWaitingNextStatus, + ElectricalTestWaitingFirstFail, + ElectricalTestWaitingSecondFail, + + ReadSIMMWaitingStartReply, + ReadSIMMWaitingData, + ReadSIMMWaitingStatusReply, + + BootloaderStateAwaitingOKReply, + BootloaderStateAwaitingReply, + BootloaderStateAwaitingOKReplyToBootloader, + BootloaderStateAwaitingReplyToBootloader, + + IdentificationAwaitingOKReply, + IdentificationWaitingData, + IdentificationAwaitingDoneReply, + + BootloaderEraseProgramAwaitingStartOKReply, + BootloaderEraseProgramWaitingFinishReply, + BootloaderEraseProgramWaitingWriteMoreReply, + BootloaderEraseProgramWaitingWriteReply +} ProgrammerCommandState; + +typedef enum ProgrammerCommand +{ + EnterWaitingMode = 0, + DoElectricalTest, + IdentifyChips, + ReadByte, + ReadChips, + EraseChips, + WriteChips, + GetBootloaderState, + EnterBootloader, + EnterProgrammer, + BootloaderEraseAndWriteProgram +} ProgrammerCommand; + +typedef enum ProgrammerReply +{ + CommandReplyOK, + CommandReplyError, + CommandReplyInvalid +} ProgrammerReply; + +typedef enum ComputerReadReply +{ + ComputerReadOK, + ComputerReadCancel +} ComputerReadReply; + +typedef enum ProgrammerReadReply +{ + ProgrammerReadOK, + ProgrammerReadError, + ProgrammerReadMoreData, + ProgrammerReadFinished, + ProgrammerReadConfirmCancel +} ProgrammerReadReply; + +typedef enum ComputerWriteReply +{ + ComputerWriteMore, + ComputerWriteFinish, + ComputerWriteCancel +} ComputerWriteReply; + +typedef enum ProgrammerWriteReply +{ + ProgrammerWriteOK, + ProgrammerWriteError, + ProgrammerWriteConfirmCancel +} ProgrammerWriteReply; + +typedef enum ProgrammerIdentifyReply +{ + ProgrammerIdentifyDone +} ProgrammerIdentifyReply; + +typedef enum ProgrammerElectricalTestReply +{ + ProgrammerElectricalTestFail, + ProgrammerElectricalTestDone +} ProgrammerElectricalTestReply; + +typedef enum BootloaderStateReply +{ + BootloaderStateInBootloader, + BootloaderStateInProgrammer +} BootloaderStateReply; + +typedef enum ProgrammerBootloaderEraseWriteReply +{ + BootloaderWriteOK, + BootloaderWriteError, + BootloaderWriteConfirmCancel +} ProgrammerBootloaderEraseWriteReply; + +typedef enum ComputerBootloaderEraseWriteRequest +{ + ComputerBootloaderWriteMore = 0, + ComputerBootloaderFinish, + ComputerBootloaderCancel +} ComputerBootloaderEraseWriteRequest; + + + + + +#define WRITE_CHUNK_SIZE 1024 +#define READ_CHUNK_SIZE 1024 +#define FIRMWARE_CHUNK_SIZE 1024 + +static ProgrammerCommandState curState = WaitingForNextCommand; + +// After identifying that we're in the main program, what will be the command +// we will send and the state we will be waiting in? +static ProgrammerCommandState nextState = WaitingForNextCommand; +static uint8_t nextSendByte = 0; + +Programmer::Programmer(QObject *parent) : + QObject(parent) +{ + serialPort = new QextSerialPort("\\\\.\\COM13", QextSerialPort::EventDriven); + connect(serialPort, SIGNAL(readyRead()), SLOT(dataReady())); +} + +Programmer::~Programmer() +{ + if (serialPort->isOpen()) + { + serialPort->close(); + } + delete serialPort; +} + +void Programmer::ReadSIMMToFile(QString filename) +{ + readFile = new QFile(filename); + readFile->open(QFile::WriteOnly); + lenRead = 0; + + emit readTotalLengthChanged(2 * 1024 * 1024); + emit readCompletionLengthChanged(lenRead); + + startProgrammerCommand(ReadChips, ReadSIMMWaitingStartReply); +} + +void Programmer::WriteFileToSIMM(QString filename) +{ + writeFile = new QFile(filename); + if (!writeFile->open(QFile::ReadOnly)) + { + curState = WaitingForNextCommand; + emit writeStatusChanged(WriteError); + return; + } + lenWritten = 0; + writeLenRemaining = writeFile->size(); + emit writeTotalLengthChanged(writeLenRemaining); + emit writeCompletionLengthChanged(lenWritten); + + startProgrammerCommand(EraseChips, WriteSIMMWaitingEraseReply); +} + +void Programmer::sendByte(uint8_t b) +{ + serialPort->write((const char *)&b, 1); +} + +uint8_t Programmer::readByte() +{ + uint8_t returnVal; + serialPort->read((char *)&returnVal, 1); + // TODO: Error checking if read fails? + return returnVal; +} + +void Programmer::dataReady() +{ + while (!serialPort->atEnd()) + { + handleChar(readByte()); + } +} + +void Programmer::handleChar(uint8_t c) +{ + switch (curState) + { + case WaitingForNextCommand: + // Not expecting anything. Ignore it. + break; + case WriteSIMMWaitingEraseReply: + { + switch (c) + { + case CommandReplyOK: + sendByte(WriteChips); + curState = WriteSIMMWaitingWriteReply; + qDebug() << "Chips erased. Now asking to start writing..."; + emit writeStatusChanged(WriteEraseComplete); + break; + case CommandReplyError: + qDebug() << "Error erasing chips."; + writeFile->close(); + curState = WaitingForNextCommand; + emit writeStatusChanged(WriteEraseFailed); + serialPort->close(); + break; + } + break; + } + case WriteSIMMWaitingWriteReply: + switch (c) + { + case CommandReplyOK: + // We're in write SIMM mode. Now ask to start writing + if (writeLenRemaining > 0) + { + sendByte(ComputerWriteMore); + curState = WriteSIMMWaitingWriteMoreReply; + qDebug() << "Write more..." << writeLenRemaining << "remaining."; + } + else + { + sendByte(ComputerWriteFinish); + curState = WriteSIMMWaitingFinishReply; + qDebug() << "Finished writing. Sending write finish command..."; + } + break; + case CommandReplyError: + qDebug() << "Error entering write mode."; + writeFile->close(); + curState = WaitingForNextCommand; + emit writeStatusChanged(WriteError); + serialPort->close(); + break; + } + + break; + case WriteSIMMWaitingWriteMoreReply: + { + switch (c) + { + case ProgrammerWriteOK: + { + qDebug() << "Programmer replied OK to send 1024 bytes of data! Sending..."; + // Write the next chunk of data to the SIMM... + + int chunkSize = WRITE_CHUNK_SIZE; + if (writeLenRemaining < WRITE_CHUNK_SIZE) + { + chunkSize = writeLenRemaining; + } + + // Read the chunk from the file! + QByteArray thisChunk = writeFile->read(chunkSize); + + // If it isn't a WRITE_CHUNK_SIZE chunk, pad the rest of it with 0xFFs (unprogrammed bytes) + // so the total chunk size is WRITE_CHUNK_SIZE, since that's what the programmer board expects. + for (int x = writeLenRemaining; x < WRITE_CHUNK_SIZE; x++) + { + thisChunk.append(0xFF); + } + + // Write the chunk out (it's asynchronous so will return immediately) + serialPort->write(thisChunk); + + // OK, now we're waiting to hear back from the programmer on the result + qDebug() << "Waiting for status reply..."; + curState = WriteSIMMWaitingWriteReply; + writeLenRemaining -= chunkSize; + lenWritten += chunkSize; + emit writeCompletionLengthChanged(lenWritten); + break; + } + case ProgrammerWriteError: + default: + qDebug() << "Error writing to chips."; + writeFile->close(); + curState = WaitingForNextCommand; + emit writeStatusChanged(WriteError); + serialPort->close(); + break; + } + break; + } + case WriteSIMMWaitingFinishReply: + switch (c) + { + case ProgrammerWriteOK: + writeFile->close(); + curState = WaitingForNextCommand; + qDebug() << "Write success at end"; + emit writeStatusChanged(WriteComplete); + serialPort->close(); + break; + case ProgrammerWriteError: + default: + qDebug() << "Write failure at end"; + curState = WaitingForNextCommand; + writeFile->close(); + emit writeStatusChanged(WriteError); + serialPort->close(); + break; + } + + break; + + // ELECTRICAL TEST STATE HANDLERS + + case ElectricalTestWaitingStartReply: + switch (c) + { + case CommandReplyOK: + curState = ElectricalTestWaitingNextStatus; + emit electricalTestStatusChanged(ElectricalTestStarted); + electricalTestErrorCounter = 0; + break; + case CommandReplyError: + case CommandReplyInvalid: + default: + curState = WaitingForNextCommand; + emit electricalTestStatusChanged(ElectricalTestCouldntStart); + serialPort->close(); + } + break; + case ElectricalTestWaitingNextStatus: + switch (c) + { + case ProgrammerElectricalTestDone: + if (electricalTestErrorCounter > 0) + { + emit electricalTestStatusChanged(ElectricalTestFailed); + } + else + { + emit electricalTestStatusChanged(ElectricalTestPassed); + } + curState = WaitingForNextCommand; + serialPort->close(); + break; + case ProgrammerElectricalTestFail: + electricalTestErrorCounter++; + curState = ElectricalTestWaitingFirstFail; + break; + } + break; + case ElectricalTestWaitingFirstFail: + electricalTestFirstErrorLoc = c; + curState = ElectricalTestWaitingSecondFail; + break; + case ElectricalTestWaitingSecondFail: + emit electricalTestFailLocation(electricalTestFirstErrorLoc, c); + curState = ElectricalTestWaitingNextStatus; + break; + + // READ SIMM STATE HANDLERS + case ReadSIMMWaitingStartReply: + curState = ReadSIMMWaitingData; + readChunkLenRemaining = READ_CHUNK_SIZE; + break; + case ReadSIMMWaitingData: + readFile->write((const char *)&c, 1); + lenRead++; + if (--readChunkLenRemaining == 0) + { + emit readCompletionLengthChanged(lenRead); + qDebug() << "Received a chunk of data"; + sendByte(ComputerReadOK); + curState = ReadSIMMWaitingStatusReply; + } + break; + case ReadSIMMWaitingStatusReply: + switch (c) + { + case ProgrammerReadFinished: + curState = WaitingForNextCommand; + serialPort->close(); + readFile->close(); + emit readStatusChanged(ReadComplete); + break; + case ProgrammerReadConfirmCancel: + curState = WaitingForNextCommand; + serialPort->close(); + readFile->close(); + emit readStatusChanged(ReadCancelled); + break; + case ProgrammerReadMoreData: + curState = ReadSIMMWaitingData; + readChunkLenRemaining = READ_CHUNK_SIZE; + break; + } + + break; + + // BOOTLOADER STATE HANDLERS + case BootloaderStateAwaitingOKReply: + if (c == CommandReplyOK) + { + // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply. + curState = BootloaderStateAwaitingReply; + } + else + { + curState = WaitingForNextCommand; + qDebug() << "Unable to enter programmer mode"; + // TODO: Error out somehow + } + break; + case BootloaderStateAwaitingReply: + switch (c) + { + case BootloaderStateInBootloader: + // Oops! We're in the bootloader. Better change over to the programmer. + // TODO: Send "enter programmer" command. + // Close serial port. + // Wait for serial port to reappear (or just wait a fixed time?) + // Open serial port. + // Ensure we're in the programmer? + // Then do the command correctly + qDebug() << "We're in the bootloader, so sending an \"enter programmer\" request."; + sendByte(EnterProgrammer); + serialPort->close(); + { + QMutex mutex; + mutex.lock(); + + QWaitCondition waitCondition; + waitCondition.wait(&mutex, 5000); + mutex.unlock(); + } + serialPort->open(QextSerialPort::ReadWrite); + curState = nextState; + sendByte(nextSendByte); + break; + case BootloaderStateInProgrammer: + // Good to go... + // So change to the next state and send out the next command + // to begin whatever sequence of events we expected. + qDebug() << "Already in programmer. Good! Do the command now..."; + curState = nextState; + sendByte(nextSendByte); + break; + // TODO: Otherwise, raise an error? + } + break; + + case BootloaderStateAwaitingOKReplyToBootloader: + if (c == CommandReplyOK) + { + // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply. + curState = BootloaderStateAwaitingReplyToBootloader; + } + else + { + curState = WaitingForNextCommand; + qDebug() << "Unable to enter bootloader mode"; + // TODO: Error out somehow + } + break; + case BootloaderStateAwaitingReplyToBootloader: + switch (c) + { + case BootloaderStateInProgrammer: + // Oops! We're in the programmer. Better change over to the bootloader. + // TODO: Send "enter bootloader" command. + // Close serial port. + // Wait for serial port to reappear (or just wait a fixed time?) + // Open serial port. + // Ensure we're in the bootloader? + // Then do the command correctly + qDebug() << "We're in the programmer, so sending an \"enter bootloader\" request."; + sendByte(EnterBootloader); + serialPort->close(); + { + QMutex mutex; + mutex.lock(); + + QWaitCondition waitCondition; + waitCondition.wait(&mutex, 5000); + mutex.unlock(); + } + serialPort->open(QextSerialPort::ReadWrite); + curState = nextState; + sendByte(nextSendByte); + break; + case BootloaderStateInBootloader: + // Good to go... + // So change to the next state and send out the next command + // to begin whatever sequence of events we expected. + qDebug() << "Already in bootloader. Good! Do the command now..."; + curState = nextState; + sendByte(nextSendByte); + break; + // TODO: Otherwise, raise an error? + } + break; + + // IDENTIFICATION STATE HANDLERS + case IdentificationAwaitingOKReply: + if (c == CommandReplyOK) + { + // Good to go, now waiting for identification data + curState = IdentificationWaitingData; + identificationCounter = 0; + } + else + { + emit identificationStatusChanged(IdentificationError); + curState = WaitingForNextCommand; + } + break; + case IdentificationWaitingData: + if (identificationCounter & 1) // device ID? + { + chipDeviceIDs[identificationCounter/2] = c; + } + else // manufacturer ID? + { + chipManufacturerIDs[identificationCounter/2] = c; + } + + // All done? + if (++identificationCounter >= 8) + { + curState = IdentificationAwaitingDoneReply; + } + break; + case IdentificationAwaitingDoneReply: + if (c == ProgrammerIdentifyDone) + { + emit identificationStatusChanged(IdentificationComplete); + } + else + { + emit identificationStatusChanged(IdentificationError); + } + curState = WaitingForNextCommand; + break; + + // WRITE BOOTLOADER PROGRAM STATE HANDLERS + case BootloaderEraseProgramAwaitingStartOKReply: + if (c == CommandReplyOK) + { + sendByte(ComputerBootloaderWriteMore); + curState = BootloaderEraseProgramWaitingWriteMoreReply; + } + else + { + emit firmwareFlashStatusChanged(FirmwareFlashError); + curState = WaitingForNextCommand; + serialPort->close(); + firmwareFile->close(); + } + break; + case BootloaderEraseProgramWaitingFinishReply: + if (c == BootloaderWriteOK) + { + emit firmwareFlashStatusChanged(FirmwareFlashComplete); + curState = WaitingForNextCommand; + serialPort->close(); + firmwareFile->close(); + } + else + { + emit firmwareFlashStatusChanged(FirmwareFlashError); + curState = WaitingForNextCommand; + serialPort->close(); + firmwareFile->close(); + } + break; + case BootloaderEraseProgramWaitingWriteMoreReply: + if (c == BootloaderWriteOK) + { + // Send the next chunk of data + qDebug() << "Bootloader replied OK to send 1024 bytes of data! Sending..."; + int chunkSize = FIRMWARE_CHUNK_SIZE; + if (firmwareLenRemaining < FIRMWARE_CHUNK_SIZE) + { + chunkSize = firmwareLenRemaining; + } + + // Read the chunk from the file! + QByteArray thisChunk = firmwareFile->read(chunkSize); + + // If it isn't FIRMWARE_CHUNK_SIZE, pad the rest with 0xFF + // (unprogrammed bytes) + for (int x = firmwareLenRemaining; x < FIRMWARE_CHUNK_SIZE; x++) + { + thisChunk.append(0xFF); + } + + // Write the chunk out (it's asynchronous so will return immediately) + serialPort->write(thisChunk); + + // OK, now we're waiting to hear back from the programmer on the result + qDebug() << "Waiting for status reply..."; + curState = BootloaderEraseProgramWaitingWriteReply; + firmwareLenRemaining -= chunkSize; + firmwareLenWritten += chunkSize; + emit firmwareFlashCompletionLengthChanged(firmwareLenWritten); + } + else + { + emit firmwareFlashStatusChanged(FirmwareFlashError); + curState = WaitingForNextCommand; + serialPort->close(); + firmwareFile->close(); + } + break; + case BootloaderEraseProgramWaitingWriteReply: + if (c == CommandReplyOK) + { + // Either ask to send the next chunk, or send a "finish" response + if (firmwareLenRemaining > 0) + { + sendByte(ComputerBootloaderWriteMore); + curState = BootloaderEraseProgramWaitingWriteMoreReply; + } + else + { + sendByte(ComputerBootloaderFinish); + curState = BootloaderEraseProgramWaitingFinishReply; + } + } + else + { + emit firmwareFlashStatusChanged(FirmwareFlashError); + curState = WaitingForNextCommand; + serialPort->close(); + firmwareFile->close(); + } + break; + } +} + +void Programmer::RunElectricalTest() +{ + startProgrammerCommand(DoElectricalTest, ElectricalTestWaitingStartReply); +} + +QString Programmer::electricalTestPinName(uint8_t index) +{ + if (index <= LAST_ADDRESS_LINE_FAIL_INDEX) + { + return QString().sprintf("A%d", index - FIRST_ADDRESS_LINE_FAIL_INDEX); + } + else if (index <= LAST_DATA_LINE_FAIL_INDEX) + { + // The byte ordering is backwards to the labeling, so I have to fix that. + // Reverse the byte ordering so we have the correct number in terms of how + // D0 to D31 are labeled... + index = index - FIRST_DATA_LINE_FAIL_INDEX; + if (index < 8) + { + index = index + 24; + } + else if (index < 16) + { + index = index + 8; + } + else if (index < 24) + { + index = index - 8; + } + else + { + index = index - 24; + } + return QString().sprintf("D%d", index); + } + else if (index == CS_FAIL_INDEX) + { + return "CS"; + } + else if (index == OE_FAIL_INDEX) + { + return "OE"; + } + else if (index == WE_FAIL_INDEX) + { + return "WE"; + } + else if (index == GROUND_FAIL_INDEX) + { + return "GND"; + } + else + { + return "?"; + } +} + +void Programmer::IdentifySIMMChips() +{ + startProgrammerCommand(IdentifyChips, IdentificationAwaitingOKReply); +} + +void Programmer::GetChipIdentity(int chipIndex, uint8_t *manufacturer, uint8_t *device) +{ + if ((chipIndex >= 0) && (chipIndex < 4)) + { + *manufacturer = chipManufacturerIDs[chipIndex]; + *device = chipDeviceIDs[chipIndex]; + } + else + { + *manufacturer = 0; + *device = 0; + } +} + +void Programmer::FlashFirmware(QString filename) +{ + firmwareFile = new QFile(filename); + if (!firmwareFile->open(QFile::ReadOnly)) + { + curState = WaitingForNextCommand; + emit firmwareFlashStatusChanged(FirmwareFlashError); + return; + } + + firmwareLenWritten = 0; + firmwareLenRemaining = firmwareFile->size(); + emit firmwareFlashTotalLengthChanged(firmwareLenRemaining); + emit firmwareFlashCompletionLengthChanged(firmwareLenWritten); + + startBootloaderCommand(BootloaderEraseAndWriteProgram, BootloaderEraseProgramAwaitingStartOKReply); +} + +// Begins a command by opening the serial port, making sure we're in the PROGRAMMER +// rather than the bootloader, then sending a command and setting a new command state. +// TODO: When it fails, this needs to carry errors over somehow. +// newState is really just a ProgrammerCommandState but in order to keep +// ProgrammerCommandState private, I did it this way. +void Programmer::startProgrammerCommand(uint8_t commandByte, uint32_t newState) +{ + nextState = (ProgrammerCommandState)newState; + nextSendByte = commandByte; + + curState = BootloaderStateAwaitingOKReply; + serialPort->open(QextSerialPort::ReadWrite); + sendByte(GetBootloaderState); +} + +// Begins a command by opening the serial port, making sure we're in the BOOTLOADER +// rather than the programmer, then sending a command and setting a new command state. +// TODO: When it fails, this needs to carry errors over somehow. +// newState is really just a ProgrammerCommandState but in order to keep +// ProgrammerCommandState private, I did it this way. +void Programmer::startBootloaderCommand(uint8_t commandByte, uint32_t newState) +{ + nextState = (ProgrammerCommandState)newState; + nextSendByte = commandByte; + + curState = BootloaderStateAwaitingOKReplyToBootloader; + serialPort->open(QextSerialPort::ReadWrite); + sendByte(GetBootloaderState); +} diff --git a/programmer.h b/programmer.h new file mode 100644 index 0000000..41000a4 --- /dev/null +++ b/programmer.h @@ -0,0 +1,125 @@ +#ifndef PROGRAMMER_H +#define PROGRAMMER_H + +#include +#include +#include + +typedef enum ReadStatus +{ + ReadComplete, + ReadError, + ReadCancelled, + ReadTimedOut +} ReadStatus; + +typedef enum WriteStatus +{ + WriteComplete, + WriteError, + WriteCancelled, + WriteEraseComplete, + WriteEraseFailed, + WriteTimedOut +} WriteStatus; + +typedef enum ElectricalTestStatus +{ + ElectricalTestStarted, + ElectricalTestPassed, + ElectricalTestFailed, + ElectricalTestTimedOut, + ElectricalTestCouldntStart +} ElectricalTestStatus; + +typedef enum IdentificationStatus +{ + IdentificationComplete, + IdentificationError, + IdentificationTimedOut +} IdentificationStatus; + +typedef enum FirmwareFlashStatus +{ + FirmwareFlashComplete, + FirmwareFlashError, + FirmwareFlashCancelled, + FirmwareFlashTimedOut +} FirmwareFlashStatus; + +// Electrical test indexes +#define GROUND_FAIL_INDEX 0xFF + +#define FIRST_ADDRESS_LINE_FAIL_INDEX 0 +#define LAST_ADDRESS_LINE_FAIL_INDEX (FIRST_ADDRESS_LINE_FAIL_INDEX + 20) +#define FIRST_DATA_LINE_FAIL_INDEX (LAST_ADDRESS_LINE_FAIL_INDEX + 1) +#define LAST_DATA_LINE_FAIL_INDEX (FIRST_DATA_LINE_FAIL_INDEX + 31) +#define CS_FAIL_INDEX (LAST_DATA_LINE_FAIL_INDEX + 1) +#define OE_FAIL_INDEX (CS_FAIL_INDEX + 1) +#define WE_FAIL_INDEX (OE_FAIL_INDEX + 1) + +class Programmer : public QObject +{ + Q_OBJECT +public: + explicit Programmer(QObject *parent = 0); + virtual ~Programmer(); + void ReadSIMMToFile(QString filename); + void WriteFileToSIMM(QString filename); + void RunElectricalTest(); + QString electricalTestPinName(uint8_t index); + void IdentifySIMMChips(); + void GetChipIdentity(int chipIndex, uint8_t *manufacturer, uint8_t *device); + void FlashFirmware(QString filename); +signals: + void readStatusChanged(ReadStatus status); + void readTotalLengthChanged(uint32_t total); + void readCompletionLengthChanged(uint32_t total); + + void writeStatusChanged(WriteStatus status); + void writeTotalLengthChanged(uint32_t total); + void writeCompletionLengthChanged(uint32_t len); + + void electricalTestStatusChanged(ElectricalTestStatus status); + void electricalTestFailLocation(uint8_t loc1, uint8_t loc2); + + void identificationStatusChanged(IdentificationStatus status); + + void firmwareFlashStatusChanged(FirmwareFlashStatus status); + void firmwareFlashTotalLengthChanged(uint32_t total); + void firmwareFlashCompletionLengthChanged(uint32_t total); +public slots: + +private: + QFile *readFile; + QFile *writeFile; + QFile *firmwareFile; + + QextSerialPort *serialPort; + void sendByte(uint8_t b); + uint8_t readByte(); + void handleChar(uint8_t c); + + uint32_t writeLenRemaining; + uint32_t lenWritten; + uint32_t electricalTestErrorCounter; + uint8_t electricalTestFirstErrorLoc; + + uint32_t readChunkLenRemaining; + uint32_t lenRead; + + int identificationCounter; + uint8_t chipManufacturerIDs[4]; + uint8_t chipDeviceIDs[4]; + + uint32_t firmwareLenRemaining; + uint32_t firmwareLenWritten; + + void startProgrammerCommand(uint8_t commandByte, uint32_t newState); + void startBootloaderCommand(uint8_t commandByte, uint32_t newState); + +private slots: + void dataReady(); +}; + +#endif // PROGRAMMER_H diff --git a/usbprogrammerfinder.h b/usbprogrammerfinder.h new file mode 100644 index 0000000..6282544 --- /dev/null +++ b/usbprogrammerfinder.h @@ -0,0 +1,21 @@ +#ifndef USBPROGRAMMERFINDER_H +#define USBPROGRAMMERFINDER_H + +#include + +#define USB_PROGRAMMER_VID 0x03EB +#define USB_PROGRAMMER_PID 0x204B + +class USBProgrammerFinder : public QObject +{ + Q_OBJECT +public: + explicit USBProgrammerFinder(QObject *parent = 0); + QString getSerialPortName(); +signals: + void programmerPresenceChanged(bool); +public slots: + +}; + +#endif // USBPROGRAMMERFINDER_H diff --git a/usbprogrammerfinder_win.cpp b/usbprogrammerfinder_win.cpp new file mode 100644 index 0000000..a8e04af --- /dev/null +++ b/usbprogrammerfinder_win.cpp @@ -0,0 +1,32 @@ +//#ifdef Q_WS_WIN + +#include "usbprogrammerfinder.h" +#include +#include +//#include + +USBProgrammerFinder::USBProgrammerFinder(QObject *parent) : + QObject(parent) +{ +} + +QString USBProgrammerFinder::getSerialPortName() +{ + /*//GUID_ + HDEVINFO result = SetupDiGetClassDevs(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); + + if (result != INVALID_HANDLE_VALUE) + { + + } + else + { + return QString::null; + } + + return QString();*/ + + return "COM13"; +} + +//#endif