初版发布

This commit is contained in:
2021-07-14 10:54:28 +08:00
parent 64dff5f90d
commit c22887658f
8 changed files with 898 additions and 0 deletions

0
avr_isp/__init__.py Normal file
View File

30
avr_isp/chipDB.py Normal file
View File

@@ -0,0 +1,30 @@
'''
Created on 2017年8月15日
@author: CreatBot-SW
'''
#===============================================================================
# Database of AVR chips for avr_isp programming. Contains signatures and flash sizes from the AVR datasheets.
# To support more chips add the relevant data to the avrChipDB list.
#===============================================================================
avrChipDB = {
'ATMega1280': {
'signature': [0x1E, 0x97, 0x03],
'pageSize': 128,
'pageCount': 512,
},
'ATMega2560': {
'signature': [0x1E, 0x98, 0x01],
'pageSize': 128,
'pageCount': 1024,
},
}
def getChipFromDB(sig):
for chip in avrChipDB.values():
if chip['signature'] == sig:
return chip
return False

66
avr_isp/intelHex.py Normal file
View File

@@ -0,0 +1,66 @@
'''
Created on 2017年8月15日
@author: CreatBot-SW
'''
import io
#===============================================================================
# Module to read intel hex files into binary data blobs.
# IntelHex files are commonly used to distribute firmware
# See: http://en.wikipedia.org/wiki/Intel_HEX
#===============================================================================
def readHex(filename):
"""
Read an verify an intel hex file. Return the data as an list of bytes.
"""
data = []
extraAddr = 0
f = io.open(filename, "r")
for line in f:
line = line.strip()
if line[0] != ':':
raise formatError("Hex file must start with ':' @ " + line)
recLen = int(line[1:3], 16)
addr = int(line[3:7], 16) + extraAddr
recType = int(line[7:9], 16)
if len(line) != recLen * 2 + 11:
raise formatError("Length error in hex file @ " + line)
checkSum = 0
for i in range(0, recLen + 5):
checkSum += int(line[i * 2 + 1:i * 2 + 3], 16)
checkSum &= 0xFF
if checkSum != 0:
raise formatError("Checksum error in hex file @ " + line)
if recType == 0: # Data record
while len(data) < addr + recLen:
data.append(0)
for i in range(0, recLen):
data[addr + i] = int(line[i * 2 + 9:i * 2 + 11], 16)
elif recType == 1: # End Of File record
pass
elif recType == 2: # Extended Segment Address Record
extraAddr = int(line[9:13], 16) * 16
elif recType == 3: # Start Segment Address Record
raise formatError("Dont support record type 03")
elif recType == 4: # Extended Linear Address Record
extraAddr = int(line[9:13], 16) << 16
elif recType == 5: # Start Linear Address Record
raise formatError("Dont support record type 05")
else:
print(recType, recLen, addr, checkSum, line)
f.close()
return data
class formatError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)

78
avr_isp/ispBase.py Normal file
View File

@@ -0,0 +1,78 @@
'''
Created on 2017年8月15日
@author: CreatBot-SW
'''
from avr_isp import chipDB
#===============================================================================
# General interface for Isp based AVR programmers.
# The ISP AVR programmer can load firmware into AVR chips. Which are commonly used on 3D printers.
#
# Needs to be subclassed to support different programmers.
# Currently only the stk500v2 subclass exists.
#===============================================================================
class IspBase():
"""
Base class for ISP based AVR programmers.
Functions in this class raise an IspError when something goes wrong.
"""
def programChip(self, flashData):
""" Program a chip with the given flash data. """
self.curExtAddr = -1
self.chip = chipDB.getChipFromDB(self.getSignature())
if not self.chip:
raise IspError("Chip with signature: " + str(self.getSignature()) + "not found")
self.chipErase()
print("Flashing %i bytes" % len(flashData))
self.writeFlash(flashData)
print("Verifying %i bytes" % len(flashData))
self.verifyFlash(flashData)
def getSignature(self):
"""
Get the AVR signature from the chip. This is a 3 byte array which describes which chip we are connected to.
This is important to verify that we are programming the correct type of chip and that we use proper flash block sizes.
"""
sig = []
sig.append(self.sendISP([0x30, 0x00, 0x00, 0x00])[3])
sig.append(self.sendISP([0x30, 0x00, 0x01, 0x00])[3])
sig.append(self.sendISP([0x30, 0x00, 0x02, 0x00])[3])
return sig
def sendISP(self, data):
"""
Send data to chip, needs to be implemented in a subclass.
"""
raise IspError("Called undefined sendISP")
def chipErase(self):
"""
Do a full chip erase, clears all data, and lockbits.
"""
self.sendISP([0xAC, 0x80, 0x00, 0x00])
def writeFlash(self, flashData):
"""
Write the flash data, needs to be implemented in a subclass.
"""
raise IspError("Called undefined writeFlash")
def verifyFlash(self, flashData):
"""
Verify the flash data, needs to be implemented in a subclass.
"""
raise IspError("Called undefined verifyFlash")
class IspError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)

