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.py或main.py)的开头,这样紧急异常缓冲区就会对其后的所有代码生效。
- micropython.mem_info(verbose: Any | None = None) None¶
打印当前已使用内存的相关信息。如果给定了 verbose 参数,则会打印额外的信息。
所打印的信息依实现而定,但目前包括已使用的栈和堆的数量。在详细模式下,它会打印出整个堆,指明哪些块已被使用、哪些是空闲的。
- micropython.qstr_info(verbose: Any | None = None) None¶
打印当前驻留字符串(interned strings)的相关信息。如果给定了 verbose 参数,则会打印额外的信息。
所打印的信息依实现而定,但目前包括驻留字符串的数量及其占用的 RAM 数量。在详细模式下,它会打印出所有 RAM 驻留字符串的名称。
- micropython.heap_lock() None¶
锁定堆。在锁定期间不能进行任何内存分配,如果尝试任何堆分配,都会引发
MemoryError。锁定是可嵌套的:多次调用
heap_lock()会增加锁定深度。堆会保持锁定状态,直到heap_unlock()被调用了相同的次数为止。如果在堆处于锁定状态时 REPL 变为活动状态,则堆会被强制解锁。
- 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.poll、poll.ipoll、time.sleep和time.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 上下文写入,则不再成立,这通常会导致数据损坏。
- 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非阻塞地写入环形缓冲区,写入量受环形缓冲区中可用空间的限制。返回值:所写入字节数量的整数计数。