嵌入式MicroPython中如何写中断处理程序(一)
ztj100 2025-01-09 17:29 14 浏览 0 评论
MicroPython提供了在适当的硬件平台上用Python语言写中断处理函数的能力。中断处理函数,即中断服务例程(ISR),被定义为回调函数,其被作为类似于定时器触发或某引脚上电压改变等事件的响应函数而执行。这些事件在常规程序代码执行中的任何时间均可能发生,也就带来了一些值得注意的问题。其中,有些问题是特定于MicroPython而存在的,有些问题则对于所有实时系统是共性存在的。该篇文档首先关注MicroPython所特有的问题,接着针对新手简要介绍了对于实时系统编程时需要注意的问题。
该篇文档中使用了一些相对模糊的词语,比如"慢"或者"尽可能地快",这是有意为之,因为执行速度具体需依赖于实际应用程序设计而言。而ISR的可接受持续运行时间长短则依赖于中断发生频率,主程序本身和有无其它并发事件的产生而言。
建议及最佳实践
这里详细总结了写中断处理程序时所推荐遵守的原则如下:
MicroPython需要注意问题
1> 紧急异常缓冲区
如果在ISR中发生了错误,MicroPython并不能报告其错误信息,除非已经预先为其创建了特定的内存缓冲区。在带有中断处理的主程序中加入如下代码,可简化程序调试过程。
import micropython
micropython.alloc_emergency_exception_buf(100)
紧急异常缓冲区仅能保留一段异常追踪信息,这意味着在异常处理期间,堆空间被锁定时,如果有第二个异常被抛出,则其异常追踪信息会覆盖掉原来信息,即使第二个异常被干净地处理过了也会这样。这可能会导致在随后打印出缓冲区信息后,使得异常信息看似难以理解。
2> 简洁性
有许多原因可以说明保持ISR尽可能简洁的重要性。在某事件发生后的ISR中应该立即做且仅做那些必需要做的事情,而能够被延迟的操作应该交给主程序循环去做。典型场景下,ISR中会处理引发中断的硬件设备,使其准备就绪以处理下次中断,并更新某共享数据来指示中断已经发生,最后返回主程序。其以这种方式来和主程序完成通信。ISR应尽快返回主程序循环,该点也可以说并不特定于MicroPython,其会在下面章节进行更详细解说。
3> 在ISR和主程序之间通信
通常,在ISR和主程序之间确实需要进行适当通信。最简单的方式便是通过一个或者多个共享的数据对象,其可以为全局数据对象或者通过一个类进行共享。该方式有各种使用限制和需注意的危险,将在后面章节详细介绍。整数对象,字节串和字节数组对象常被用于该场景,还有来自于array模块的数组对象,其能够存储各种数据类型,也经常被使用。
4> 使用对象方法作为回调函数
MicroPython支持该项有力的技术特性,使得ISR能够使用底层代码共享实例变量,同时也使实现设备驱动的类能够同时支持多个设备实例。下面的示例代码使得两个LED以不同的频率闪烁。
import pyb, micropython
micropython.alloc_emergency_exception_buf(100)
class Foo(object):
def __init__(self, timer, led):
self.led = led
timer.callback(self.cb)
def cb(self, tim):
self.led.toggle()
red = Foo(pyb.Timer(4, freq=1), pyb.LED(1))
green = Foo(pyb.Timer(2, freq=0.8), pyb.LED(2))
该示例中,red实例将定时器4和LED1关联起来:定时器4中断发生时,red.cb()被调用,导致LED1改变状态。green实例以相似方式工作:定时器2的中断会导致green.cb()执行和LED2状态翻转。实例方法的使用有两个好处。第一,一个单独的类能够在多个硬件实例对象之间共享代码。第二,作为绑定方法,回调函数的第一个参数是self,这便使得回调函数能够访问实例数据并在连续调用之间保存状态。例如,如果上面的类在构造函数中将变量self.count置为零,cb()可以增加该计数器,然后,red和green实例将保持对各自LED状态翻转次数的独立计数。
5> 创建python对象
不能在ISR中创建python对象实例。这是因为对象实例的创建需要MicroPython从被称为堆的空闲空间中分配内存,但内存分配操作是不可重入的,所以这种操作在中断处理程序中是不被允许的。换句话说,当主程序正在执行内存分配时,可能产生中断,为了保持堆的完整性,python解释器不允许同时在ISR中进行内存分配。这样做的后果是,ISR中也不允许使用浮点运算,因为浮点数在python中均是数据对象。同样,ISR中也不能对列表追加元素。但实际使用过程中,往往很难准确地确定哪些代码执行了对象构造从而引起内存分配并最终引发错误消息:这也是倡导尽量保持ISR代码简短的另一个原因。
避免此问题的一种方法是在ISR中仅使用预分配的缓冲区。例如,在类构造函数中创建一个bytearray实例和一个布尔标志,然后ISR中仅将数据设置到缓冲区中适当位置,并置标志位。这样,当类对象被实例化时,内存分配就发生在主程序代码中,而不是在ISR中。
MicroPython库输入/输出函数通常提供使用预分配缓冲区的参数选项。例如,pyb.i2c.recv()可以接受一个可变缓冲区作为它的一个参数:这使得该函数可以在ISR中被使用。
不使用类或全局对象而创建新对象的另一种方法如下:
def set_volume(t, buf=bytearray(3)):
buf[0] = 0xa5
buf[1] = t >> 4
buf[2] = 0x5a
return buf
这样,在第一次加载函数时(通常是在导入其所在模块时),编译器会实例化默认的buf参数。
当创建对绑定方法的引用时,也会发生对象实例的创建,这意味着ISR中不能将绑定方法传递给函数。一种解决方案是在类的构造函数中创建对绑定方法的引用,并在ISR中传递该引用。比如:
class Foo():
def __init__(self):
self.bar_ref = self.bar # 在此分配内存
self.x = 0.1
tim = pyb.Timer(4)
tim.init(freq=2)
tim.callback(self.cb)
def bar(self, _):
self.x *= 1.2
print(self.x)
def cb(self, t):
# 直接传递self.bar会引起内存分配
micropython.schedule(self.bar_ref, 0)
其它的技术还包括在构造函数中定义和实例化方法等等。
6> python对象的使用
源于python本身的工作方式,其对数据对象的进一步限制也需要考虑在内。当执行import语句时,python代码被编译成字节码,并且一行代码通常可映射到多个字节码。当代码运行时,解释器读取每个字节码,并将其作为一系列机器码指令来执行。假设中断可以在机器码指令执行时的任何时间发生,python的原本代码行可能在中断发生时只被部分执行。因此,在主循环中被修改的python对象(如集合,列表或字典)可能会在中断发生时不能保证其内部一致性。
这样导致的典型后果如下:在极少情况下,ISR可能在某数据对象被部分修改过程中的某时刻开始运行,这样当ISR尝试读取数据对象时,可能导致崩溃发生。因为这种问题通常发生在罕见的随机情况下,所以很难被诊断出来。当然,也有一些方法可以避免该问题,将在下面“临界区”章节进行描述。
清楚地明白到底是什么原因造成了对数据对象的修改十分重要。对内置类型(如字典)数据的修改容易导致问题的发生,但对bytes或bytearray类型数据的修改则一般不会导致这种问题,这是因为字节或字类型数据在内部会被转化为单独的机器码,不可被中断执行:在实时编程的术语中,其被称为原子操作。这样,用户定义的类对象中便可以尽量实例化整数,数组或字节数组,其内容对于主循环和ISR来说均相对可靠有效。
MicroPython可支持任意精度的整数,2**30-1到-2**30之间的任何数值均可被存储在一个字存储空间中,但更大的数值则会以python对象形式被存储。因此,对长整数的修改不能被视作原子性的。在ISR中使用长整数是不安全的,因为当该变量值被修改时,可能会尝试内存分配动作。
7> 克服浮点数的限制
一般来说,最好避免在ISR中使用浮点数:硬件设备通常处理整数,然后在主循环中再进行浮点数转换。然而,有一些DSP算法确实需要进行浮点运算。在有硬件浮点支持的平台上(比如pyboard),其内嵌的ARM Thumb汇编器可以解决该限制。由于在这些平台上,处理器可将浮点值存储在一个字存储空间中,这样便可以通过浮点数数组在ISR和主程序代码之间共享数据。
8> 使用micropython.schedule
该函数使得ISR能够“很快”安排回调函数,使其得以执行。通过该调用该函数,回调函数会排队等待,在堆空间未被锁定的时候再开始执行,因此,其便又能够创建python对象并使用浮点值了。并且,这也会保证回调函数会在主程序完成对python对象的任何修改之后再开始运行,那么,回调函数执行时也就不会遇到被部分修改的数据对象了。
其典型用途是处理传感器原始数据:ISR从传感器硬件获取原始数据,并重新使能中断,之后使用该函数来安排回调函数具体处理数据。
使用micropython.schedule函数安排的回调函数在编写时应遵循下面章节所述通用中断处理程序设计原则。因为这些原则能够避免由于输入/输出活动和修改共享数据所引起的问题,这些问题可能出现在任何先于主程序循环的代码中。
ISR持续时间需要综合考虑中断发生频率来进行调整。如果在前一个回调执行过程中又发生了中断,则另一个回调函数实例将排队等待执行,其将在当前回调函数执行完毕之后再开始运行。因此,持续高频率的中断会带来队列无限增长的风险,最终导致产生RuntimeError异常。
如果要传递给micropython.schedule()的回调是绑定方法,可参考"创建python对象"章节中所述建议。
注:为避免太长,分两篇介绍,关注获取吧...
相关推荐
- 干货 | 各大船公司VGM提交流程(msc船运公司提单查询)
-
VGM(VerifiedGrossMass)要来了,大外总管一本正经来给大家分享下各大船公司提交VGM流程。1,赫伯罗特(简称HPL)首先要注册账户第一,登录进入—选择product------...
- 如何修改图片详细信息?分享三个简单方法
-
如何修改图片详细信息?分享三个简单方法我们知道图片的详细信息里面包含了很多属性,有图片的创建时间,修改时间,地理位置,拍摄时间,还有图片的描述等信息。有时候为了一些特殊场景的需要我们需要对这些信息进行...
- 实用方法分享:没有图像处理软件,怎么将一张照片做成九宫格?
-
在发朋友圈时,如果把自己的照片做成九宫格,是不是更显得高大上?可能你问,是不是要借助图片处理软件,在这里,我肯定告诉你,不需要!!!你可能要问,那怎么实现呢?下面你看我是怎么做的,一句代码都不写,只是...
- 扫描档PDF也能变身“最强大脑”?RAG技术解锁尘封的知识宝藏!
-
尊敬的诸位!我是一名物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与AI的无尽可能。今天有网友问我扫描档的PDF文件能否做知识库,其实和普通pdf处理起来差异...
- 这两个Python库,轻而易举就能实现MP4与GIF格式互转,太好用了
-
mp4转gif的原理其实很简单,就是将mp4文件的帧读出来,然后合并成一张gif图。用cv2和PIL这两个库就可以轻松搞定。importglobimportcv2fromPILimpo...
- python图片处理之图片切割(python把图片切割成固定大小的子图)
-
python图片切割在很多项目中都会用到,比如验证码的识别、目标检测、定点切割等,本文给大家带来python的两种切割方式:fromPILimportImage"""...
- python+selenium+pytesseract识别图片验证码
-
一、selenium截取验证码#私信小编01即可获取大量Python学习资源#私信小编01即可获取大量Python学习资源#私信小编01即可获取大量Python学习资源importjso...
- 如何使用python裁剪图片?(python图片截取)
-
如何使用python裁剪图片如上图所示,这是一张包含了各类象棋棋子的图片。我们需要将其中每一个棋子都裁剪出来,此时可以利用python的...
- Python rembg 库去除图片背景(python 删除图片)
-
rembg是一个强大的Python库,用于自动去除图片背景。它基于深度学习模型(如U^2-Net),能够高效地将前景物体从背景中分离,生成透明背景的PNG图像。本教程将带你从安装到实际应用...
- 「python脚本」批量修改图片尺寸&视频安帧提取
-
【python脚本】批量修改图片尺寸#-*-coding:utf-8-*-"""CreatedonThuAug2316:06:352018@autho...
- 有趣的EXCEL&vba作图(vba画图表)
-
还记不记得之前有个日本老爷爷用EXCEL绘图,美轮美奂,可谓是心思巧妙。我是没有那样的艺术细胞,不过咱有自己的方式,用代码作图通过vba代码将指定的图片写入excel工作表中,可不是插入图片哦解题思...
- 怎么做到的?用python制作九宫格图片,太棒了
-
1.应用场景当初的想法是:想把一张图切割成九等份,发布到微信朋友圈,切割出来的图片,上传到朋友圈,发现微信不按照我排列的序号来排版。这样的结果是很耗时间的。让我深思,能不能有一种,直接拼接成一张...
- Python-连续图片合成视频(python多张图叠加为一张)
-
前言很多时候,我们需要将图片直接转成视频。下面介绍用python中的OpenCV将进行多张图合成视频。cv2安装不要直接用pipinstallcv2,这会报错。有很多人建议用打开window自带的...
- 如何把多个文件夹里的图片提取出来?文件夹整理合并工具
-
在项目管理中,团队成员可能会将项目相关的图片资料分散存储在不同的文件夹中,以便于分类和阶段性管理。然而,当项目进入汇报或总结阶段时,需要将所有相关图片整合到一个位置,以便于制作演示文稿、报告或进行项目...
- 超简单!为图片和 PDF 上去掉水印(pdf图片和水印是一体,怎么去除)
-
作者:某某白米饭...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 干货 | 各大船公司VGM提交流程(msc船运公司提单查询)
- 如何修改图片详细信息?分享三个简单方法
- 实用方法分享:没有图像处理软件,怎么将一张照片做成九宫格?
- 扫描档PDF也能变身“最强大脑”?RAG技术解锁尘封的知识宝藏!
- 这两个Python库,轻而易举就能实现MP4与GIF格式互转,太好用了
- python图片处理之图片切割(python把图片切割成固定大小的子图)
- python+selenium+pytesseract识别图片验证码
- 如何使用python裁剪图片?(python图片截取)
- Python rembg 库去除图片背景(python 删除图片)
- 「python脚本」批量修改图片尺寸&视频安帧提取
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)