micropython --- 访问和控制 MicroPython 内部机制

函数

micropython.const(expr: int) int

用于声明某个表达式是常量,以便编译器对其进行优化。该函数应当按如下方式使用:

from micropython import const

CONST_X = const(123)
CONST_Y = const(2 * CONST_X + 1)

以这种方式声明的常量,仍可作为全局变量在其声明所在模块之外访问。另一方面,如果常量名以下划线开头,则它会被隐藏,无法作为全局变量使用,并且在执行期间不占用任何内存。

这个 const 函数会被 MicroPython 解析器直接识别,它作为 micropython 模块的一部分提供,主要是为了让脚本能够通过遵循上述模式,在 CPython 和 MicroPython 下都能运行。

micropython.opt_level(level: int | None = None) int | None

如果给定了 level,则该函数会为后续脚本编译设置优化级别,并返回 None。否则它会返回当前的优化级别。

优化级别控制以下编译特性:

  • 断言:在级别 0 时,断言语句被启用并编译进字节码;在级别 1 及更高级别时,断言不会被编译。

  • 内置的 __debug__ 变量:在级别 0 时,该变量展开为 True;在级别 1 及更高级别时,它展开为 False

  • 源代码行号:在级别 0、1 和 2 时,源代码行号会与字节码一起存储,以便异常能够报告其发生的行号;在级别 3 及更高级别时,行号不会被存储。

默认的优化级别通常是级别 0。

micropython.alloc_emergency_exception_buf(size: int) None

为紧急异常缓冲区分配 size 字节的 RAM(一个合适的大小约为 100 字节)。该缓冲区用于在正常 RAM 分配会失败的情况下(例如在中断处理程序内部)创建异常,从而在这些情形下提供有用的回溯信息。

使用该函数的一个好方法是将其放在主脚本(例如 boot.pymain.py)的开头,这样紧急异常缓冲区就会对其后的所有代码生效。

micropython.mem_info(verbose: Any | None = None) None

打印当前已使用内存的相关信息。如果给定了 verbose 参数,则会打印额外的信息。

所打印的信息依实现而定,但目前包括已使用的栈和堆的数量。在详细模式下,它会打印出整个堆,指明哪些块已被使用、哪些是空闲的。

micropython.qstr_info(verbose: Any | None = None) None

打印当前驻留字符串(interned strings)的相关信息。如果给定了 verbose 参数,则会打印额外的信息。

所打印的信息依实现而定,但目前包括驻留字符串的数量及其占用的 RAM 数量。在详细模式下,它会打印出所有 RAM 驻留字符串的名称。

micropython.stack_use() int

返回一个整数,表示当前正在使用的栈量。其绝对值并不特别有用,更应当用它来计算不同位置处栈使用量的差值。

micropython.heap_lock() None

锁定堆。在锁定期间不能进行任何内存分配,如果尝试任何堆分配,都会引发 MemoryError

锁定是可嵌套的:多次调用 heap_lock() 会增加锁定深度。堆会保持锁定状态,直到 heap_unlock() 被调用了相同的次数为止。

如果在堆处于锁定状态时 REPL 变为活动状态,则堆会被强制解锁。

micropython.heap_unlock() int

将堆锁定深度减一,并以非负整数形式返回新的深度。返回值为 0 表示堆已不再锁定,再次允许进行分配。

micropython.heap_locked() int

以非负整数形式返回当前的堆锁定深度;0 表示堆未被锁定。

注意:该函数在 OpenMV Cam 上不可用。

micropython.kbd_intr(chr: int) None

设置将引发 KeyboardInterrupt 异常的字符。默认情况下,在脚本执行期间该字符设置为 3,对应 Ctrl-C。向该函数传入 -1 会禁用对 Ctrl-C 的捕获,传入 3 则会恢复它。

该函数可用于阻止在通常用于 REPL 的输入字符流上捕获 Ctrl-C,以防该字符流被用于其他用途。

micropython.schedule(func: Callable[[Any], Any], arg: Any) None

