复位与启动序列¶
运行 MicroPython 的设备在复位后会遵循特定的启动序列来启动并初始化自身。
备注
下面描述的 _boot.py → boot.py → main.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 套接字会就对端而言被干净地关闭。
已打开的文件。文件系统会保持在有效状态。
某些系统状态在软复位后保持不变,包括:
启动序列¶
当 MicroPython 在硬复位或软复位后启动时,会按以下顺序执行启动序列:
_boot.py¶
这是一个 冻结到 MicroPython 固件中 的内部脚本。MicroPython 在许多移植版本上提供它,用于执行必要的初始化。
例如,在大多数移植版本上,_boot.py 会检测新设备的首次启动,并格式化 内部闪存文件系统 以备使用。
除非你要创建自定义的 MicroPython 构建或添加新的移植版本,否则你可能无需关心 _boot.py。除非你确实清楚自己在做什么,否则最好不要更改其内容。
boot.py¶
可以使用 mpremote 将名为 boot.py 的文件复制到主板的内部 文件系统 中。
如果找到 boot.py,它就会被执行。你可以在 boot.py 中添加代码来执行自定义的一次性初始化(例如,配置主板的硬件)。
一种常见的做法是在 boot.py 中配置主板的网络连接,这样它在复位后就始终可用,可配合 REPL、mpremote 等使用。
警告
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.py 和 main.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 论坛 上提问。