1

我想知道嵌入式系统代码中如何使用事件。

主要目的是了解事件标志在代码中的设置/重置方式。以及如何识别哪个任务正在使用哪个事件标志以及标志的哪些位被每个任务设置/重置。

请提出您的建议或意见。

提前致谢。


(编辑1:从下面的答案中的澄清复制)

很抱歉没有指定所需的详细信息。实际上,我对使用 vxworks/Itron/OSEK OS 分析任何用 C 语言编写的应用程序很感兴趣。例如 vxworks 中有 eventLib 库来支持事件处理。我想知道如何利用这样的系统例程来处理任务中的事件。什么是事件标志(是全局/本地......还是什么?),如何设置任何事件标志的位以及任务和事件标志之间可能的关系?

任务如何在 AND 和 OR 模式下等待多个事件?我遇到了一个示例,其中下面给出的场景看起来很危险,但是为什么呢?

            Scenarios is ==> *[Task1 : Set(e1), Task2 : Wait(e1) and Set(e2), Task3 : Wait(e2) ]*                                             

我知道一个任务等待的多个事件标志或多个任务之间的循环依赖(死锁)是任务事件关系中的危险情况,但上述情况有多危险,我不明白......请解释一下。

                (Are there any more such scenarios possible in task-event handling which should be reviewed in code ?? ) 

我希望以上信息足够......

4

7 回答 7

2

VxWorks 中的 eventLib 类似于 unix 中的 signal() —— 它可以向不同的线程指示发生了什么事。如果您需要通过事件传递数据,您可能需要改用消息队列。

这些事件在发送者和接收者之间是“全局的”。由于每个发送者都指明了事件的目的是什么,因此系统中可以有多个事件掩码,每个发送者/接收者对都有自己的解释。

一个基本的例子:

 #define EVENT1       0x00000001
 #define EVENT2       0x00000002
 #define EVENT3       0x00000004
 ...
 #define EVENT_EXIT   0x80000000

 /* Spawn the event handler task (event receiver) */
 rcvTaskId = taskSpawn("tRcv",priority,0,stackSize,handleEvents,0,0,0,0,0,0,0,0,0,0);
 ...

 /* Receive thread: Loop to receive events */
 STATUS handleEvents(void)
 {
     UINT32 rcvEventMask = 0xFFFFFFFF;

     while(1)
     {
         UINT32 events = 0;

         if (eventReceive(rcvEventMask. EVENTS_WAIT_ANY, WAIT_FOREVER, &events) == OK)
         {
             /* Process events */
             if (events & EVENT1)
                 handleEvent1();
             if (events & EVENT2)
                 handleEvent2();
             ...
             if (events & EVENT_EXIT)
                 break;
         }
     }

     return OK;
 }

事件发送者通常是硬件驱动程序 (BSP) 或另一个线程。当期望的动作发生时,驱动程序构建所有相关事件的掩码并将它们发送到接收器任务。

发送方需要获取接收方的taskID。taskID 可以是全局的,

int RcvTaskID = ERROR;
...
eventSend(RcvTaskID, eventMask);

它可以由接收者向驱动程序/发送者任务注册,

static int RcvTaskID = ERROR;

void DRIVER_setRcvTaskID(int rcvTaskID)
{
    RcvTaskID = rcvTaskID;
}
...
eventSend(RcvTaskID, eventMask);

或者驱动程序/发送者任务可以调用接收者 API 方法来发送事件(包装器)。

static int RcvTaskID;
void RECV_sendEvents(UINT32 eventMask)
{
    eventSend(RcvTaskID, eventMask);
}
于 2008-10-22T17:01:38.867 回答
2

许多嵌入式系统使用中断服务程序 (ISR) 来处理事件。您将为给定的“标志”定义 ISR,并在处理事件后重置该标志。

例如,假设您有一个执行模数转换 (ADC) 的设备。在设备上,您可以有一个 ISR,每次 ADC 完成转换时触发,然后在 ISR 中处理它或通知其他任务数据可用(如果您想通过某些通信协议发送它)。完成后,您将重置 ADC 标志,以便它可以在下一次转换时再次触发。

通常在设备手册中定义了一组 ISR。有时它们会提供通用标志,您也可以根据需要处理这些标志。每次重置导致例程触发的标志。

