STM32程序烧录软件设计
? ? ? 本次STM32程序燒錄軟件是基于本人的上一篇博客所設計的BootLoader實現的,因為實際使用過程中,我們不能說每次下載程序都打開一個Python工程來進行下載,到別的電腦上也不一定有Python的環境,最好的方式是能夠做個下載助手,這樣更加的使用和友好。?因為上一篇博客中使用的TCP客戶端是用Python寫的,Python也能用來開發界面軟件,所以程序燒錄軟件用了PyQt5來做,
? ? ? PyQt5可以簡單的理解為Python和QT的融合,QT是非常流行的功能強大的界面開發軟件,PyQt幾乎擁有QT中所有的功能,而且函數形式也是大同小異,使用PyQt開發的感覺總體上來說比用QT開發爽很多,因為PyQt中可以使用Python的各種API,有時候同一種功能的實現既可以用Python API來實現,也可以用QT的API來實現,哪種實現起來更爽就用哪種,因為以前學了一段時間的深度學習,用的是Python語言,如果使用PyQt的話,就可以把深度學習等等看起來比較牛逼的應用結合QT一起開發。
? ? ? 一、燒錄軟件界面設計
? ? ? QT Designer的具體使用方法就不多說了,首先打開Qt designer創建新的QT窗體工程,目前只實現最基本的程序下載功能,界面簡簡單單的不用太花里胡哨。程序燒錄助手的界面如下(隨便拖一拖控件就完成了):
?
? ? 二、將ui文件轉化為py文件
? ? 打開cmd命令行,進入到ui文件所在的目錄,然后輸入命令pyuic5 -o mainwindow.py mainwindow.ui
命令執行正確的話就可以在ui文件同級目錄看到生成的mainwindow.py文件了,接下來我把py文件的名字改成了ISPwindow.py,當然不改也行,生成的py文件就可以供python調用生成界面了。
?
三、如何顯示界面
剛剛將ui界面文件轉換成了py文件,現在我們要將這個界面顯示出來,簡單寫幾行代碼就可以了,我使用的Python編譯器是PyCharm,先看看ISPwindow.py里面的代碼,這是根據ui文件自動生成的。
# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'ISPwindow.ui' # # Created by: PyQt5 UI code generator 5.6 # # WARNING! All changes made in this file will be lost!from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_MainWindow(object):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(525, 306)self.centralWidget = QtWidgets.QWidget(MainWindow)self.centralWidget.setObjectName("centralWidget")self.btn_download = QtWidgets.QPushButton(self.centralWidget)self.btn_download.setGeometry(QtCore.QRect(10, 100, 131, 51))self.btn_download.setObjectName("btn_download")self.edt_ipAddress = QtWidgets.QLineEdit(self.centralWidget)self.edt_ipAddress.setGeometry(QtCore.QRect(30, 10, 151, 21))self.edt_ipAddress.setObjectName("edt_ipAddress")self.label = QtWidgets.QLabel(self.centralWidget)self.label.setGeometry(QtCore.QRect(10, 10, 31, 16))self.label.setObjectName("label")self.edt_port = QtWidgets.QLineEdit(self.centralWidget)self.edt_port.setGeometry(QtCore.QRect(220, 10, 61, 21))self.edt_port.setObjectName("edt_port")self.label_2 = QtWidgets.QLabel(self.centralWidget)self.label_2.setGeometry(QtCore.QRect(190, 10, 31, 16))self.label_2.setObjectName("label_2")self.btn_selectFile = QtWidgets.QPushButton(self.centralWidget)self.btn_selectFile.setGeometry(QtCore.QRect(10, 40, 131, 51))self.btn_selectFile.setObjectName("btn_selectFile")self.btn_tcpConnect = QtWidgets.QPushButton(self.centralWidget)self.btn_tcpConnect.setGeometry(QtCore.QRect(290, 10, 71, 21))self.btn_tcpConnect.setObjectName("btn_tcpConnect")self.pgb_downloadProgress = QtWidgets.QProgressBar(self.centralWidget)self.pgb_downloadProgress.setGeometry(QtCore.QRect(150, 110, 371, 23))self.pgb_downloadProgress.setProperty("value", 24)self.pgb_downloadProgress.setObjectName("pgb_downloadProgress")self.edt_filePath = QtWidgets.QLineEdit(self.centralWidget)self.edt_filePath.setGeometry(QtCore.QRect(150, 50, 361, 21))self.edt_filePath.setObjectName("edt_filePath")self.edt_downloadMsg = QtWidgets.QTextEdit(self.centralWidget)self.edt_downloadMsg.setEnabled(False)self.edt_downloadMsg.setGeometry(QtCore.QRect(10, 180, 511, 121))self.edt_downloadMsg.setObjectName("edt_downloadMsg")self.label_3 = QtWidgets.QLabel(self.centralWidget)self.label_3.setGeometry(QtCore.QRect(10, 160, 91, 16))self.label_3.setObjectName("label_3")# MainWindow.setCentralWidget(self.centralWidget)self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate("MainWindow", "程序燒錄助手V1.0"))self.btn_download.setText(_translate("MainWindow", "下載"))self.edt_ipAddress.setText(_translate("MainWindow", "192.168.1.41"))self.label.setText(_translate("MainWindow", "IP"))self.edt_port.setText(_translate("MainWindow", "5198"))self.label_2.setText(_translate("MainWindow", "端口"))self.btn_selectFile.setText(_translate("MainWindow", "選擇文件"))self.btn_tcpConnect.setText(_translate("MainWindow", "連接"))self.edt_downloadMsg.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" "p, li { white-space: pre-wrap; }\n" "</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n" "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))self.label_3.setText(_translate("MainWindow", "下載信息"))然后創建一個新的py文件 ISPdownloader.py,內容如下:
from ISPwindow import Ui_MainWindow from PyQt5.QtWidgets import *class Demo(QWidget, Ui_MainWindow):def __init__(self):super(Demo, self).__init__()self.setupUi(self)if __name__ == '__main__':app = QApplication(sys.argv)demo = Demo()demo.show()sys.exit(app.exec_())?這樣就可以把界面顯示出來了,效果如下,其實和Qt designer中的效果是一樣的。
?
?四、編寫功能實現
?在我的上一篇博客里面已經實現了使用TCP通信燒寫程序的基本功能了,現在只需要做做界面上控件的功能映射就可以了,還是原來的套路,實現代碼如下:
import sys from socket import * from ISPwindow import Ui_MainWindow from PyQt5.QtWidgets import * import os import ctypes import tracebackclass Demo(QWidget, Ui_MainWindow):def __init__(self):super(Demo, self).__init__()self.setupUi(self)self.btn_selectFile.clicked.connect(self.slot_openFile)self.btn_tcpConnect.clicked.connect(self.slot_tcpConnect)self.btn_download.clicked.connect(self.slot_downloadFile)self.pgb_downloadProgress.setValue(0)self.edt_downloadMsg.setEnabled(True)self.edt_downloadMsg.setReadOnly(True)self.edt_filePath.setEnabled(True)self.edt_filePath.setReadOnly(True)self.edt_downloadMsg.setText("")self.filePath = ""self.tcpHost = 'localhost'self.tcpRecBufSize = 2048self.tcpAddr = ""self.isTcpConnected = Falseself.maxFIleSize = 384*1024self.fileSize = 0self.fileCRC = 0def slot_openFile(self):filePath, _ = QFileDialog.getOpenFileName(self, "選擇下載文件", os.getcwd(), "Bin File (*.bin)")if filePath == "":if self.filePath == "":QMessageBox.warning(self, "文件選擇錯誤", "沒有選擇下載文件,請重新選擇!", QMessageBox.Ok, QMessageBox.Ok)returnelse:filePath = self.filePathsize = os.path.getsize(filePath)if size > self.maxFIleSize:QMessageBox.warning(self, "文件選擇錯誤", "下載文件太大了,最大為384KB,請重新選擇!", QMessageBox.Ok, QMessageBox.Ok)returnself.fileSize = sizeself.filePath = filePathself.edt_filePath.setText(self.filePath)def slot_downloadFile(self):try:self.DownloadFile()except Exception as e:self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + '\r\n下載出錯')traceback.print_exc()def slot_tcpConnect(self):if self.isTcpConnected == False:try:self.tcpSock = socket(AF_INET, SOCK_STREAM)self.tcpAddr = (self.edt_ipAddress.text(), int(self.edt_port.text()))self.tcpSock.connect(self.tcpAddr)self.btn_tcpConnect.setText("斷開")self.isTcpConnected = Trueexcept Exception as e:self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + '\r\n連接失敗!')traceback.print_exc()returnself.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + '\r\n連接成功!')else:self.tcpSock.close()self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + '\r\n斷開成功!')self.btn_tcpConnect.setText("連接")self.isTcpConnected = Falsedef CalculateFileCRC(self, filePath):file = open(filePath, "rb")buf = file.read(4)crc = 0while len(buf) > 0:crc = crc ^ (int.from_bytes(buf, byteorder='little', signed=False))buf = file.read(4)file.close()return crcdef DownloadFile(self):if self.filePath == "":QMessageBox.warning(self, "提示", "請選擇下載文件!", QMessageBox.Ok, QMessageBox.Ok)returnif self.isTcpConnected == False:QMessageBox.warning(self, "提示", "請先連接到開發板!", QMessageBox.Ok, QMessageBox.Ok)return# 傳輸下載頭self.fileCRC = self.CalculateFileCRC(self.filePath)headInfo = []headInfo.append(ctypes.c_uint32(0x55591012))headInfo.append(ctypes.c_uint32(~0x55591012))headInfo.append(ctypes.c_uint32(0x00000000))headInfo.append(ctypes.c_uint32(self.fileSize))headInfo.append(ctypes.c_uint32(self.fileCRC))headInfo.append(ctypes.c_uint32(headInfo[0].value ^ headInfo[1].value ^ headInfo[2].value ^ headInfo[3].value ^ headInfo[4].value))DownloadHead = bytes()for item in headInfo:DownloadHead = DownloadHead + bytes(item)self.tcpSock.send(DownloadHead)file = open(self.filePath, "rb")downloadLen = 0self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n下載中......")while True:buf = file.read(120)if len(buf) <= 0:if downloadLen == self.fileSize:self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n下載成功!")else:self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n下載出錯!")self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n文件大小為" + str(self.fileSize) + "Byte,實際下載大小為" + str(downloadLen) + "Byte")break;self.tcpSock.send(buf)rec = self.tcpSock.recv(self.tcpRecBufSize)if buf != rec:self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n下載出錯!")self.edt_downloadMsg.setText(self.edt_downloadMsg.toPlainText() + "\r\n恢復數據包不正確")breakdownloadLen = downloadLen + len(buf)self.pgb_downloadProgress.setValue(downloadLen * 100 / self.fileSize)#程序下載完,開發板程序跳轉到APP之后TCP連接就斷了self.tcpSock.close()self.btn_tcpConnect.setText("連接")self.isTcpConnected = Falseif __name__ == '__main__':app = QApplication(sys.argv)demo = Demo()demo.show()sys.exit(app.exec_())? ? 五、效果演示
? ? STM32端的BootLoader代碼還是上一篇博客的代碼,一點沒改過,接下來看看效果。
?
? ? 六、將工程打包成exe文件
? ? 開發的工作都做好了,調試也沒問題了,然后將python工程打包成exe文件就可以拿到其他人的電腦上去用了。要打包成exe文件,首先打開cmd命令窗口,進入到python工程的ISPdownloader.py文件所在的同級目錄,然后運行命令pyinstaller.exe -F -w ISPdownloader.py,運行沒問題的話就生成了兩個文件夾,build和dist。exe文件就在dist目錄里面。
?
其實軟件也沒實現太多的功能,打包一下有15M左右,感覺有點大,上網查了很多資料,貌似PyQt開發界面軟件就是有這樣的一個讓人不爽的地方,exe文件比較大,但也能理解啊,里面要包含一些Python和Qt的庫,肯定會大一點的。
?
? ? 七、結束語
? ? 到這里,就實現了通過上位機軟件更新STM32端程序的功能了,還是比較好實現的。我們只需要把設備連接到局域網里面來,就可以實現遠程對設備程序的更新了,用網絡下載比用燒錄器或者是串口下載程序方便很多。
總結
以上是生活随笔為你收集整理的STM32程序烧录软件设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: scratch python的区别ev3
- 下一篇: 【D-S证据理论】学习笔记