打印
[G32R]

G32R501与PyOCD的进阶应用:一键GUI+打包EXE,让烧写更优雅

[复制链接]
274|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

前情回顾

在上一篇《G32R501与pyocd:一顿饭搞定的适配流程》中,我们讲解了如何让 PyOCD 正确适配 G32R501,关注点是“如何让 PyOCD 认识 M52 内核、如何加载目标脚本、并在命令行里快速完成Flash下载”。

可是,仅仅在命令行里敲命令,远远不能满足“产线/测试人员”的友好度需求。想象一下,产线上每天要烧写几十到几百次固件,一不小心输错命令就很尴尬。

本篇给各位小伙伴分享:①如何给 PyOCD 加一个 Python 写的图形界面(GUI),让烧写操作一键点按;②如何利用 PyInstaller 把这个 Python 项目打包成 EXE,从而让大家在没有安装 Python 的电脑上也能轻松使用。


1. 为什么要做GUI与打包EXE

  1. 更友好、更直观 如果我们给同事展示一个命令行,他们可能会有点“吓人”:mad: 的感觉,担心忘记或输错参数。而一个UI界面则是一目了然,“选择固件→点击下载”这样的流程几乎零学习成本。
  2. 无需安装Python 经常在产线或测试环境中,电脑系统可能很久不会升级,也没有装Python。要想让他们去装Python+库依赖,就比较麻烦:dizzy: 。 而如果能打包成EXE,所有Python解释器、第三方模块都内置其中,对外只需要双击文件即可启动GUI。一旦配置好,就可以在任何Windows机器上复用。
  3. 可延伸的封装思路 有了GUI后,我们还可以更进一步添加“固件版本管理”“设备自动识别”“烧写统计”等功能,把它变成一个灵活的产线工具:lol: 。

2. 实现GUI的思路与技术选型

在Python里做GUI并不复杂,一般常见选项是:

  1. Tkinter(Python标准库自带,轻量基础)
  2. PyQt/PySide(功能强大,界面美观,但相对体量大)
  3. wxPythonKivy等其他选项

对于“简单的产线烧写工具”来说,Tkinter 足够胜任,所以本篇使用 Tkinter 作演示。


3. 设计功能需求:“又简单又够用”

为了在界面上实现基本的烧写操作,我们梳理了下面几项功能:

  1. 选择自定义脚本 用户可以选择自定义脚本,因为G32R501核心DCS功能需要特定的脚本进行KEY写入,这个我们需要关注。
  2. 选择固件 用户能通过文件对话框,选择 .hex.bin 格式固件。
  3. 擦除操作 一键擦除Flash,让G32R501的存储空间干净如新。
  4. 下载操作 把选定的固件文件下载到目标芯片中,并可通过进度提示或成功/失败信息告诉用户结果。
  5. 仿真器识别 如果电脑插着多个调试器或多个板子,可以列出来让用户选。

4. 代码层级:分文件设计

以下是示例代码,思路供参考。我们可以根据项目规模或个人习惯决定是否拆分成多个 .py 文件;也可以使用一个 .py 文件简单粗暴写齐。这里分拆成若干功能模块,好处是层次更清晰,也便于后续维护。

4.1 文件结构概览

假设我们有个目录“PyOCDDownloadToolGUI”,内部结构大概是:

PyOCDDownloadToolGUI/
├─ main.py                  # 程序入口 - 启动GUI、Tkinter界面逻辑
├─ download_operation.py    # 下载功能
├─ erase_operation.py       # 擦除功能
├─ file_selector.py         # 打开文件对话框
├─ get_debuggers.py         # 获取当前CMSIS-DAP设备列表
└─ get_supported_targets.py # 获取当前pyocd支持芯片列表

下面我们聚焦主要内容,展示部分代码,以下两段代码演示了如何调用 pyocd 的命令行。

erase_operation.py

"""
实现对目标芯片的全擦除函数 erase_chip()。
若用户勾选了自定义脚本路径,则在命令行中附加 --script=<脚本路径>。
"""
import subprocess
import sys

def erase_chip(target_name, user_script=None):
    """
    Perform chip erase using pyOCD command. 
    - target_name: e.g. 'g32r501dxx'
    - user_script: path to user script, or None if not used
    Return the command output for logging or error info.
    """
    cmd = ["pyocd", "erase", "--chip", "--target", target_name]
    if user_script:
        cmd.append(f"--script={user_script}")
    try:
        if sys.platform.startswith("win"):
            # Windows专用
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = 0  # 0 对应 SW_HIDE
            result = subprocess.run(
                cmd,
                startupinfo=si,
                capture_output=True,
                text=True,
                check=False
            )
        else:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=False
            )

        return result.stdout + result.stderr
    except Exception as e:
        return f"Error executing erase_chip: {e}"

download_operation.py

"""
实现下载固件函数 download_firmware(),
针对 .bin 文件可附加 --format bin 和 --base-address 参数;
针对 .hex 文件则直接 pyocd load -t <target> <file_path>。
"""

import subprocess
import sys

def download_firmware(target_name, file_path, file_type, user_script=None, base_address=None):
    """
    Download firmware to target chip using 'pyocd load'.
    - target_name: e.g. 'g32r501dxx'
    - file_path: absolute path of the firmware
    - file_type: 'bin' or 'hex'
    - user_script: if not None, specify '--script=xxx'
    - base_address: for .bin format, required if user wants to specify
    Return the command output for logging.
    """
    cmd = ["pyocd", "load", "-t", target_name]
    # If user_script is specified
    if user_script:
        cmd.append(f"--script={user_script}")
    # If it's bin, add extra options
    if file_type == "bin":
        cmd.extend(["--format", "bin"])
        # If user provided base address
        if base_address is not None:
            cmd.extend(["--base-address", str(base_address)])
    # Finally, add the firmware file path
    cmd.append(file_path)
    try:
        if sys.platform.startswith("win"):
            # Windows专用
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = 0  # 0 对应 SW_HIDE
            result = subprocess.run(
                cmd,
                startupinfo=si,
                capture_output=True,
                text=True,
                check=False
            )
        else:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=False
            )
        return result.stdout + result.stderr
    except Exception as e:
        return f"Error executing download_firmware: {e}"