304
avr_isp/stk500v2.py Normal file
View File

@@ -0,0 +1,304 @@
'''
Created on 2017年8月15日
@author: CreatBot-SW
'''
#===============================================================================
# STK500v2 protocol implementation for programming AVR chips.
# The STK500v2 protocol is used by the ArduinoMega2560 and a few other Arduino platforms to load firmware.
#===============================================================================
import struct, sys
import time
from PyQt5.QtCore import QIODevice, QThread, pyqtSignal
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
from PyQt5.QtWidgets import QApplication
from avr_isp import intelHex, ispBase
class Stk500v2(ispBase.IspBase, QSerialPort):
progressCallback = pyqtSignal(int, int)
def __init__(self):
super(Stk500v2, self).__init__()
self.seq = 1
self.lastAddr = -1
self.portInfo = None
def connect(self, port = 'COM4', speed = 115200):
self.portInfo = QSerialPortInfo(port)
self.setPort(self.portInfo)
self.setBaudRate(speed)
if self.portInfo.isNull():
raise portError(portError.errorInvalid, port)
else:
if self.portInfo.isBusy():
raise portError(portError.errorBusy, port)
else:
if self.open(QIODevice.ReadWrite):
# self.setBreakEnabled()
self.entryISP()
else:
raise portError(portError.errorOpen, port)
def close(self):
super(Stk500v2, self).close()
self.portInfo = None
def entryISP(self):
self.seq = 1
# Reset the controller
self.setDataTerminalReady(True)
QThread.msleep(100)
self.setDataTerminalReady(False)
QThread.msleep(200)
self.clear()
recv = self.sendMessage([1])[3:]
if "".join([chr(c) for c in recv]) != "AVRISP_2":
raise ispBase.IspError("Unkonwn bootloaders!")
if self.sendMessage([0x10, 0xc8, 0x64, 0x19, 0x20, 0x00, 0x53, 0x03, 0xac, 0x53, 0x00, 0x00]) != [0x10, 0x00]:
raise ispBase.IspError("Failed to enter programming mode!")
def leaveISP(self):
if self.portInfo is not None:
if self.sendMessage([0x11]) != [0x11, 0x00]:
raise ispBase.IspError("Failed to leave programming mode!")
def isConnected(self):
return self.isOpen()
def sendISP(self, data):
recv = self.sendMessage([0x1D, 4, 4, 0, data[0], data[1], data[2], data[3]])
return recv[2:6]
def writeFlash(self, flashData):
# Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
pageSize = self.chip['pageSize'] * 2
flashSize = pageSize * self.chip['pageCount']
if flashSize > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
loadCount = (len(flashData) + pageSize - 1) // pageSize
for i in range(0, loadCount):
self.sendMessage([0x13, pageSize >> 8, pageSize & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flashData[(i * pageSize):(i * pageSize + pageSize)])
self.progressCallback.emit(i + 1, loadCount * 2)
def verifyFlash(self, flashData):
# Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
flashSize = self.chip['pageSize'] * 2 * self.chip['pageCount']
if flashSize > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
loadCount = (len(flashData) + 0xFF) // 0x100
for i in range(0, loadCount):
recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102]
self.progressCallback.emit(loadCount + i + 1, loadCount * 2)
for j in range(0, 0x100):
if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]:
raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + j))
def fastReset(self):
QThread.msleep(50)
self.setDataTerminalReady(True)
self.setDataTerminalReady(False)
def sendMessage(self, data):
message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E)
for c in data:
message += struct.pack(">B", c)
checksum = 0
for c in message:
checksum ^= c
message += struct.pack(">B", checksum)
try:
self.write(message)
self.flush()
except:
raise ispBase.IspError("Serial send timeout")
self.seq = (self.seq + 1) & 0xFF
if(self.waitForReadyRead(100)):
return self.recvMessage()
else:
raise ispBase.IspError("Serial recv timeout")
def recvMessage(self):
state = 'Start'
checksum = 0
while True:
s = self.read(1)
if(len(s) < 1):
if(self.waitForReadyRead(20)):
continue
else:
raise ispBase.IspError("Serial read timeout")
b = struct.unpack(">B", s)[0]
checksum ^= b
if state == 'Start':
if b == 0x1B:
state = 'GetSeq'
checksum = 0x1B
elif state == 'GetSeq':
state = 'MsgSize1'
elif state == 'MsgSize1':
msgSize = b << 8
state = 'MsgSize2'
elif state == 'MsgSize2':
msgSize |= b
state = 'Token'
elif state == 'Token':
if b != 0x0E:
state = 'Start'
else:
state = 'Data'
data = []
elif state == 'Data':
data.append(b)
if len(data) == msgSize:
state = 'Checksum'
elif state == 'Checksum':
if checksum != 0:
state = 'Start'
else:
return data
class portError(Exception):
errorInvalid = 0
errorBusy = 1
errorOpen = 2
def __init__(self, value, port):
self.value = value
self.port = str(port)
def __str__(self):
if self.value == self.errorInvalid:
return "Invalid serial port : " + self.port + " !"
elif self.value == self.errorBusy:
return "Serial port " + self.port + " is busy!"
elif self.value == self.errorOpen:
return "Serial port " + self.port + " failed to open!"
class stk500v2Thread(QThread):
stateCallback = pyqtSignal([str], [Exception])
def __init__(self, parent, port, speed, filename, callback = None):
super(stk500v2Thread, self).__init__()
self.parent = parent
self.port = port
self.speed = speed
self.filename = filename
self.callback = callback
self.programmer = None
self.isWork = False
self.finished.connect(self.done)
def run(self):
self.isWork = True
try:
self.programmer = Stk500v2()
if self.callback is not None:
self.programmer.progressCallback.connect(self.callback)
if self.parent is None:
runProgrammer(self.port, self.speed, self.filename, self.programmer)
else:
self.stateCallback[str].emit(self.tr("Connecting..."))
self.msleep(200)
self.programmer.connect(self.port, self.speed)
self.stateCallback[str].emit(self.tr("Programming..."))
self.programmer.programChip(intelHex.readHex(self.filename))
except Exception as e:
if(self.isInterruptionRequested()):
print("Int")
else:
if self.parent is not None:
self.stateCallback[Exception].emit(e)
while(self.isWork):
pass # 等待父进程处理异常
else:
raise e
self.isWork = False
finally:
if self.programmer.isConnected():
self.programmer.fastReset()
self.programmer.close()
self.programmer = None
def isReady(self):
return self.programmer is not None and self.programmer.isConnected()
def done(self):
if self.parent is not None:
if(self.isWork):
self.isWork = False
self.stateCallback[str].emit(self.tr("Done!"))
else:
print("Success!")
else:
print("Failure!")
def terminate(self):
if self.programmer is not None:
self.requestInterruption()
self.programmer.close()
self.programmer = None
return super(stk500v2Thread, self).terminate()
def runProgrammer(port, speed, filename, programmer = None):
""" Run an STK500v2 program on serial port 'port' and write 'filename' into flash. """
if programmer is None:
programmer = Stk500v2()
programmer.connect(port = port, speed = speed)
programmer.programChip(intelHex.readHex(filename))
programmer.close()
def main():
""" Entry point to call the stk500v2 programmer from the commandline. """
programmer = Stk500v2()
try:
if(len(sys.argv) > 2):
programmer.connect(sys.argv[1])
programmer.programChip(intelHex.readHex(sys.argv[2]))
else:
programmer.connect("COM4")
programmer.programChip(intelHex.readHex("D:/OneDrive/Desktop/CreatBot F160 01 EN KTC ( AUTO_LEVELING ).hex"))
except portError as e:
print(e.value)
print("PortError: " + str(e))
except ispBase.IspError as e:
print("IspError: " + str(e))
except intelHex.formatError as e:
print("HexError: " + str(e))
finally:
programmer.close()
if __name__ == '__main__':
# main()
# runProgrammer("COM4", 115200, "D:/OneDrive/Desktop/CreatBot F160 01 EN KTC ( AUTO_LEVELING ).hex")
app = QApplication(sys.argv)
task = stk500v2Thread(None, "COM4", 115200, "D:/OneDrive/Desktop/CreatBot F160 01 EN KTC ( AUTO_LEVELING ).hex")
# task = stk500v2Thread(None, "COM4", 115200, "D:/OneDrive/Desktop/test.hex")
try:
task.start()
except Exception as e:
print(e)
sys.exit(app.exec())

