复位与启动序列

运行 MicroPython 的设备在复位后会遵循特定的启动序列来启动并初始化自身。

备注

下面描述的 _boot.pyboot.pymain.py → REPL 序列是固件在每次复位时都会运行的,无论你以何种方式连接——因此它始终适用。当你从 OpenMV IDE 运行脚本时,IDE 会中断当前正在运行的 main.py,并通过其自身的调试协议运行编辑器中打开的脚本来取而代之。它不使用设备上的 REPL,因此本页中特定于 REPL 的内容(交互式提示符、在串口终端按 Ctrl-D / Ctrl-C 等)适用于独立运行和直接的串口终端会话——但启动序列本身适用于所有情况。

硬复位

硬复位启动发生在主板首次上电时,即冷启动。这是对 MCU 硬件的完全复位。

MicroPython 移植代码会初始化所有必要的硬件(包括嵌入式时钟和电源稳压器、内部串行 UART 等),然后启动 MicroPython 环境。已有的 RTC 配置在硬复位后可能会保留,但所有其他硬件状态都会被清除。

相同的硬复位启动序列可由多种事件触发,例如:

  • Python 代码执行 machine.reset()

  • 用户按下主板上的物理复位按钮(如适用)。

  • 从深度睡眠中唤醒(在大多数移植版本上)。

  • MCU 硬件看门狗复位。

  • MCU 硬件欠压检测器。

硬件相关复位触发的具体细节取决于移植版本及相关硬件。可使用 machine.reset_cause() 函数进一步确定复位的原因。

软复位

当 MicroPython 已经在运行时,可以通过 在 REPL 中按 Ctrl-D 或执行 machine.soft_reset() 来触发软复位。

软复位会清除 Python 解释器、释放所有 Python 内存,并重新启动 MicroPython 环境。

软复位会清除的状态包括:

  • 所有 Python 变量、对象、已导入的模块等。

  • 使用 machine 模块 配置的大多数外设。也有极少数例外,例如 machine.Pin 模式(即引脚是输入还是输出、是高电平还是低电平)在大多数移植版本上不会被复位。而更高级的配置(例如 Pin.irq())则总是会被复位。

  • 蓝牙。

  • 网络套接字。已打开的 TCP 套接字会就对端而言被干净地关闭。

  • 已打开的文件。文件系统会保持在有效状态。

某些系统状态在软复位后保持不变,包括:

  • 任何已有的网络连接(以太网、Wi-Fi 等)在 IP 网络层仍然保持活动。从代码中查询 网络接口 可能会显示网络接口仍处于活动状态并配置了 IP 地址等。

  • 活动的 REPL 在软复位前后看起来是连续的,但在某些特殊情况下例外:

    • 串行 UART REPL 会恢复其默认的硬件配置(波特率等)。

  • 软复位通常不会改变 CPU 时钟速度。

  • RTC 配置(即当前时间的设置)不会被软复位改变。

启动序列

当 MicroPython 在硬复位或软复位后启动时,会按以下顺序执行启动序列:

_boot.py

这是一个 冻结到 MicroPython 固件中 的内部脚本。MicroPython 在许多移植版本上提供它,用于执行必要的初始化。

例如,在大多数移植版本上,_boot.py 会检测新设备的首次启动,并格式化 内部闪存文件系统 以备使用。

除非你要创建自定义的 MicroPython 构建或添加新的移植版本,否则你可能无需关心 _boot.py。除非你确实清楚自己在做什么,否则最好不要更改其内容。

boot.py

可以使用 mpremote 将名为 boot.py 的文件复制到主板的内部 文件系统 中。

如果找到 boot.py,它就会被执行。你可以在 boot.py 中添加代码来执行自定义的一次性初始化(例如,配置主板的硬件)。

一种常见的做法是在 boot.py 中配置主板的网络连接,这样它在复位后就始终可用,可配合 REPLmpremote 等使用。

警告

boot.py 应当始终退出,而不应无限期运行。

根据主板的不同,某些硬件初始化会延迟到 boot.py 退出之后。这包括在基于 STM32 的 OpenMV Cam 上初始化 USB。在这些主板上,boot.py 打印的输出可能要等到 boot.py 运行结束后,才能在内置的 USB 串口上看到。

这种延迟初始化的目的是让你可以在 boot.py 中预先配置特定的硬件,然后让它以正确的配置启动。

备注

