前情提要 #
笔者最近在开发一个项目的生产软件,用来测试产品的功能,产品在出厂的最后一步需要烧写 MCU(芯源)和 FPGA(高云)的固件,因此需要给测试人员讲解 keil 和高云的软件怎么烧写固件,增加了学习成本。因此萌生出在生产软件中集成烧写 MCU 和 FPGA 固件的想法。
FPGA 芯片使用的是高云的 GW1N-4D,在高云官方提供的烧录软件中,提供了一个 cli 的可执行文件,于是我们就可以使用 QProcess 来调用这个软件,实现集成烧写 FPGA 固件的功能。
用到的指令有:
.\programmer_cli.exe --scan-cables // 列出当前所有已连接的下载器
.\programmer_cli.exe --scan // 列出当前所有已连接的设备
.\programmer_cli.exe --operation_index 6 --device GW1N-4D --fsFile 固件路径 // 烧写固件
其中
--operation_index 6 代表embFlash Erase,Program,Verify,你可以根据实际情况填写
--device GW1N-4D 代表指定要烧写的芯片型号,你应该根据实际情况填写
--fsFile 固件路径 代表需要烧写的固件,应该使用.fs 结尾的文件
还有更多的参数可以查阅高云提供的《SUG502-1.6_Gowin_Programmer 用户指南.pdf》
开发环境 #
- Qt6.9
- MSVC 2019
- Windows 11 24H2
- Gowin_Programmer Version 1.9.9 (64-bit) build(31129)
在 Qt 中实现调用 Gowin_Programmer #
在 Qt 中,调用另一个可执行文件,我们可以通过 QProcess,可以转入我们想要的参数,并在回调函数中获取可执行文件的输出。
获取所有连接的下载器 #
由于 Gowin_Programmer 不支持输出 json 格式,我直接使用字符串解析来获取它的返回值。
核心代码:
void FpgaUpdateInterface::ScanProbe(QString gowin_cli_path)
{
emit DebugMessage("Scanning probes...");
action_ = Fpga_Proces_Action_ScanProbes;
process_->start(gowin_cli_path, QStringList() << "--scan-cables");
}
void FpgaUpdateInterface::ParseProbeList(const QString& raw_output)
{
qDebug() << "Raw probe scan output:" << raw_output;
/*
PS C:> .\programmer_cli.exe --scan-cables
Cable found: Gowin USB Cable(FT2CH)/0/786/null (USB location:786)
Cost 0.05 second(s)
*/
for (const QString& line : raw_output.split("\r\n")) {
if (line.contains("Cable found:")) {
QString probe = line.mid(QString("Cable found:").length() + 1).trimmed();
probe_list_.append(probe);
}
}
}
获取所有连接的设备 #
同样也是使用字符串解析来获取它的返回值。
核心代码:
vvoid FpgaUpdateInterface::ScanDevice(QString gowin_cli_path)
{
emit DebugMessage("Scanning device...");
action_ = Fpga_Proces_Action_ScanDevice;
process_->start(gowin_cli_path, QStringList() << "--scan");
}
void FpgaUpdateInterface::ParseDeviceList(const QString& raw_output)
{
qDebug() << "Raw device scan output:" << raw_output;
/*
失败
PS C:> .\programmer_cli.exe --scan
Scanning!
Target Cable: Gowin USB Cable(FT2CH)/0/0/null@2.5MHz
Error: No Gowin devices found!
Cost 0.54 second(s)
成功
PS C:> .\programmer_cli.exe --scan
Scanning!
Target Cable: Gowin USB Cable(FT2CH)/0/0/null@2.5MHz
Device Info:
Family: GW1NRF
Name: GW1N-4D GW1NR-4D GW1N-4B GW1NR-4B GW1NRF-4B (One of them)
ID: 0x1100381B
1 device(s) found!
Cost 0.54 second(s)
*/
for (const QString& line : raw_output.split('\n')) {
if (line.contains("ID:")) {
QString id = line.mid(QString("ID:").length() + 1).trimmed();
device_list_.append(id);
}
}
}
烧写固件 #
烧写固件的指令就是上面说的那个指令,不过有一点要注意的是,烧写过程中会有两个进度条,先是烧写固件,随后进行校验,并且 Gowin_Programmer 每次输出都是完整的一行,因此可以直接使用正则表达式来解析输出,这点还是比较方便的。
核心代码:
void FpgaUpdateInterface::StartFirmwareUpdate(FirmwareUpdateParam param)
{
emit DebugMessage("Starting Firmware Update");
QStringList arguments;
arguments << "--operation_index" << QString::number(param.operation_index);
if (!param.target_chip.isEmpty())
{
arguments << "--device" << param.target_chip;
}
if (!param.firmware_path.isEmpty())
{
arguments << "--fsFile" << param.firmware_path;
}
else
{
emit DebugMessage("Firmware path is empty!");
return;
}
qDebug() << "QProcess run command: [" << param.gowin_cli_path << " " << arguments << "]";
action_ = Fpga_Proces_Action_InProgress;
process_->setProcessChannelMode(QProcess::MergedChannels); // 设置进程通道模式
process_->start(param.gowin_cli_path, arguments);
}
void FpgaUpdateInterface::ParseFirmwareUpdateInfo(const QString& output)
{
qDebug() << "Firmware update output:" << output;
// 升级阶段
// gowin/programmer_cli.exe 通常输出类似 "\rProgramming...: [# ] 4% " 的进度信息
// 校验阶段
// gowin/programmer_cli.exe 通常输出类似 "\rVerifying...: [# ] 4% " 的进度信息
QRegularExpression progressRegex(R"(\r(Programming|Verifying)\s*\.{0,3}:\s*\[(#+)\s*\]\s*(\d+)%\s*)");
QRegularExpressionMatch match = progressRegex.match(output);
if (match.hasMatch()) {
QString stage = match.captured(1); // Programming 或 Verifying
int percentage = match.captured(3).toInt(); // 百分比
// 根据阶段处理进度
if (stage == "Programming") {
emit ProgrammingUpdated(percentage);
}
else if (stage == "Verifying") {
emit VerifyingUpdated(percentage);
}
}
}
运行效果 #
结语 #
集成在生产软件中后,通过傻瓜式的页面操作,降低了测试同事的学习成本,也能够减少工作量,算是很有收获的一次功能更新。
本文的完整代码和编译好的可执行程序可以在 GitHub 中找到:QGowin