419
firmwareInstaller.py Normal file
View File

@@ -0,0 +1,419 @@
'''
Created on 2017年8月15日
@author: CreatBot-SW
'''
import os
import sys
from PyQt5.Qt import pyqtSignal
from PyQt5.QtCore import QSize, QDir, QTimer
from PyQt5.QtGui import QIcon
from PyQt5.QtSerialPort import QSerialPortInfo
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QGroupBox, \
QRadioButton, QGridLayout, QWidget, QProgressBar, QStatusBar, QComboBox, QLabel, \
QHBoxLayout, QLineEdit, QPushButton, QFileDialog, QCheckBox
from avr_isp.intelHex import formatError
from avr_isp.ispBase import IspError
from avr_isp.stk500v2 import stk500v2Thread, portError
if getattr(sys, 'frozen', False):
bundle_dir = sys._MEIPASS
else:
bundle_dir = os.path.dirname(os.path.abspath(__file__))
def portListAll():
return [port.portName() for port in QSerialPortInfo.availablePorts()]
def portList():
return [port.portName() for port in QSerialPortInfo.availablePorts() if port.description() == "Arduino Mega 2560"]
class countLabel(QLabel):
def __init__(self, text):
super(countLabel, self).__init__(text)
def mouseDoubleClickEvent(self, e):
self.setText("0")
return super(countLabel, self).mouseDoubleClickEvent(e)
class portCombox(QComboBox):
showPopupSignal = pyqtSignal(bool)
def __init__(self):
super(portCombox, self).__init__()
def showPopup(self):
self.showPopupSignal.emit(True)
self.setSizeAdjustPolicy(QComboBox.AdjustToContents)
return super(portCombox, self).showPopup()
class mainWindow(QWidget):
def __init__(self):
super(mainWindow, self).__init__()
self.setWindowTitle(self.tr("Firmware Installer"))
self.setWindowIcon(QIcon(os.path.join(bundle_dir, "ico.ico")))
self.setFixedSize(QSize(480, 240))
self.setAcceptDrops(True)
self.portUpdateTimer = QTimer()
self.portUpdateTimer.timeout.connect(self.portUpdate)
self.portUpdateTimer.start(100)
self.autoTimer = QTimer()
self.autoTimer.setSingleShot(True)
self.autoTimer.timeout.connect(self.installFile)
self.task = None
self.initUI()
self.configUI()
self.resize()
def initUI(self):
mainLayout = QVBoxLayout()
self.portBox = QGroupBox(self.tr("Port Selection"))
self.fileBox = QGroupBox(self.tr("File Selection"))
self.ctrlBox = QGroupBox(self.tr("Control"))
# PortBox widget
self.autoRadio = QRadioButton(self.tr("Auto"))
self.manualRadio = QRadioButton(self.tr("Manual"))
self.manualBox = QGroupBox()
manualLayout = QHBoxLayout()
self.manualBox.setLayout(manualLayout)
self.portCombo = portCombox()
self.baudCombo = QComboBox()
manualLayout.addWidget(QLabel(self.tr("Port:")))
manualLayout.addWidget(self.portCombo)
manualLayout.addStretch()
manualLayout.addWidget(QLabel(self.tr("Baudrate:")))
manualLayout.addWidget(self.baudCombo)
manualLayout.addStretch()
portLayout = QGridLayout()
self.portBox.setLayout(portLayout)
portLayout.addWidget(self.autoRadio, 0, 0)
portLayout.addWidget(self.manualRadio, 0, 1)
portLayout.addWidget(self.manualBox, 1, 0, 1, 2)
# FileBox widget
self.file = QLineEdit()
self.file.setPlaceholderText("Please select a hex file.")
self.file.setReadOnly(True)
self.file.setFrame(False)
self.file.setDragEnabled(True)
# self.file.setDisabled(True)
self.fileBtn = QPushButton(self.tr("&Open"))
fileLayout = QHBoxLayout()
self.fileBox.setLayout(fileLayout)
fileLayout.addWidget(QLabel(self.tr("Hex file:")))
fileLayout.addWidget(self.file)
fileLayout.addWidget(self.fileBtn)
# CtrlBox widget
self.installBtn = QPushButton(self.tr("&Install"))
self.stopBtn = QPushButton(self.tr("&Stop"))
self.autoCheck = QCheckBox(self.tr("Auto Install"))
self.autoTimeLabel = QLabel(self.tr("Idle Time:"))
self.autoTimeLabel2 = QLabel(self.tr("s"))
self.autoTime = QLineEdit("2")
self.autoTime.setInputMask("00")
self.autoTime.setMaximumWidth(20)
layout1 = QVBoxLayout()
layout1.addWidget(self.autoCheck)
layout2 = QHBoxLayout()
layout2.addWidget(self.autoTimeLabel)
layout2.addWidget(self.autoTime)
layout2.addWidget(self.autoTimeLabel2)
layout1.addLayout(layout2)
self.autoInstallWidget = QWidget()
self.autoInstallWidget.setLayout(layout1)
ctrlLayout = QHBoxLayout()
self.ctrlBox.setLayout(ctrlLayout)
ctrlLayout.addWidget(self.autoInstallWidget)
ctrlLayout.addStretch()
ctrlLayout.addWidget(self.installBtn)
ctrlLayout.addStretch()
ctrlLayout.addWidget(self.stopBtn)
ctrlLayout.addStretch()
# 进度条和状态栏
self.progress = QProgressBar()
self.progress.setTextVisible(False)
self.progress.setRange(0, 100)
self.statusBar = QStatusBar()
self.statusBar.setSizeGripEnabled(False)
self.tryAgainLabel = QLabel(self.tr("Try again..."))
self.tryAgain = QLabel("0")
self.statusBar.addPermanentWidget(self.tryAgainLabel)
self.statusBar.addPermanentWidget(self.tryAgain)
# 计数栏
self.countBar = QStatusBar()
self.countBar.setSizeGripEnabled(False)
self.countBar.setStyleSheet("QStatusBar::item { border: 0 } QLabel {border:0; font-size: 14px; font-weight: bold}")
countSuccessLabel = QLabel("Success: ")
countFailureLabel = QLabel("Failure: ")
self.countSuccess = countLabel("0")
self.countFailure = countLabel("0")
countSuccessLabel.setStyleSheet("QLabel {color: green}")
self.countSuccess.setStyleSheet("QLabel {color: green}")
countFailureLabel.setStyleSheet("QLabel {color: red}")
self.countFailure.setStyleSheet("QLabel {color: red}")
# print(self.countFailure.mouseDoubleClickEvent())
self.url = QLabel("<a href = www.creatbot.com>www.CreatBot.com</a>")
self.url.setOpenExternalLinks(True)
self.countBar.addWidget(QLabel(""), 1)
self.countBar.addWidget(countSuccessLabel)
self.countBar.addWidget(self.countSuccess, 1)
self.countBar.addWidget(countFailureLabel)
self.countBar.addWidget(self.countFailure, 1)
self.countBar.addWidget(self.url)
self.countBar.addWidget(QLabel(""), 1)
# MainLayout
mainLayout.addWidget(self.portBox)
mainLayout.addWidget(self.fileBox)
mainLayout.addWidget(self.ctrlBox)
mainLayout.addWidget(self.progress)
mainLayout.addWidget(self.statusBar)
mainLayout.addWidget(self.countBar)
self.setLayout(mainLayout)
def configUI(self):
self.baudCombo.addItems([str(baud) for baud in QSerialPortInfo.standardBaudRates()])
self.progress.hide()
self.statusBar.hide()
self.stopBtn.setDisabled(True)
self.portCombo.show()
self.autoRadio.toggled.connect(self.manualBox.setDisabled)
self.autoRadio.toggled.connect(self.autoInstallWidget.setVisible)
self.autoRadio.toggled.connect(self.resize)
self.manualRadio.clicked.connect(self.disableAutoInstall)
self.manualRadio.clicked.connect(self.resize)
self.autoCheck.toggled.connect(self.autoTimeLabel.setEnabled)
self.autoCheck.toggled.connect(self.autoTime.setEnabled)
self.autoCheck.toggled.connect(self.autoTimeLabel2.setEnabled)
self.autoCheck.stateChanged.connect(self.autoStateChangeAction)
self.portCombo.showPopupSignal.connect(self.portUpdate)
self.autoTime.returnPressed.connect(self.autoTimeChangeAction)
self.statusBar.messageChanged.connect(self.stateClearAction)
self.autoCheck.click()
self.autoCheck.click()
self.autoRadio.click()
self.fileBtn.clicked.connect(self.selectFile)
self.installBtn.clicked.connect(self.installFile)
self.installBtn.setFocus()
self.stopBtn.clicked.connect(self.stopInstall)
self.file.__class__.dragEnterEvent = self.dragEnterEvent
def portUpdate(self, forceUpdate = False):
if self.autoRadio.isChecked():
self.baudCombo.setCurrentText("115200")
# self.portCombo.addItems(portList())
self.portCombo.clear()
for port in QSerialPortInfo.availablePorts():
if port.description() not in ["Arduino Mega 2560", "USB-SERIAL CH340"]: # 过滤2560和CH340
continue
self.portCombo.addItem(port.portName() + " (" + port.description() + ")" , port.portName())
else:
currentPortData = self.portCombo.currentData()
if forceUpdate or (currentPortData and currentPortData not in [port.portName() for port in QSerialPortInfo.availablePorts()]):
self.portCombo.clear()
for port in QSerialPortInfo.availablePorts():
self.portCombo.addItem(port.portName() + " (" + port.description() + ")" , port.portName())
self.portCombo.setCurrentIndex(self.portCombo.findData(currentPortData))
self.portCombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.baudCombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
def disableAutoInstall(self):
self.autoCheck.setChecked(False)
def autoStateChangeAction(self, check):
if not check and self.autoTimer.remainingTime() > 0:
self.stopInstall()
def autoTimeChangeAction(self):
self.autoTime.clearFocus()
if self.autoCheck.isChecked() and self.autoTimer.remainingTime() > 0:
self.autoTimer.stop()
self.installFile()
def stateClearAction(self, msg):
if msg is "" and self.statusBar.isVisible():
self.statusBar.hide()
if msg is not "" and self.statusBar.isHidden():
self.statusBar.show()
self.resize()
def selectFile(self):
hexFileDialog = QFileDialog()
hexFileDialog.setWindowTitle(self.tr("Select hex file"))
hexFileDialog.setNameFilter(self.tr("Hex files(*.hex)"))
hexFileDialog.setFileMode(QFileDialog.ExistingFile)
if(self.file.text() is ""):
hexFileDialog.setDirectory(QDir.home()) # 设置为Home目录
else:
fileDir = QDir(self.file.text())
fileDir.cdUp()
if(fileDir.exists()):
hexFileDialog.setDirectory(fileDir) # 设置为当前文件所在目录
else:
hexFileDialog.setDirectory(QDir.home())
if(hexFileDialog.exec()):
self.file.setText(hexFileDialog.selectedFiles()[0])
def installFile(self, notFromButton = True):
if(self.file.text() is ""):
if(not notFromButton):
self.selectFile()
self.installFile(True)
else:
if self.autoTimer.remainingTime() is not -1:
self.autoTimer.stop()
self.portBox.setDisabled(True)
self.fileBox.setDisabled(True)
self.installBtn.setDisabled(True)
self.stopBtn.setEnabled(True)
self.progress.show()
self.resize()
self.task = stk500v2Thread(self, self.portCombo.currentData(), int(self.baudCombo.currentText()), self.file.text(), self.progressUpdate)
self.task.stateCallback[str].connect(self.stateUpdate)
self.task.stateCallback[Exception].connect(self.stateUpdate)
self.task.finished.connect(self.autoAction)
self.task.start()
self.statusBar.showMessage(" ")
def stopInstall(self, succeed = False, autoInstall = False):
if autoInstall:
self.progress.reset()
else:
self.portBox.setEnabled(True)
self.fileBox.setEnabled(True)
self.installBtn.setEnabled(True)
self.stopBtn.setDisabled(True)
self.progress.reset()
self.progress.hide()
self.tryAgain.setText("0")
self.resize()
if succeed:
self.countSuccess.setText(str(int(self.countSuccess.text()) + 1))
self.task = None
else:
if self.autoTimer.remainingTime() is not -1:
self.autoTimer.stop()
if self.task is not None and self.task.isRunning():
self.task.finished.disconnect()
if self.task.isReady():
self.countFailure.setText(str(int(self.countFailure.text()) + 1))
if(self.task.isInterruptionRequested()):
pass
else:
# self.task.requestInterruption()
self.task.terminate()
self.statusBar.clearMessage()
# self.task = None
else:
self.statusBar.clearMessage()
def autoAction(self):
self.stopInstall(True, self.autoCheck.isChecked())
if self.autoCheck.isChecked():
self.autoTimer.start(int(self.autoTime.text()) * 1000)
def resize(self):
self.setFixedHeight(self.sizeHint().height())
def progressUpdate(self, cur, total):
self.progress.setMaximum(total)
self.progress.setValue(cur)
def stateUpdate(self, stateOrError):
self.tryAgainLabel.setHidden(True)
self.tryAgain.setHidden(True)
if self.task.isReady():
self.tryAgain.setText("0")
if type(stateOrError) == str:
# self.statusBar.setStyleSheet("QStatusBar::item { border: 0 } QLabel {color: red; font-weight: bold}")
self.statusBar.setStyleSheet("QStatusBar {font-weight: bold; color: black} QStatusBar::item { border: 0 } QLabel {font-weight: bold}")
if self.task is not None and not self.task.isWork and not self.autoCheck.isChecked():
self.statusBar.showMessage(stateOrError, 3000)
else:
self.statusBar.showMessage(stateOrError)
else:
self.task.requestInterruption()
self.statusBar.setStyleSheet("QStatusBar {font-weight: bold; color: red} QStatusBar::item { border: 0 } QLabel {font-weight: bold; color: red}")
if type(stateOrError) == portError:
if (stateOrError.value in [portError.errorInvalid, portError.errorBusy]) and (int(self.tryAgain.text()) < 20):
self.statusBar.showMessage("PortError: " + str(stateOrError))
self.tryAgain.setText(str(int(self.tryAgain.text()) + 1))
self.tryAgainLabel.setVisible(True)
self.tryAgain.setVisible(True)
self.stopInstall(autoInstall = True)
self.autoTimer.start(1000) # 1秒自动重试
else:
self.statusBar.showMessage("PortError: " + str(stateOrError), 5000)
self.stopInstall()
elif type(stateOrError) == IspError:
if self.autoCheck.isChecked():
self.statusBar.showMessage("IspError: " + str(stateOrError))
self.tryAgainLabel.setVisible(True)
self.tryAgain.setVisible(True)
self.stopInstall(autoInstall = True)
self.autoTimer.start(int(self.autoTime.text()) * 1000)
else:
self.statusBar.showMessage("IspError: " + str(stateOrError), 5000)
self.stopInstall()
elif type(stateOrError) == formatError:
self.statusBar.showMessage("HexError: " + str(stateOrError), 5000)
self.stopInstall()
else:
self.statusBar.showMessage("Error: " + str(stateOrError), 5000)
self.stopInstall()
self.task.isWork = False
self.task.wait(100)
self.task = None
if __name__ == '__main__':
app = QApplication(sys.argv)
win = mainWindow()
win.show()
if(len(sys.argv) > 1): # 关联hex文件自动安装
win.portUpdate()
win.file.setText(sys.argv[1])
win.installBtn.click()
sys.exit(app.exec())

BIN
ico.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
PyQt5~=5.15.4