6.1 中断机制


6.1 中断机制

本节摘要:中断是外部事件进入系统的唯一快速通道,实时系统的微秒级响应全部押在这条通道上。本节拆解中断延迟的构成、讲清中断优先级与任务优先级的关系、立出服务程序的纪律清单,并给出两个最容易踩中的配置陷阱。

「短进短出」是中断服务程序写法的第一行军规:进来只做必须立刻做的事,其余全部交给任务。这条军规人人听过,但多数人第一次违反它时毫无自觉——在中断里调了一个格式化打印,或者取了个互斥量。本节把军规背后的原理讲透:中断为什么必须短、延迟由哪几段构成、优先级体系怎么配才不埋雷。

延迟由四段构成

从「引脚电平变化」到「你的第一行服务程序代码」,中间隔着四段。第一段是全局关中断时间:系统里所有执行过关中断指令的代码(临界区)都会推迟这段等待,全系统最长的一次关中断决定了延迟的下界——这也是 4.1 节强调关中断必须以微秒级为限的原因。第二段是中断控制器仲裁:多个请求同时到达时按优先级裁决,典型几个周期。第三段是硬件入栈与向量跳转:Cortex-M 自动压栈八个寄存器并跳到向量表项,约十二个周期。第四段才是你写的服务程序的前奏。四段里前三段由架构与配置决定、均有界可查,第四段由你的代码决定、无界可写——军规管的正是第四段。

顺带厘清三个容易混用的名词:中断延迟指事件到服务程序第一行;中断响应时间指到完成关键动作;中断恢复指返回被打断现场。实时需求写在延迟或响应上,测量与优化都按此对号入座。

两套优先级,别混为一谈

中断优先级与任务优先级是两个独立体系,配置错误是新手事故的头号来源。任务优先级由内核管理,服务于调度器;中断优先级由中断控制器管理,服务于硬件仲裁,数量级上是「中断快于一切任务」。两条铁律:

第一,不要在中断里调用任务世界的阻塞函数。服务程序运行在中断上下文,没有任务身份,阻塞等待会让整个系统停摆。需要通知任务时,只调用带 FromISR 后缀的内核函数——它们不阻塞、只记录、出口统一切换。

第二,会调用内核 FromISR 的中断,优先级必须落在内核允许的区间。以 Cortex-M 的 FreeRTOS 为例,配置项划出一条线:优先级数值低于这条线的中断(抢占能力更强)被禁止调用任何内核函数,只允许纯硬件操作。把高速率中断配到线以上又调了 FromISR,是「偶发死机」的著名来源——它违反的是内核对临界区保护的假设。数值方向也容易配反:很多芯片上数值越小优先级越高,与任务优先级的习惯正好相反。

嵌套与屏蔽:让重要的先跑

中断嵌套指高优先级中断打断低优先级服务程序。它是保证「最急的事件最先处理」的机制,但也让最坏情况分析变复杂:一个服务的执行时间要叠加在所有低优先级服务的延迟上。配置原则是把「必须微秒级响应」的事件放最高档、且这一档不做任何内核调用;次急的下调;把所有需要内核服务的归入统一区间。这样延迟分析变成简单的分段加法。

屏蔽(暂时关掉某些中断)用于保护极短临界区,注意两点:屏蔽要精准——只屏蔽与自己竞争的那几路,而不是一关全关;屏蔽时段要计入该中断的延迟预算,写进设计文档。下面是测「全系统最长关中断时间」的思路,它是延迟分析的第一输入:

/* 用一路空闲定时器捕获关中断窗口:进入 ISR 时读时间戳 */ void Probe_IRQHandler(void) /* 用一路真实中断做探针 */ { uint32_t lat = Timer_CounterRead() - s_edgeStamp; if (lat > s_maxLatency) { s_maxLatency = lat; /* 记录最大值,周期上报 */ } Timer_ClearFlag(); }

图:中断嵌套下的时间线与各段延迟

图:中断嵌套下的时间线与各段延迟

服务程序纪律清单

允许做的事,清单很短:读写外设数据寄存器或 FIFO、往队列或静态池里搬数据、调用 FromISR 函数通知任务、清中断标志。禁止的事按危害排序:任何阻塞调用(包括看似无害的内存分配与互斥量请求)、浮点运算(部分内核要求额外声明)、超长的循环处理(超过几十微秒就该挪去任务)、调用不可重入的库函数(格式化、字符串转换常见中招)。审查一段服务程序时按这张清单过一遍,越界项一目了然。

把处理逻辑挪到任务侧不是损失而是收益:任务里可以用完整的语言设施、可以阻塞等待资源、崩溃也有任务级的隔离与恢复手段。中断留给它唯一的职责——把事件尽快变成任务世界的一个通知

本节要点回顾

  • 延迟四段中前三段有界可查,全局最长关中断时间是第一输入;
  • 两套优先级独立运作,混淆它们是中断配置事故的头号来源;
  • FromISR 函数与内核允许的优先级区间,两条配置铁律缺一不可;
  • 嵌套让最坏延迟变为分段加法,最高档中断应保持纯硬件路径;
  • 服务程序只做取数与通知,处理逻辑全部下沉到任务侧;
  • 禁止清单的头三项:阻塞调用、超长循环、不可重入库函数。

常见问题

问:怎么给中断分档? 从后果倒推:先找系统中响应最快的硬需求(它的时限决定最高档),再按延迟四段加法逐档下排,最后把需要内核服务的中断统一放进允许区间。分档表写进设计文档,配一路探针实测校验。

问:中断里读 FIFO 读到多满才罢手? 以「清空到下一次中断前不会溢出」为限,多了就是把任务的工作搬到中断。经验是把「搬运」与「判断」分开:中断只搬,判断在任务——判断逻辑一变,中断代码零改动。

问:向量表重定位是什么坑? 代码搬进 RAM 或挂上引导加载器后,向量表位置变了,忘了重定位就是「中断全灭」或「跳进野地址」。移植后用一路测试中断验证向量,这是 8.2 节第二步的固定判据。

问:中断嵌套深度有上限吗? 硬件支持任意深度,工程上建议限制在两层以内:每深一层,最坏延迟的加法就多一项,可分析性随之衰减。需要更深嵌套的需求,通常是优先级分档没做对的症状。

问:下半部机制在 RTOS 里对应什么? 任务世界的延迟处理就是把工作交给任务——队列加专职任务就是 RTOS 的下半部。机制名字不同,思路与通用系统一致:上半部短到极致,其余全部延后。

从案例看延迟预算的编制

把本节的方法合成一张中断预算表的样子:某项目有三路中断——急停引线(硬时限两百微秒,最高档,纯硬件路径)、编码器捕获(十微秒级服务,次高档)、串口接收(需要内核投递,允许区间内)。编制校验时发现:急停档下面若再放任何一路服务时长无界的中断,加法就会越过两百微秒——于是把原本挤在最高档的一路日志中断下调两档,加法立即闭合。这张表的编制过程没有一行代码,但它避免了未来某天「急停偶尔慢一拍」的定性返工。预算的意义正在于此:冲突在设计阶段现形,而不是在客户现场。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U