两个任务之间的同步

发布于 2024-08-01 14:13:08 字数 349 浏览 9 评论 0原文

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

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

This is actually a design question for firmware in an embedded system
I have two ISRs (of same priority) executed independently .
These ISRs are triggered when the h/w generates data.
I want a mechanism which must be put in place to synchronise between task1 and task2. task 2 must know about the certain values calculated in task1 which must then be taken into account while computing certain values in task2.
I don't have OS primitives to use ie the system does not have any Operating system.
Task1 is executed within the context of ISR1 and task2 within the context of ISR2.
The processor which we use is a STMicroelectronics 32 controller

EDIT: additional info
The processor is connected to certain IPs which trigger interrupts when they are ready with data. These IPs act as type of Accumulators on input streaming frame data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

青巷忧颜 2024-08-08 14:13:08

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

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

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

volatile uint8 state = INIT;

例如

I'm coming at this from a standpoint of no OS, no real framework other than C and registers and such. The way I would do it is to have a state variable for each ISR that can be seen by the other ISR. Then when you enter ISR1 you just check the state of ISR2's task and operate as necessary. Then ISR2 gets called and checks its own state and ISR1's state and operates as it sees fit. I'd use an ENUM in a header file to enumerate the states for clarity (INIT, WAITONDATA, etc) and then a switch in the ISR to handle each state. Then you just have to make sure that the data that needs to be shared can be seen by both ISRs and you're set.

Of course as others have said you don't want to do many calculations in ISRs. You just want to set a flag that an event has occurred or some data is present and then handle the data in the main loop. Then your system isn't blocked for too long handling an ISR. You do this in the same way - a state variable that can be seen in the main loop (or at least the function you call to do the data manipulation FROM the main loop) and the ISR. Change its state in the ISR and check that state in the main loop function. Do chores as needed.

Don't forget - if you are updating variables in ISRs they must be defined as volatile!

volatile uint8 state = INIT;

for example.

岁月静好 2024-08-08 14:13:08
  • 读取或写入共享值之前禁用中断读取或写入
  • 共享值之后重新启用中断
  • Disable interrupts before reading or writing to the shared values
  • Re-enable interrupts after reading or writing to the shared values
月下凄凉 2024-08-08 14:13:08

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

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

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

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

I wouldn't do heavy processing in interrupt context, just read the data and set a flag.

The flags can then be checked in a simple scheduler in the main loop to execute the tasks as necessary. So tasks can't interrupt each other and can't see inconsistent output of the other task.

A task could also set such a flag to activate another task. E.g. Task1 could activate Task2 because Task2 needs values from Task1.

For the data that is read in the ISR you need some buffer. Depending on the timing of your incoming data that might be a ring buffer or a double buffer.

秋日私语 2024-08-08 14:13:08

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

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

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

异步(无锁)通信队列 http://www.freeimagehosting。网/uploads/477741db06.gif
通过使用线程安全的 FIFO 队列,可以将 ISR 与任务解耦。 可以在 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)) { invoke task 1 }

现在讨论任务 1 和任务 2 的同步。
如果任务 1 只是生成数据,则可以使用相同的机制:如果有一个队列 (fifo),任务 1 可以在其中写入数据,而任务 2 可以读取该数据,则任务是解耦的。 同样,这可以在您的调度程序中进行检查(if (TRUE==fifo_filled(task1_to_2_queue() ) { invoke task2) }

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

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

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

I'll try to give you an answer based on the limited information, assuming:

  • a simple, homebrew scheduler is used to invoke task1 and task2 based on simple criterea
  • task1 and task2 run to completion (i.e. do not preempt each other)
  • data is a byte-based stream (a slightly different implementation is required if you need packets)

What I usually try when designing embedded systems is to minimize use of semaphores, and model lock-free data flows. I'll illustrate this below:

asynchronous (lock free) communication queues http://www.freeimagehosting.net/uploads/477741db06.gif
The ISRs can be decoupled from tasks by use of a thread-safe FIFO queue. An example can be found in http://msmvps.com/blogs/vandooren/archive/2007/01/05/creating-a-thread-safe-producer-consumer-queue-in-c-without-using-locks.aspx

Such an implementation (without locks) has no OS dependencies and should be trivially easy to support. This also gives a clear producer-consumer design which is free of deadlocks.

Assuming that the tasks are triggered by a home-brewn scheduler, you can elect to check for events (non-empty fifo) there.
i.e. if (TRUE == fifo_filled(my_read_queue)) { invoke task 1 }

Now on the synchronisation of task1 and task2.
If task1 is just producing data, the same mechanism may be used: if you have a queue (fifo) in which task1 can write data, which can be read by task 2 then the tasks are decoupled. Again, this can be checked in your scheduler (if (TRUE==fifo_filled(task1_to_2_queue() ) { invoke task2) }

If you need more (i.e. if tasks do not run to completion, but are preempted) you'll need some mechanism to synchronize. Options include:
- use a (free) operating system or simple scheduler anyway
- brew your own (my experience: should only be done if it is as simple as a for loop and a few if statements)
- use a simple scheduler (much more lightweigth than a full RTOS)
- refactor code to integrate task1 and task2 in one. In that case, you'll effectively make the scheduler part of your application code.

Note: the example function I've used in this explanation (fifo_filled() ) is not part of the example code. It should return true if (read != write)
Also, the example code uses global variables read and write; you may either google up a function capable of handling multiple queues, or move read/write and buffer variables to structure, and refer to the structure in the parameters.

An alternative way is to create a criticial section by means of interrupt disable. However, I try to limit this as it often creates a strong system coupling, and code becomes harder to re-use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文