MicroPython 交互式解释器模式 (又名 REPL)

本节介绍 MicroPython 交互式解释器模式的一些特性。一个常用的术语是 REPL (read-eval-print-loop),它将用来指代这个交互式提示。

自动缩进

当输入以冒号结尾的 python 语句时(例如 if、for、while),提示符将变为三个点(…),光标将缩进 4 个空格。当你按下回车键时,下一行将在相同缩进或适当的附加缩进后继续。如果你按退格键,那么它将撤消一级缩进。

如果你的光标一直回到开头,则按 RETURN (回车)将执行您输入的代码。下方显示了输入 for 语句后你会看到的内容(下划线表示光标结束的位置):

>>> for i in range(30):
...     _

如果你接着输入 if 语句,将会出现额外的缩进:

>>> for i in range(30):
...     if i > 3:
...         _

现在输入 break ,然后按 RETURN (回车)并按 BACKSPACE (退格)

>>> for i in range(30):
...     if i > 3:
...         break
...     _

最后输入 print(i),按 RETURN (回车),按 BACKSPACE (退格)然后再按 RETURN (回车):

>>> for i in range(30):
...     if i > 3:
...         break
...     print(i)
...
0
1
2
3
>>>

如果前两行都是空格,则不会应用自动缩进。这意味着你可以通过按两次 RETURN (回车)结束输入复合语句,然后按第三次时将结束输入并执行。

自动补全

在 REPL 中输入命令时,如果到当前行输入的内容能对应于某些名称的开头,则按 TAB 将显示可能输入的内容。例如,首先通过输入 import machine 并按 RETURN (回车)来导入机器模块。然后输入 m 并按 TAB,它应该能补全为 machine 。此时输入一个句点 . 并再次按 TAB。你应该会看到如下内容:

>>> machine.
__name__        info            unique_id       reset
bootloader      freq            rng             idle
sleep           deepsleep       disable_irq     enable_irq
Pin

输入的词将尽可能地补全,直到存在多种可补全的选项而导致无法默认选择时。例如,输入 machine.Pin.AF3 并按 TAB,它将补全为 machine.Pin.AF3_TIM。再次按 TAB 将显示可能的扩展:

>>> machine.Pin.AF3_TIM
AF3_TIM10       AF3_TIM11       AF3_TIM8        AF3_TIM9
>>> machine.Pin.AF3_TIM

中断正在运行的程序

你可以通过按 Ctrl-C 来中断正在运行的程序。这将引发一个 KeyboardInterrupt ,并它会带你回到 REPL,前提是你的程序不会捕获 KeyboardInterrupt 异常。

例如:

>>> for i in range(1000000):
...     print(i)
...
0
1
2
3
...
6466
6467
6468
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt:
>>>

粘贴模式

如果你想将一些代码粘贴到终端窗口中,自动缩进功能会表现得很糟糕。例如,如果你有以下 python 代码:

def foo():
    print('This is a test to show paste mode')
    print('Here is a second line')
foo()

然后你尝试将它粘贴到正常的 REPL 中,然后你会看到如下内容:

>>> def foo():
...         print('This is a test to show paste mode')
...             print('Here is a second line')
...             foo()
...
  File "<stdin>", line 3
IndentationError: unexpected indent

如果你按下 Ctrl-E,那么你将进入粘贴模式,这实际上会关闭自动缩进功能,并将前缀的提示从 更改 >>>===。例如:

>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== def foo():
===     print('This is a test to show paste mode')
===     print('Here is a second line')
=== foo()
===
This is a test to show paste mode
Here is a second line
>>>

在粘贴模式中,粘贴空白行也是。粘贴的文本实际上作为文件来编译。按下 Ctrl-D 退出粘贴模式时,则启动编译。

软复位

软复位将重置 python 解释器,但不会重置你连接到 MicroPython 板的方式(例如 USB 串口或 Wifi)。

你可以通过按 Ctrl-D 从 REPL 执行软复位,或者通过从你的 python 代码执行:

machine.soft_reset()

例如,如果你重置 MicroPython 板,并执行 dir() 命令,你会看到如下内容:

>>> dir()
['__name__', 'pyb']

现在创建一些变量再重复 dir() 命令:

>>> i = 1
>>> j = 23
>>> x = 'abc'
>>> dir()
['j', 'x', '__name__', 'pyb', 'i']
>>>

现在,如果你输入 Ctrl-D,并重复执行 dir() 命令,你会看到你的变量已经不再存在:

MPY: sync filesystems
MPY: soft reboot
MicroPython v1.5-51-g6f70283-dirty on 2015-10-30; PYBv1.0 with STM32F405RG
Type "help()" for more information.
>>> dir()
['__name__', 'pyb']
>>>

