Initial import of SIMM programmer code into Git

This commit is contained in:
Doug Brown 2012-05-13 00:33:24 -07:00
commit 348d6121f4
9 changed files with 1739 additions and 0 deletions

26
ROMSIMMFlasher.pro Normal file
View File

@ -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

11
main.cpp Normal file
View File

@ -0,0 +1,11 @@
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

365
mainwindow.cpp Normal file
View File

@ -0,0 +1,365 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "programmer.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
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<QextPortInfo> 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);
}
}
}

65
mainwindow.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <qextserialenumerator.h>
#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

318
mainwindow.ui Normal file
View File

@ -0,0 +1,318 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>557</width>
<height>403</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<widget class="QWidget" name="controlPage">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="portLabel">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="portList"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="writeGroupBox">
<property name="title">
<string>Write file to SIMM</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="chosenWriteFile">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectWriteFileButton">
<property name="text">
<string>Select file...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="writeToSIMMButton">
<property name="text">
<string>Write to SIMM</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="readGroupBox">
<property name="title">
<string>Read from SIMM to file</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="chosenReadFile">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectReadFileButton">
<property name="text">
<string>Select file...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="readFromSIMMButton">
<property name="text">
<string>Read from SIMM</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="miscGroupBox">
<property name="title">
<string>Miscellaneous</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="identifyButton">
<property name="text">
<string>Identify chips</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="electricalTestButton">
<property name="text">
<string>Electrical test</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Status label:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="progressLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>557</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuAdvanced">
<property name="title">
<string>Advanced</string>
</property>
<addaction name="actionUpdate_firmware"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuAdvanced"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionUpdate_firmware">
<property name="text">
<string>Update firmware...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>actionQuit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>278</x>
<y>201</y>
</hint>
</hints>
</connection>
</connections>
</ui>

776
programmer.cpp Normal file
View File

@ -0,0 +1,776 @@
#include "programmer.h"
#include <QDebug>
#include <QWaitCondition>
#include <QMutex>
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);
}

125
programmer.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef PROGRAMMER_H
#define PROGRAMMER_H
#include <QObject>
#include <QFile>
#include <qextserialport.h>
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

21
usbprogrammerfinder.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef USBPROGRAMMERFINDER_H
#define USBPROGRAMMERFINDER_H
#include <QObject>
#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

View File

@ -0,0 +1,32 @@
//#ifdef Q_WS_WIN
#include "usbprogrammerfinder.h"
#include <windows.h>
#include <setupapi.h>
//#include <tchar.h>
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