两个 CPU 之间棘手的队列使用

Tricky queue use between two CPUs

悬崖笔记版本 TI F28377S有两个CPU,一个主CPU和一个副CPU(CLA,一次只能执行一个任务,任务不间断)-它们共享RAM的消息区域。当快速向队列提供大约 15 个字节(最大 32 个队列长度),CLA 将发送这些字节时,有时永远不会传输几个字节。我认为 CPU 中断存在一些问题,导致单个字节在将它们移交给缓冲区时偶尔会得到 "lost"。

完整版 (这是使用 TI F28377S,它有一个主 CPU 主频为 200 MHz,辅助独立 CLA 运行 以相同的速度,但一次只能执行一个任务。他们可以共享单向可写变量)。

我对如何完成这项涉及 CLA 和队列的更复杂任务感到有点困惑。

一些快速背景知识:我有两个主要的 CLA 任务,第一个 (Task1) 由 ADC 转换结束触发(它本身由 Timer0 在 100 kHz 触发),第二个 (Task2) 由Timer0 本身(这是经过大量实验和调整后得出的,因为每当我比 ADC 任务更频繁地执行 Task2 运行ning 时,ADC 任务将永远不会启动 - 所以我将它们设置为使用相同的间隔,只是交错)。 Task1 工作完美,将 ADC 结果存储在一个简单的环形缓冲区中,并在 Task1 完成后的 ISR 中执行简单的计算。第二种主要有效。

Task2 用于切换一些 GPIO 引脚以与外部设备通信。因为代码的总长度大约为 100 微秒,我没有延迟,而是在每个触发器上使用一个简单的 case 结构来确定它是否应该:什么都不做,打开代码引脚,打开选通引脚,关闭选通引脚,关闭代码引脚。这样,每次调用任务时,它几乎立即完成,输出代码的长度适合外部设备。该任务每次处理一个代码,一旦完成,就会尝试从队列中获取另一个代码。如果none,它只是继续通过。

现在,棘手的部分。我有两个要求:1)我可以将字节添加到队列末尾的速度比任务消耗它们的速度快(在理论上和实践中都非常容易)和 2)我可以将一个字节添加到队列的前面(不是替换当前传输的字节,只是队列的前面)。第一个能力是发送中短消息(2-20个字符)。第二种能力是发送有关任何外部中断的单个字节所必需的 - 尽可能快,甚至在传输消息的过程中也是如此。我已将其设置为任务每 500 微秒恰好发送 1 个字节(~300 "on" 和 ~200“关闭”)。这样,如果有中断消息进来,将保证收到更少发生后不到 1 毫秒。

目前的工作是:CPU 上的一个函数,它接收传入的字节(一次一个)并将它们添加到 CPU2CLA 缓冲区并递增 CPU2CLA长度计数器。每次 Task2 运行,它都会检查这个队列并从 CLAonly 缓冲区的前面抓取一个字节,增加它自己的缓冲区长度,并标记一个字节已被消耗。当 Task2 任务后 ISR 为 运行 时,它将检查一个字节是否已被消耗,并从 CPU2CLA 缓冲区中删除第一个字节。目前这个双缓冲系统没有添加到前面的标志,所以它不处理中断情况。

我之前尝试的是有一个 Task3,它使用一个字节从 CPU2CLA 和 运行 通过 Task3andWait 从 CPU 传递。虽然这种方法理论上应该同时满足这两个要求,但是大约一半的时间消息的一两个字节永远不会被传输(总是发送一个字节)。

CLA 任务永远不会被中断,但 CPU 任务可以。这就是为什么我试图让队列的所有修改只发生在 CLA 中,这样就不会出现可能中断队列修改的不确定状态。

听起来将高优先级和普通优先级项目拆分到单独的缓冲区中是一个近乎最优的解决方案。

这也将确保如果在消费任何东西之前生产了一个高优先级项目、一个普通优先级项目和另一个高优先级项目,那么高优先级项目将在普通优先级项目之前被消费.

(使用单个缓冲区,这种情况会导致普通优先级项目在第二个高优先级项目之前被消耗。我怀疑这是非常不可取的。)

如果高优先级缓冲区中有项目,则接下来将使用该项目。否则,将消耗正常优先级缓冲区中的一项。

两个缓冲区都有一个生产者和一个消费者(因此,SPSC类型),并以简单的先进先出方式处理;因此,无锁循环缓冲区实现(对于每个缓冲区)应该在这里工作得很好。

(如果两个缓冲区只有 32 个字节可用,请考虑先尝试 8:24 拆分。)