有时不使用 boot.py 文件,而是将任何初始化代码放在 main.py 的开头会更简单。

main.py

boot.py 类似,可以将名为 main.py 的文件复制到主板的内部 文件系统 中。如果找到,它会在启动过程中接着被执行。

main.py 用于存放你希望在每次设备启动时运行的任何 Python 代码。

关于 main.py 用法的一些提示:

  • main.py 不一定要退出,可以随意在其中放置无限的 while True 循环。

  • 对于复杂的 Python 应用程序,你不需要把所有代码都放在 main.py 中。main.py 可以作为一个简单的入口点,导入你的应用程序并启动执行:

    import my_app
    my_app.main()
    

    这有助于保持应用程序结构的清晰。它还便于在主板上安装多个应用程序并在它们之间切换。

  • 编写健壮的应用程序时,一种良好的做法是在 main.py 中用异常处理程序包裹代码,以便在代码崩溃时采取适当的措施。例如:

    import machine, sys
    import my_app
    try:
        my_app.main()
    except Exception as e:
        print("Fatal error in main:")
        sys.print_exception(e)
    
    # Following a normal Exception or main() exiting, reset the board.
    # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C),
    # this code will drop to a REPL. Place machine.reset() in a finally
    # block to always reset, instead.
    machine.reset()
    

    否则,在任何崩溃之后或当 main 退出时(见下文),MicroPython 都会进入 REPL。

  • 任何在 boot.py 中设置的全局变量在 main.py 的全局上下文中仍然有效。

  • 为了充分优化闪存使用和内存占用,你可以将 预编译的 main.mpy 和/或 boot.mpy 文件复制到文件系统中,甚至将它们 冻结 到固件构建中。

  • 当软复位从 原始 REPL 模式 发起时(例如,当 mpremote 或其他程序直接与 MicroPython 交互时),会跳过 main.py 的执行。

交互式解释器(REPL)

如果未找到 main.py,或者 main.py 退出,那么 MicroPython 交互式解释器模式(即 REPL) 将立即启动。

备注

即使 main.py 包含一个无限循环,在 REPL 串口上按 Ctrl-C 也会注入一个 KeyboardInterrupt。如果没有异常处理程序捕获它,那么 main.py 将退出,REPL 将启动。

任何在 boot.pymain.py 中设置的全局变量在 REPL 的全局上下文中仍然有效。

REPL 会持续执行,直到 Python 代码触发硬复位或软复位。

软变砖(无法启动)

虽然罕见,但 MicroPython 有可能在启动期间变得无响应,这种状态有时被称为“软变砖”。例如:

  • 如果 boot.py 的执行卡住,导致原生 USB 串口始终无法初始化。

  • 如果 Python 代码重新配置了 REPL 接口,使其无法访问。

请放心,是可以恢复的!

如果你使用 OpenMV IDE,通常只要连接就足够了——IDE 会停止正在运行的 main.py 并接管。如果摄像头完全无法连接,请使用下面的 Factory Reset(出厂重置)。接下来描述的 Ctrl-C 方法适用于直接的串口终端会话——它依赖于设备上的 REPL,而 OpenMV IDE 并不使用它。

KeyboardInterrupt

在许多情况下,打开 REPL 串口并按 Ctrl-C 会注入 KeyboardInterrupt,可能会使正在运行的脚本退出并启动 REPL。从 REPL 中,你可以使用 os.remove() 来删除行为异常的 Python 文件:

import os
os.remove('main.py')

要确认内部文件系统中仍然存在哪些文件:

import os
os.listdir()

出厂重置

如果你无法用上述方法进入 REPL,那么剩下的选择就是出厂重置:擦除内部闪存文件系统的全部内容。如果内部文件系统已损坏,这也是修复方法。

OpenMV IDE 提供了几种内置的方式来执行此操作。首先让摄像头进入其恢复/引导加载程序模式——具体方法因主板而异,请参阅你主板 快速参考 中的 Recovery and debug pins(恢复与调试引脚)一节,了解如何进入该模式。然后在 OpenMV IDE 中点击连接按钮,并按照提示擦除文件系统并重新烧录固件。

警告

擦除文件系统的情况下重新烧录固件通常无法从软变砖中恢复,因为正常的固件更新会保留文件系统的内容。请务必在 OpenMV IDE 提示时选择擦除选项。

如果你遇到困难,请在 OpenMV 论坛 上提问。