5. 开关,回调和中断

TPYBoard 开发板上有两个小按键,分别标示为 USR 和 RST。 RST 按键属于复位按键,如果按下的话将使开发板重新运行,相当于将开发板断电再重启。

USR按键供用户使用,且其可以通过声明一个按键对象(Switch object)进行控制。 创建开关对象的方法如下:

>>> sw = pyb.Switch()

如果你得到一个错误的名字PYB不存在,请记住,您可能需要键入import pyb

利用按键对象可以得到按键的状态:::

>>> sw()
False

如果按键被按下将打印 True,松开则打印 False。可以尝试运行上述指令时按下 USR 按键。

5.1. 按键回调函数

尽管按键算是一种简单的构造,但它具有明显的优势特征**sw.callback()**函数。 该回调函数将在按键按下时创建一些东西,且使用了一个中断。 在理解中断工作机制前最好的是用一个例子进行描述。尝试在解释器里边运行如下的代码:

>>> sw.callback(lambda:print('press!'))

这个例子要求每次按下按键时都能打印 press! 字符。 先进行尝试:按下USR按键并观测你 PC 上的输出。 注意该打印将打断目前你在TPYBorad 板上的任何程序,且其属于一种异步中断例程。

尝试如下另一个例子:

>>> sw.callback(lambda:pyb.LED(1).toggle())

这将在每次按键按下时翻转 LED 的亮灭状态,且它能打断当前其他代码的运行。

若要关闭开关回调,只需将回调函数的参数设置为 None 即可。

>>> sw.callback(None)

你可以传递不带参数的函数作为参数给按键回调函数使用。 我们可以充分利用 Python 中的 lamba 声明特性,我们可以用下面的形式替代:

>>> def f():
...   pyb.LED(1).toggle()
...
>>> sw.callback(f)

这将创建一个名为“f”的函数,并将其传递给按键回调函数。 当 lamba 比较复杂时你可以尝试使用这种方法。

注意回调函数一定不能含有任何分配内存的定义(比如不能声明创建列表和元组)。 回调函数应该相对简单。 如果你需要做一个列表,事先作出它,并存储在全局变量(或定义为局部变量并对其进行封装)。 如果需要做一个多次复杂的计算,那么可以用按键回调设置一个标志供其他代码响应使用。

5.2. 中断的原理细节

让我们现在来看看按键回调函数发生时的细节。 当你调用了含有 sw.callback( )的函数时,按键将在其连接引脚产生一个边沿触发(下降沿)外部中断。 这意味着芯片将监听该引脚的任何状态变换,且会发生如下错误:

  • 当开关按下时引脚将发生改变(电平从低到高),芯片处理器将记录这种变化。
  • 处理器完成当前机器指令的执行,退出执行并保存其当前状态(将寄存器的内容推入栈中)。这将停止当前运行的任何代码,例如正在运行的Python脚本。
  • 芯片开始执行与按键相关的特定外部中断触发处理,该处理指向你在 sw.callback( )函数中指定的函数功能并执行。
  • 您的回调函数被执行,直到它完成,返回控制中断处理。
  • 按键中断处理程序返回,而芯片处理器是确认记录该中断已处理。
  • 芯片调回步骤 2 的状态。
  • 继续执行开始时的代码,除了短暂的暂停,此代码好像没有被他中断过。

当同一时间多个中断同时发生上述的过程时将会复杂得多。 这种情况下拥有最高优先级别的中断将被首先执行,其他的中断按各自的优先级数序执行。 开关中断设置在最低优先级。

5.3. 进一步参考

进一步使用硬件中断的信息可参考文档:writing interrupt handlers <isr_rules>.