将函数 func 调度为“很快”执行。该函数会以 arg 的值作为其唯一参数。“很快”意味着 MicroPython 运行时会在以下条件成立的前提下,尽其所能在最早可能的时间执行该函数,同时它也力求保持高效:

  • 一个被调度的函数永远不会抢占另一个被调度的函数。

  • 被调度的函数总是在“操作码之间”执行,这意味着所有基本的 Python 操作(例如向列表追加元素)都保证是原子的。

  • 某个特定的移植版可能定义了“临界区”,在临界区内绝不会执行被调度的函数。函数可以在临界区内被调度,但要等到退出该区域后才会执行。临界区的一个例子是抢占式中断处理程序(IRQ)。

  • 在原生代码函数内部,除非原生代码调用了专门会执行被调度函数的函数,否则被调度的函数不会被调用。

  • 某些函数包括 poll.pollpoll.ipolltime.sleeptime.sleep_ms(包括零时长的睡眠)会调用被调度的函数。

该函数的一个用途是从抢占式 IRQ 中调度回调。这样的 IRQ 会对其中运行的代码施加限制(例如堆可能被锁定),而调度一个函数稍后调用则可解除这些限制。

在多线程移植版上,被调度函数的行为取决于该移植版是否启用了全局解释器锁(GIL):

  • 如果启用了 GIL,该函数可以抢占任何线程并在其上下文中运行。

  • 如果禁用了 GIL,该函数将只会抢占主线程并在其上下文中运行。

注意:如果从抢占式 IRQ 中调用 schedule(),此时不允许内存分配,而要传给 schedule() 的回调又是一个绑定方法,那么直接传入它会失败。这是因为创建对绑定方法的引用会导致内存分配。一种解决方法是在类构造函数中创建对该方法的引用,并将该引用传给 schedule()。这一点在 参考文档 中的“Creation of Python objects”部分有详细讨论。

用于保存被调度函数的队列是有限的,如果队列已满,schedule() 会引发 RuntimeError

class micropython.RingIO(size: int)
class micropython.RingIO(buffer: bytes | bytearray | memoryview)

为字节提供一个具有流接口的固定大小环形缓冲区。可以将其视为 io.BytesIO 的 FIFO 队列变体。两种构造形式仅在如何提供后备缓冲区方面有所不同:

  • RingIO(size) 在内部分配后备缓冲区。经典的环形缓冲区算法会保留一个字节用于跟踪,因此分配的缓冲区比 size 大一个字节,而实例可容纳完整 size 字节的数据。例如,RingIO(16) 会分配一个 17 字节的缓冲区,并容纳 16 字节的数据。

  • RingIO(buffer) 就地使用所提供的 buffer,而不是分配一个新的。由于会保留一个字节用于跟踪,实例可容纳 len(buffer) - 1 字节的数据。例如,RingIO(bytearray(16)) 容纳 15 字节的数据。

当 RingIO 实例用于单向传递数据时(例如从 IRQ 写入、从非 IRQ 函数读取,或反之),它是 IRQ 安全/线程安全的。如果同一个实例同时从 IRQ 和非 IRQ 上下文写入,则不再成立,这通常会导致数据损坏。

any() int

返回一个整数,表示可读取的字符数量。

read(nbytes: int | None = None) bytes

读取可用的字符。这是一个非阻塞函数。如果指定了 nbytes,则最多读取那么多字节,否则尽可能多地读取数据。

返回值:一个包含所读取字节的 bytes 对象。如果没有可用数据,则为长度为零的 bytes 对象。

readline(nbytes: int | None = None) bytes

读取一行,以换行符结束,如果缓冲区中存在换行符则返回该行,否则返回缓冲区中可用的字节。如果指定了 nbytes,则最多读取那么多字节。

返回值:一个包含所读取行的 bytes 对象。

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

将可用的字节读取到所提供的 buf 中。如果指定了 nbytes,则最多读取那么多字节。否则,最多读取 len(buf) 字节。

返回值:读取到 buf 中的字节数量的整数计数。

write(buf: bytes | bytearray | memoryview) int

将字节从 buf 非阻塞地写入环形缓冲区,写入量受环形缓冲区中可用空间的限制。

返回值:所写入字节数量的整数计数。

close() None

作为标准 stream 接口一部分提供的空操作。对环形缓冲区中的数据没有任何影响。