MicroPython优雅的实现中断

MicroPython优雅的实现中断

五月 13, 2021

中断就像它的声音一样,是一个 “中断 “程序正常流程的事件。在一般的情况下,我们处理的是外部硬件中断,这意味着在程序继续运行之前,需要处理一个信号或状态变化。

中断的基本实现方法

为了实现中断,我们需要将一个引脚定义为 “中断输入”, 定义该点上的状态变化被认为是一个中断。

1
2
3
4
import machie

int = machine.Pin(1, machine.Pin.IN, machine.Pin.PULL_DOWN)
# 1 号引脚,输入模式,下拉电阻

同时,还需要创建了一个 “中断处理程序 “函数,我们希望在检测到中断时运行该函数。

1
2
3
def interrupt_handler(pin):
# 参数 `pin` 代表触发该中断的引脚
print("%d号引脚触发中断!" % pin)

然后将 “中断处理程序 “与 “中断输入 “配对。

1
int.irq(trigger=machine.Pin.IRQ_RISING, handler=interrupt_handler)

现在,每当中断输入条件发生时,Pico 将停止它正在做的任何事情,并执行 “中断处理程序”。然后它将恢复到原来的位置。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import machie
import utime

high = machine.Pin(0, machine.Pin.OUT)
high.value(1)
int_pin = machine.Pin(1, machine.Pin.IN, machine.Pin.PULL_DOWN)


def interrupt_handler(pin):
print("%s触发中断!" % str(pin))


int_pin.irq(trigger=machine.Pin.IRQ_RISING, handler=interrupt_handler)
while True:
print("Running!")
utime.sleep(1)

运行这个脚本

0号引脚和1号引脚间连接一个开关,当按下这个开关时,即打印

1
Pin(1, mode=IN, pull=PULL_DOWN)触发中断!

细心的朋友应该发现了,按钮按一次,中断会触发多次,我们可以通过判断中断与上一次中断的时间来决定是否执行中断函数,这部分非常简单,大家自己改改就行了。

优雅的实现中断

上面基本方法虽然简单,但略显发杂,代码比较乱,下面让我们使用Python装饰器封装一个中断实现。

如果你对装饰器不了解,你仍然可以使用该方法,但如果想自己DIY的话,可以看看我的这篇文章

创建interrupt.py,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import machine
import utime


class Interrupt:
def __init__(self,
pin,
pull=machine.Pin.PULL_DOWN,
trigger=machine.Pin.IRQ_RISING,
time=1000,
before=None,
final=None):
self.pin = machine.Pin(pin, machine.Pin.IN, pull)
self.trigger = trigger
self.time = time
self.last_time = utime.ticks_ms()
self.before = before
self.final = final

def __call__(self, func):
def wrapped_func(*args, **kwargs):
if self.before is not None:
self.before()
this_time = utime.ticks_ms()
res = None
if this_time - self.last_time > self.time:
self.pin.irq(handler=None)
res = func(*args, **kwargs)
self.pin.irq(handler=wrapped_func)
self.last_time = this_time
if self.final is not None:
self.final()
return res
self.pin.irq(trigger=self.trigger, handler=wrapped_func)
return wrapped_func

相信了解Python装饰器的朋友应该能看懂这个类的工作原理

下面我再写一个中断的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import machine
import utime

from interrupt import Interrupt

high = machine.Pin(0, machine.Pin.OUT)
high.value(1)


@Interrupt(1, time=300)
def handler(pin):
print("Interrupt Detected!")
print("%s触发中断!" % str(pin))


while True:
print("Running!")
utime.sleep(1)

是不是清爽很多?

@Interrupt()可以有6个参数,分别为:

  • pin

    表示由于中断的引脚ID,必填

  • pull

    表示电阻是上拉还是下拉,选填

  • trigger

    表示当引脚电位如何变化时触发中断,选填

  • time

    表示两次触发中断的最短时间,选填

  • before

    表示执行中断函数前执行的函数,注意不论是否满足最短间隔时间都会执行,选填

  • final

    表示执行中断函数后执行的函数,注意不论是否满足最短间隔时间都会执行,选填