特殊变量 _ (下划线)

当你使用 REPL 时,你可以执行计算并查看结果。MicroPython 将前一条语句的结果存储在变量 _(下划线)中。因此,你可以使用下划线将结果保存在变量中。例如:

>>> 1 + 2 + 3 + 4 + 5
15
>>> x = _
>>> x
15
>>>

Raw 模式和 Raw 粘贴模式

Raw 模式(也称为 Raw REPL),它并不是人们通常会使用的东西。它旨在用于编写程序,本质上类似于粘贴模式,关闭了输出,并且拥有可选的流控制。

使用 Ctrl-A 即可进入 Raw 模式。然后发送你的 python 代码,然后按下 Ctrl-D。Ctrl-D 代表 ‘OK’ 确认,接着 python 代码将被编译和执行。任何输出(或错误)都将被发回。按下 Ctrl-B 即可离开原始模式并返回常规(又名友好)的 REPL。

Raw 粘贴模式是 Raw REPL 中的一种附加模式,它包括流控制,并在收到代码时就对其进行编译。这使得它在将代码高速传输到设备时更加健壮,并且在接收时也使用更少的 RAM,因为它不需要在编译之前逐字存储代码的副本(与标准 raw 模式不同)

Raw 粘贴模式使用以下协议:

  1. 和一般情况一样通过 ctrl-A 输入 Raw REPL

  2. 写入 3 个字节: (b"\x05A\x01" 即 ctrl-E,然后是 “A”,再然后是 ctrl-A)。

  3. 读取 2 个字节以确定设备是否进入 Raw 粘贴模式:

    • 如果结果是 b"R\x00" ,则说明设备理解命令但不支持 Raw 粘贴模式。

    • 如果结果是 b"R\x01" ,则说明设备确实支持 Raw 粘贴并已进入此模式。

    • 其他情况下,结果应该是 b"ra" 且代表设备不支持 Raw 粘贴;然后你将看到字符串``b”w REPL; CTRL-B to exitrn>”`` ,并放弃当前操作。

  4. 如果设备处于 Raw 粘贴模式,则继续,否则回退到标准 Raw 模式。

  5. 读取 2 个字节,这是存储为 16 位无符号小端整数的流控制窗口增量大小(以字节为单位)。剩余窗口大小变量的初始值应设置为此数字。

  6. 将代码写入设备

    • 当有字节要发送时,写入剩余窗口大小的字节,并将剩余窗口大小减少写入的字节数。

    • 如果剩余窗口大小等于 0,或者有一个字节等待读取,则读取 1 个字节。如果这个字节是 b"\x01" 然后将剩余窗口大小增加步骤 5 中的窗口大小增量。如果这个字节是 b"\x04" 那么代表设备想要结束数据接收,并且 b"\x04" 应该写回设备并且之后不再发送代码。(注意:如果有一个字节等待从设备读取,也不需要立即读取和操作,只要当剩余窗口大小大于 0,设备就将继续消耗传入的字节。)

  7. 将所有代码写入设备后,写入 b"\x04" 以标识数据结束。

  8. 从设备读取数据直到收到 b"\x04"。此时,设备已经接收到并编译了所有发送的代码,并且开始执行。

  9. 设备输出执行代码产生的任何字符。当代码结束(如果能)时, b"\x04" 将被输出,接着是未捕获的任何异常,再然后是 b"\x04"。然后它返回到标准的 Raw REPL 并输出 b">"

例如,在正常(友好) REPL 的新一行中,如果你写:

b"\x01\x05A\x01print(123)\x04"

然后设备将响应如下内容:

b"\r\nraw REPL; CTRL-B to exit\r\n>R\x01\x80\x00\x01\x04123\r\n\x04\x04>"

随着时间的推移,它看起来就会像这样:

# Step 1: enter raw REPL
write: b"\x01"
read: b"\r\nraw REPL; CTRL-B to exit\r\n>"

# Step 2-5: enter raw-paste mode
write: b"\x05A\x01"
read: b"R\x01\x80\x00\x01"

# Step 6-8: write out code
write: b"print(123)\x04"
read: b"\x04"

# Step 9: code executes and result is read
read: b"123\r\n\x04\x04>"

在这个案例下,流控制窗口增量大小为 128,并且在开始时有两个窗口值的数据立即可用,一个来自初始窗口增量值,另一个来自 b"\x01" 发送的显式值。因此,这意味着在等待或检查更多传入流控制字符之前,最多可以写入 256 个字节。

tools/pyboard.py 程序中,使用了 Raw REPL(包括 Raw 粘贴模式),用以在支持 MicroPython 的开发板上执行 Python 代码。