最终启动效果:

image-20250705181352892.png

5. 打包成EXE:PyInstaller“一键搞定”

下面进入本篇文章最重要的部分:如何把这个GUI打包成一个单独可执行文件

5.1 为什么选用PyInstaller

  • 使用简单:只需写一个 spec文件,或者直接 pyinstaller xxx.py就能包装出 dist文件夹;
  • 依赖收集:它会自动收集 .py 文件、第三方依赖、.dll文件等,打包到一起;
  • 常规方案:是Python社区里最常见、成熟度高的打包工具之一。

5.2 PyInstaller打包我们的exe

(1) 命令行打包

只需在命令行(在main.py同级目录下启动)中执行以下命令:

pyinstaller --onefile --noconsole --name PyOCDDownloadToolGUI main.py

命令解释:

  1. --onefile:生成一个单文件EXE,启动时会自解压到临时目录里。
  2. --noconsole:不弹出额外的命令行窗口,让界面更干净;如果想看调试信息,可以去掉此参数。
  3. --name PyOCDDownloadToolGUI:指定生成的 EXE 文件名;如省略则默认与脚本同名。
  4. main.py:我们的GUI入口脚本。

执行完该命令后,PyInstaller 会在当前目录下生成一个 dist文件夹,里面就有 PyOCDDownloadToolGUI.exe

我们生成的PyOCDDownloadToolGUI.exe在已经安装好pyocd的Windows电脑上就可以使用啦。

(2) 打包pyocd

PyOCD也带有 pyocd.exe 命令行工具。如果我们把它也打包到和 PyOCDDownloadToolGUI.exe同一目录,下次就可以不再额外安装pyocd或python。 打包方法如下:

  1. https://codeload.github.com/pyocd/pyOCD/zip/refs/tags/v0.37.0 下载 pyOCD 的压缩包。

  2. 解压后在根目录会看到 pyocd.py、pyocd.spec 等文件,将这两个文件复制到我们已经完成了 G32R501 适配工作的本地 pyocd 所在目录(注意:这个目录与 pyocd 同级,而不是放到它的子目录下)。比如默认 Python 的 site-packages 路径:

    C:\Users\<用户名>\AppData\Local\Programs\Python\Python312\Lib\site-packages\pyocd

    我们就需要把下载解压后的 pyocd.py、pyocd.spec 放到上述 pyocd 文件夹的“父目录”里。

  3. 在存放 pyocd.spec 的目录(即刚才所述的本地 pyocd 同级目录)启动 cmd,输入命令: pyinstaller pyocd.spec

  4. 等待一段时间打包完成后,就会在 dist 文件夹下生成一个针对 pyocd 的可执行文件及其相关资源,以后就可以直接通过这个打包结果来使用 pyocd 命令行。这样就不必再单独安装 Python 或在其它电脑上安装 pyocd 依赖了。


6. 运行与演示

当我们完成打包后,dist\PyOCDDownloadToolGUI\ 下会出现 PyOCDDownloadToolGUI.exe(名称取决于spec里定义)。把这整个文件夹+我们打包好的pyocd.exe文件夹,拷贝到一台没有Python的Windows电脑上,插上我们的G32R501开发板+调试器,直接双击 PyOCDDownloadToolGUI.exe理论上就能弹出我们的烧写GUI。

试着点击“Download/Erase/Verify”按钮,看看是否可以正常工作。

下面是我直接安装的Windows10 电脑,在无python,无pyocd环境下的演示:

PixPin_2025-07-05_18-09-04.gif


7. 更多可能

  1. 进度条

    如果想做酷一点,可以在下载的时候显示一个进度条,PyOCD的Flash API并非默认回调进度,但我们可以自己在 flash.flash_binary() 之前拆分写入,或在PyOCD的 session 里加一层钩子。

  2. 日志记录或错误捕获

    产线常需要把烧写记录保存到日志文件。可以加一个 log_operation.py来记录每次烧写信息和结果。

  3. 更多的功能扩展

    设置校验、批量烧录、与数据库联动等等……只要我们会玩Python,这些都能实现。

非常感谢阅读这篇文章,如果有任何困难或疑问,欢迎在评论区留言。让我们一起让G32R501的烧写过程越来越好玩、越来越好用!

这里是源码和打包后的exe,请各位玩玩哦(#^.^#)。

  1. upload 附件:pyocd_501_download_ui.part01.rar
  2. upload 附件:pyocd_501_download_ui.part02.rar
  3. upload 附件:pyocd_501_download_ui.part03.rar
  4. upload 附件:pyocd_501_download_ui.part04.rar
  5. upload 附件:pyocd_501_download_ui.part05.rar
  6. upload 附件:pyocd_501_download_ui.part06.rar
  7. upload 附件:pyocd_501_download_ui.part07.rar
  8. upload 附件:pyocd_501_download_ui.part08.rar
  9. upload 附件:pyocd_501_download_ui.part09.rar

使用特权

评论回复
沙发
kai迪皮|  楼主 | 2025-7-5 18:30 | 只看该作者

使用特权

评论回复
板凳
lulugl| | 2025-7-7 09:10 | 只看该作者
这个可以实现调试不?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

253

帖子

11

粉丝