K230使用两种方法实现按键中断

对于k230,想实现按键中断,我们有两种方法:定时器检测和多线程检测:
定时器:定时器就相当于单片机内有一个闹钟,每到点就会去提醒CPU执行一个任务(函数)
目前k230有六个硬件定时器,但目前只能使用软件定时器,具体请查看api: 2.11 Timer 模块API手册 — K230 CanMV (canaan-creative.com),
使用方法:
1.初始化定时器:

from machine import Timer
tim = Timer(-1)#0-5六个硬件定时器,-1为软件定时器

软件定时器是使用软件实现,可能没有硬件定时器精确
2.开启定时器:

Timer.init(mode=Timer.PERIODIC, freq=-1, period=-1, callback=None, arg=None)

其中,我们只需要关心mode,period,callback三个参数:
mode: 运行模式,单次或周期
period: Timer运行周期,单位ms
callback: 超时回调函数
其余参数可以不用理会,接下来只需要把官方的按键翻转LED的代码copy过来即可:

'''
实验名称:定时器检测按键按下
版本:v1.0
作者:Tao
实验平台:01Studio CanMV K230
说明:通过定时器检测按键改变LED的亮灭状态
'''

from machine import Pin
from machine import FPIOA
import time
from machine import Timer

#将GPIO52、GPIO21配置为普通GPIO模式
fpioa = FPIOA()
fpioa.set_function(52,FPIOA.GPIO52)
fpioa.set_function(21,FPIOA.GPIO21)

LED=Pin(52,Pin.OUT) #构建LED对象,开始熄灭
KEY=Pin(21,Pin.IN,Pin.PULL_UP) #构建KEY对象
LED.value(0)

# 实例化一个软定时器
tim = Timer(-1)

state=0 #LED引脚状态
def keyfilp():
    global state
    if KEY.value()==0:   #按键被按下
        time.sleep_ms(10) #消除抖动
        if KEY.value()==0: #确认按键被按下
            state=not state  #使用not语句而非~语句
            LED.value(state) #LED状态翻转
            print('KEY')
            while not KEY.value(): #检测按键是否松开
                pass

tim.init(period=100, mode=Timer.PERIODIC,callback=lambda t:keyfilp())

while 1:
    pass

其中,callback的参数中lambda t:是固定内容,主函数中设置while 1是为了让主函数一直运行,因为如果主函数运行结束,那么定时器也会随之结束
多线程:多线程顾名思义就是同时执行多个函数,用法和定时器类似,但是更加简单,只需要启动线程即可:

import _thread
_thread.start_new_thread(keyfilp,())
 #开启线程,第一个参数为回调函数,不需要加括号,第二个参数必须是元组,元组包含线程函数的参数,即使没有参数输入也要填入空元组

使用这个代码后,线程函数就会开始运行,同样的,当主线程执行完毕线程也会停止,接下来是完整代码:

'''
实验名称:多线程检测按键按下
版本:v1.0
作者:Tao
实验平台:01Studio CanMV K230
说明:通过多线程检测按键改变LED的亮灭状态
'''
import _thread
from machine import Pin
from machine import FPIOA
import time
from machine import Timer

#将GPIO52、GPIO21配置为普通GPIO模式
fpioa = FPIOA()
fpioa.set_function(52,FPIOA.GPIO52)
fpioa.set_function(21,FPIOA.GPIO21)

LED=Pin(52,Pin.OUT) #构建LED对象,开始熄灭
KEY=Pin(21,Pin.IN,Pin.PULL_UP) #构建KEY对象
LED.value(0)

state=0 #LED引脚状态
def keyfilp():
    global state
    while 1:
        if KEY.value()==0:   #按键被按下
            time.sleep_ms(10) #消除抖动
            if KEY.value()==0: #确认按键被按下
                state=not state  #使用not语句而非~语句
                LED.value(state) #LED状态翻转
                print('KEY')
                while not KEY.value(): #检测按键是否松开
                    pass


_thread.start_new_thread(keyfilp,()) #开启线程,参数必须是元组
while 1:
    pass

当然,多线程还有许多高级玩法:
1.结束当前线程:

_thread.exit()

2.互斥锁:
在多线程中,一个变量可以同时被多个线程读取,但不能被多个线程修改,这时候我们就需要使用互斥锁,当前线程获取了互斥锁后,当前线程访问(修改)的变量,其他线程就不可以再修改,直到释放为止

    #创建互斥锁
    Lock = _thread.allocate_lock()
    
    #获得互斥锁
    Lock.acquire()

    #释放互斥锁
    Lock.release()

完整API:
1、_thread.get_ident()
返回值:获取当前线程号
2、_thread.start_new_thread(function, args[, kwargs])
开启一个新线程并返回其标识。线程使用参数列表 args(必须是元组)执行函数函数。可选的 kwargs 参数指定关键字参数的字典。当函数返回时,线程将以静默方式退出。当函数因未处理的异常而终止时,将打印堆栈跟踪,然后线程退出(但其他线程继续运行)。
3、_thread.stop_thread(thread_id)
根据线程 id 对线程进行删除
4、_thread.stack_size(size)
设置创建新线程时所使用的栈大小
5、_thread.allocate_lock()
创建一个互斥锁对象
返回值:返回互斥锁对象
6、lock.acquire()
获取锁
返回值:成功返回 True ,失败返回 False 。
7、lock.release()
释放锁
8、lock.delete_locked()
删除锁
9、_thread.locked()
返回值:返回锁的状态,True 表示备某个线程占用,False 则表示没有备占用
注意:为什么要使用线程锁,这是由于在程序中,当主线程一旦运行结束,那抹就会关闭其余线程。这将会导致主线程或早或晚地结束进程,这是线程锁就会起到作用,主线程可在其余子线程结束后立即退出线程。

注意到你在定时器实现按键中断中使用了 time.sleep,这通常不是一个好的习惯,我按你的思路作了一些改动,用记录按键状态持续次数的方式实现了类似功能:

'''
    Led Toggle
    Written by tiny_fish
'''
from machine import *
from media.sensor import *
from media.display import *
from media.media import *

FPIOA().set_function(52, FPIOA.GPIO52)
FPIOA().set_function(21, FPIOA.GPIO21)

led = Pin(52, Pin.OUT)
key = Pin(21, Pin.IN, Pin.PULL_UP)

keyCount, ledState = 0, 0

def timerCallback(x):
    global keyCount, ledState
    # print(key.value(), keyCount)
    if key.value() == 0:
        keyCount += 1
        if keyCount == 50:
            # 50 ms
            print('led toggle')
            ledCount = 0
            ledState ^= 1
            led.value(ledState)
    else:
        keyCount = 0

tim = Timer(-1) # -1 sf timer
tim.init(period = 1, mode = Timer.PERIODIC, callback = timerCallback)

while True:
    pass

你说得对,在定时器中需要避免使用延迟和死循环,以免阻碍主程序的运行