由于产品上用到了STC的单片机,而需要自行写一个烧录软件,该软件可以实现STC系列MCU文件的烧录。
本软件最终取自开源的库,因此感谢为开源无私奉献的人们!
一、将开源的STC的库,编译生成exe文件
本示例采用的github上的开源库:https://github.com/grigorig/stcgal
由于该demo采用的是python库,用C++不是很容易调用,因此将其编译为exe可执行文件使用。
具体方法如下:
1、首先将源码下载或者clone下来。
2、安装pyinstaller
pip install pyinstaller
输入$ pyinstaller -v显示版本信息表示安装成功,显示结果如下:
3.6
3、进入stcgal文件夹,输入以下命令:
pyinstaller -F __main__.py frontend.py ihex.py models.py options.py protocols.py utils.py
会在当前文件夹的dist子文件夹下面生成__main__.exe
然后执行 __main__.exe ,会生成如下的帮助命令,表示编译成功,文件可用
usage: __main__.exe [-h] [-a] [-r RESETCMD] [-P {stc89,stc12a,stc12b,stc12,stc15a,stc15,stc8,usb15,auto}] [-p PORT]
[-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D] [-V]
[code_image] [eeprom_image]
stcgal 1.6 - an STC MCU ISP flash tool
(C) 2014-2018 Grigori Goronzy and others
https://github.com/grigorig/stcgal
positional arguments:
code_image code segment file to flash (BIN/HEX)
eeprom_image eeprom segment file to flash (BIN/HEX)
optional arguments:
-h, --help show this help message and exit
-a, --autoreset cycle power automatically by asserting DTR
-r RESETCMD, --resetcmd RESETCMD
shell command for board power-cycling (instead of DTR assertion)
-P {stc89,stc12a,stc12b,stc12,stc15a,stc15,stc8,usb15,auto}, --protocol {stc89,stc12a,stc12b,stc12,stc15a,stc15,stc8,usb15,auto}
protocol version (default: auto)
-p PORT, --port PORT serial port device
-b BAUD, --baud BAUD transfer baud rate (default: 19200)
-l HANDSHAKE, --handshake HANDSHAKE
handshake baud rate (default: 2400)
-o OPTION, --option OPTION
set option (can be used multiple times, see documentation)
-t TRIM, --trim TRIM RC oscillator frequency in kHz (STC15+ series only)
-D, --debug enable debug output
-V, --version print version info and exit
二、通过命令行测试编译是否成功
./__main__.exe -P stc15 -p COM5 -l 1200 -o clock_source=internal -t 33000 test.hex
具体命令解释如下,-P为选择的型号,-p为选择的串口号 ,-l为波特率 -o为选择时钟源,-t为时钟频率 最后test.exe表示烧录的文件的路径。
如有更具体的参数,请参考https://github.com/grigorig/stcgal/blob/master/doc/USAGE.md文件,上面对命令的解释更加详细。
三、编写QT软件
可以调用qprocess,当外部文件命令行调用该文件,具体代码如下:
头文件:
#ifndef STC_ISP_CONFIG_H
#define STC_ISP_CONFIG_H
#include <QObject>
#include <QProcess>
#include <log_generate.h>
//下载参数
typedef struct
{
QString ComName; //串口号
uint32_t BaudRate; //波特率
QString FirmwarePath; //烧录文件路径
uint32_t CrystalFreq; //晶振
}_STC_ISP_DOWNLOAD_PAR;
//烧录是否成功
typedef enum
{
ISP_DOWNLOADING = 0,
ISP_DOWNLOAD_FAIL,
ISP_DOWNLOAD_SUCC,
}_ENUM_STC_ISP_DOWNLOAD;
extern _ENUM_STC_ISP_DOWNLOAD isp_download_state; //程序下载情况
class STC_ISP_Config : public QObject
{
Q_OBJECT
public:
explicit STC_ISP_Config(QObject *parent = nullptr);
signals:
void Signal_STC_ISP_Log(int msgType,QString loginfo);
public slots:
void Slot_Command_Info_Read(); //读取 cmd 执行完命令
void Slot_Command_Error_Read(); //读取 错误输出信号
void Slot_Command_Finished(int exitCode);
bool ISP_Write_Program(_STC_ISP_DOWNLOAD_PAR); //写入程序
private:
QProcess *m_Process; //用于操作命令行工具
};
#endif // STC_ISP_CONFIG_H
源文件:
#include "stc_isp_config.h"
#include <QDebug>
_STC_ISP_DOWNLOAD_PAR ISP_DownloadPar;
_ENUM_STC_ISP_DOWNLOAD isp_download_state; //程序下载情况
bool isp_finish_flag = true; //默认为完成
STC_ISP_Config::STC_ISP_Config(QObject *parent) : QObject(parent)
{
//新的对象声明
m_Process = new QProcess(this);
// m_Process->setReadChannel(QProcess::StandardOutput);
// m_Process->setProcessChannelMode(QProcess::ForwardedChannels);
//可读时 触发信号
connect(m_Process,&QProcess::readyRead,this,&STC_ISP_Config::Slot_Command_Info_Read);
connect(m_Process,&QProcess::readyReadStandardError,this,&STC_ISP_Config::Slot_Command_Info_Read);
connect(m_Process,SIGNAL(finished(int)),this,SLOT(Slot_Command_Finished(int)));
}
//烧录程序
bool STC_ISP_Config::ISP_Write_Program(_STC_ISP_DOWNLOAD_PAR download_par)
{
ISP_DownloadPar = download_par;
isp_download_state = ISP_DOWNLOADING; //正在烧录
//参数合法性检查
if(download_par.FirmwarePath.length() == 0)
{
emit this->Signal_STC_ISP_Log(LOG_ERROR,QString("烧录文件路径无效,请重新选择烧录文件!"));
return false;
}
//如果未完成 则退出
if(!isp_finish_flag)
{
emit this->Signal_STC_ISP_Log(LOG_ERROR,QString("上次烧录程序未执行完成,请稍后重试!"));
return false;
}
//默认处于未完成状态
isp_finish_flag = false;
//文件主路径
QString Command_Main = QString("stcisp.exe");
//arg赋值
QStringList Command_Args;
//型号
Command_Args.append("-P");
Command_Args.append("stc15");
//串口号
Command_Args.append("-p");
Command_Args.append(download_par.ComName);
//波特率
Command_Args.append("-l");
Command_Args.append("1200");
//时钟选择
Command_Args.append("-o");
Command_Args.append("clock_source=internal");
//时钟频率选择
Command_Args.append("-t");
Command_Args.append(QString::number(download_par.CrystalFreq));
//文件名
Command_Args.append(download_par.FirmwarePath);
//执行
m_Process->start(Command_Main,Command_Args);
return true;
}
//读取信息
void STC_ISP_Config::Slot_Command_Info_Read()
{
static uint32_t count = 0;
QByteArray Temp_Array; //当前数组
int CurrentIndex = 0; //当前的索引
QByteArray read_info = m_Process->readAll();
//信息判断
if(read_info.contains("Waiting for MCU, please cycle power")) //重新 上电
{
emit this->Signal_STC_ISP_Log(LOG_NORMAL,QString("串口连接成功,请重新上电!"));
}
else if(read_info.contains("Serial port error: could not open port ")) //串口无效或被占用
{
isp_download_state = ISP_DOWNLOAD_FAIL; //下载失败
emit this->Signal_STC_ISP_Log(LOG_ERROR,QString("打开串口 %1 失败!").arg(ISP_DownloadPar.ComName));
}
else if(read_info.contains("Writing flash:")) //烧录中
{
emit this->Signal_STC_ISP_Log(LOG_NORMAL,QString("串口烧录中,请稍等..."));
}
else if(read_info.contains("Finishing write"))
{
isp_download_state = ISP_DOWNLOAD_SUCC; //下载成功
emit this->Signal_STC_ISP_Log(LOG_HEALTH,QString("程序烧录完成!"));
}
qDebug() << read_info;
}
//出错信息
void STC_ISP_Config::Slot_Command_Error_Read()
{
QByteArray read_info = m_Process->readAllStandardError();
}
//完成
void STC_ISP_Config::Slot_Command_Finished(int exitCode)
{
isp_finish_flag = true;
}