根据 CPU 架构(中断嵌套和优先级、软件中断支持等),有 100 种不同的方法可以给这只猫换皮,但让我们采用一种相对简单易懂且不受竞争条件和资源共享的直接方法抢占式内核的危害。
(免责声明:我的首选通常是抢占式实时内核,其中许多可以在资源极其受限的系统中运行……SecurityMatt 的建议很好,但如果您不习惯实现自己的抢占式内核/任务切换器,尤其是一个处理异步(中断触发)抢占,你可以很快地绕在轴上。所以我在下面提出的不如基于抢占的内核响应迅速,但它更简单且通常足够)。
创建 3 个事件/工作队列:
- Q1 是最低优先级,处理缓慢的后台 SD 卡写入
- Q2 持有处理传入 UART 数据包的请求
- Q3(最高优先级)保存 UART RX FIFO 读取请求。
我将 UART RX FIFO 读取和读取数据包的处理分开,以便始终在数据包处理之前为 FIFO 读取提供服务;也许你想让它们在一起,你的选择。
为此,您将大型(约 100 毫秒)SD 卡写入过程分解为一组较小的、离散的、运行到完成的步骤。
例如,要写入 5 个块,每个 20 毫秒,您写入第一个块,然后将“写入下一个块”排入队列到 Q1。您在每个步骤结束时返回调度程序并按优先级顺序扫描队列,从 Q3 开始。如果 Q2 和 Q3 为空,则将下一个事件从 Q1 中拉出(“写入下一个块”),然后再运行该命令 20 毫秒,然后再返回并再次扫描队列。如果 20 毫秒的响应不够快,您可以将每个 20 毫秒的块写入分解为一组更细粒度的步骤,不断将下一个工作步骤发布到 Q1。
现在对于传入的 UART 的东西;在 UART RX ISR 中,您只需在 Q3 中将“读取 UART FIFO”命令排入队列,然后从中断返回到被中断的 20 毫秒“写入块”步骤。一旦 CPU 完成写入,它就会返回并按优先级顺序扫描队列(如果块写入在中断时刚刚开始,最坏情况下的响应将是 20 毫秒)。队列扫描器(调度器)将看到 Q3 现在有工作要做,它会在返回并再次扫描之前运行该命令。
在最坏的情况下,系统中的响应能力将取决于系统中最长的运行到完成步骤,而不管优先级如何。通过在小的、离散的、运行到完成的步骤中进行工作,您可以保持系统的响应速度非常快。
请注意,我必须在这里泛泛而谈。也许您想读取 ISR 中的 UART RX FIFO,将数据放入缓冲区,并且只延迟数据包处理,而不是实际读取 FIFO(那么您只有 2 个队列)。你必须自己解决这个问题。但我希望这种方法是有意义的。
这种具有优先队列的事件驱动方法正是Quantum 平台 (QP) 事件驱动框架使用的方法。QP 实际上支持底层的非抢占式(协作式)调度程序,例如这里描述的,或者在每个事件排队时运行调度程序的抢占式调度程序(类似于 SecurityMatt 建议的方法)。您可以在 QP 网站上查看 QP 协作调度程序的代码/实现。