于 2008-10-22T05:32:09.883 回答
1

如果您询问如何设置、清除和检查表示事件的各种位,此示例可能会有所帮助。基本策略是声明一个(通常是全局的)变量并使用一位来表示每个条件。

unsigned char bit_flags = 0;

现在我们可以将事件分配给位:

#define TIMER_EXPIRED   0x01   // 0000 0001
#define DATA_READY      0x02   // 0000 0010
#define BUFFER_OVERFLOW 0x04   // 0000 0100

我们可以使用位运算符设置、清除和检查位:

// Bitwise OR: bit_flags | 00000001 sets the first bit.
bit_flags |=  TIMER_EXPIRED;  // Set TIMER_EXPIRED bit.

// Bitwise AND w/complement clears bits: flags & 11111101 clears the 2nd bit.
bit_flags &= ~DATA_READY;     // Clear DATA_READY bit.

// Bitwise AND tests a bit.  The result is BUFFER_OVERFLOW
// if the bit is set, 0 if the bit is clear.
had_ovflow = bit_flags & BUFFER_OVERFLOW;

我们还可以设置或清除位组合:

// Set DATA_READY and BUFFER_OVERFLOW bits.
bit_flags |=  (DATA_READY | BUFFER_OVERFLOW);

您经常会看到这些操作以宏的形式实现:

#define SET_BITS(bits, data)    data |=  (bits)
#define CLEAR_BITS(bits, data)  data &= ~(bits)
#define CHECK_BITS(bits, data)  (data & (bits))

另外,关于中断和中断服务例程的说明:它们需要快速运行,因此典型的 ISR 将简单地设置一个标志、增加一个计数器或复制一些数据并立即退出。然后,您可以在闲暇时查看旗帜并参加活动。您可能不想ISR 中进行冗长或容易出错的活动。

希望这会有所帮助!

于 2008-11-06T05:21:17.483 回答
1

这个问题需要提供更多的上下文。嵌入式系统可以使用广泛的语言、操作系统(包括无操作系统)、框架等来创建。在嵌入式系统中如何创建和处理事件并没有什么普遍性,就像事件的方式没有什么普遍性一样一般在计算中创建和处理。

于 2008-10-22T05:31:17.543 回答
0

在我设计的一个嵌入式系统系列中(对于具有约 128KB 闪存和 3.5KB RAM 的 PIC18Fxx 微控制器),我编写了一个库来处理多达 16 个分辨率为 1/16 秒的定时器(通过输入到 CPU 的 16Hz 脉冲测量)。代码设置为确定是否有任何定时器处于过期状态或任何专用唤醒引脚正在发出信号,如果不是,则休眠直到下一个定时器到期或唤醒输入改变状态。相当方便的代码,尽管我回想起来可能已经将它设计为与多组 8 个计时器而不是一组 16 个计时器一起工作。

我发现定时例程的一个关键方面是有用的,它们大多不是由中断驱动的。相反,我有一个“方便时轮询”例程,它可以更新 16Hz 计数器的计时器。虽然有时让定时器不通过中断运行感觉很奇怪,但这样做可以避免担心在奇怪的时间发生中断。如果定时器控制的动作不能在中断中发生(由于堆栈嵌套和其他限制),则无需担心中断中的定时器——只需跟踪已经过去了多少时间。

于 2011-02-02T05:40:28.927 回答
0

很抱歉没有指定所需的详细信息。实际上,我对使用 vxworks/Itron/OSEK OS 分析任何用 C 语言编写的应用程序很感兴趣。例如 vxworks 中有 eventLib 库来支持事件处理。我想知道如何利用这样的系统例程来处理任务中的事件。什么是事件标志(是全局/本地......还是什么?),如何设置任何事件标志的位以及任务和事件标志之间可能的关系?

我希望以上信息足够......

于 2008-10-22T05:38:50.913 回答
0

如果您对在嵌入式级别使用事件驱动编程感兴趣,那么您真的应该研究一下QP。这是一个优秀的轻量级框架,如果你得到 Miro Samek 的“C/C++ 中的实用 UML 状态图”一书,你会发现从如何在嵌入式 linux 内核(ISR 等)中处理系统事件到在构建中处理和创建它们的所有内容QP 作为您的环境。(这里是一个示例事件的链接)。

于 2011-12-08T23:26:54.090 回答