【问题标题】:synchronization between two tasks两个任务之间的同步
【发布时间】:2009-08-03 09:47:22
【问题描述】:

这实际上是嵌入式系统中固件的设计问题 我有两个独立执行的 ISR(具有相同的优先级)。 这些 ISR 在硬件生成数据时触发。 我想要一个必须在task1和task2之间同步的机制。任务 2 必须知道在任务 1 中计算的某些值,然后在计算任务 2 中的某些值时必须考虑这些值。 我没有要使用的操作系统原语,即系统没有任何操作系统。 Task1 在 ISR1 的上下文中执行,task2 在 ISR2 的上下文中执行。 我们使用的处理器是 STMicroelectronics 32 控制器

编辑:附加信息 处理器连接到某些 IP,当它们准备好数据时触发中断。这些 IP 充当输入流帧数据的累加器类型。

【问题讨论】:

  • 你在哪个平台上工作?
  • 鉴于两个ISR具有相同的优先级,它们可以互相中断吗?如果没有,您可能不需要任何额外的同步。
  • @Matthem - 通常 ISR 在进入时会禁用中断。但这将是一件好事。
  • 您是否试图确保 task1 在 task2 之前运行而不管中断的顺序如何?

标签: synchronization embedded firmware


【解决方案1】:

我不会在中断上下文中进行繁重的处理,只需读取数据并设置一个标志。

然后可以在主循环中的简单调度程序中检查标志,以根据需要执行任务。所以任务不能互相打断,也看不到其他任务的输出不一致。

一个任务也可以设置这样的标志来激活另一个任务。例如。 Task1 可以激活 Task2,因为 Task2 需要来自 Task1 的值。

对于在 ISR 中读取的数据,您需要一些缓冲区。取决于传入数据的时间,可能是环形缓冲区或双缓冲区。

【讨论】:

    【解决方案2】:
    • 读取或写入共享值之前禁用中断
    • 读取或写入共享值后重新启用中断

    【讨论】:

      【解决方案3】:

      我是从没有操作系统的角度来看的,除了 C 和寄存器等没有真正的框架。我这样做的方法是为每个 ISR 设置一个状态变量,让另一个 ISR 可以看到。然后,当您进入 ISR1 时,您只需检查 ISR2 的任务状态并根据需要进行操作。然后 ISR2 被调用并检查它自己的状态和 ISR1 的状态并按照它认为合适的方式运行。为了清楚起见,我会在头文件中使用 ENUM 来枚举状态(INIT、WAITONDATA 等),然后在 ISR 中使用一个开关来处理每个状态。然后,您只需确保两个 ISR 都可以看到需要共享的数据,并且您已做好准备。

      当然,正如其他人所说,您不想在 ISR 中进行大量计算。您只想设置一个事件已发生或存在某些数据的标志,然后在主循环中处理数据。然后您的系统不会因为处理 ISR 而被阻塞太久。您以相同的方式执行此操作 - 可以在主循环(或至少是您调用的从主循环进行数据操作的函数)和 ISR 中看到的状态变量。在 ISR 中更改其状态并在主循环函数中检查该状态。根据需要做家务。

      不要忘记 - 如果您在 ISR 中更新变量,它们必须定义为 volatile!

      易失性 uint8 状态 = INIT;

      例如。

      【讨论】:

        【解决方案4】:

        我会尝试根据有限的信息给你一个答案,假设:

        • 一个简单的自制调度程序用于根据简单的标准调用 task1 和 task2
        • task1 和 task2 运行完成(即不互相抢占)
        • 数据是基于字节的流(如果您需要数据包,则需要稍微不同的实现方式)

        在设计嵌入式系统时,我通常会尝试尽量减少信号量的使用,并为无锁数据流建模。我将在下面说明这一点:

        asynchronous (lock free) communication queues http://www.freeimagehosting.net/uploads/477741db06.gif ISR 可以通过使用线程安全的 FIFO 队列与任务分离。可以在http://msmvps.com/blogs/vandooren/archive/2007/01/05/creating-a-thread-safe-producer-consumer-queue-in-c-without-using-locks.aspx中找到一个示例

        这样的实现(没有锁)没有操作系统依赖,应该很容易支持。这也提供了一个清晰的生产者-消费者设计,没有死锁。

        假设任务由自制调度程序触发,您可以选择在那里检查事件(非空 fifo)。 即 if (TRUE == fifo_filled(my_read_queue)) { 调用任务 1 }

        现在关于task1和task2的同步。 如果 task1 只是产生数据,则可以使用相同的机制:如果您有一个队列 (fifo),task1 可以在其中写入数据,并且可以被任务 2 读取,那么这些任务就会解耦。同样,这可以在您的调度程序中检查(if (TRUE==fifo_filled(task1_to_2_queue() ) { invoke task2) }

        如果您需要更多(即如果任务没有运行完成,而是被抢占),您将需要一些同步机制。选项包括: - 无论如何都要使用(免费)操作系统或简单的调度程序 - 自己酿造(我的经验:只有当它像 for 循环和一些 if 语句一样简单时才应该这样做) - 使用简单的调度程序(比完整的 RTOS 轻得多) - 重构代码以将 task1 和 task2 集成为一体。在这种情况下,您将有效地使调度程序成为应用程序代码的一部分。

        注意:我在本说明中使用的示例函数 (fifo_filled() ) 不是示例代码的一部分。如果 (read != write) 它应该返回 true 此外,示例代码使用全局变量读写;您可以搜索一个能够处理多个队列的函数,或者将读/写和缓冲区变量移动到结构中,并在参数中引用结构。

        另一种方法是通过中断禁用来创建关键部分。但是,我尝试限制这一点,因为它通常会产生强大的系统耦合,并且代码变得更难重用。

        【讨论】:

        • 请把你的插图放大一点好吗?
        猜你喜欢
        • 2021-06-01
        • 2021-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多