【问题标题】:Event handling in embedded code嵌入式代码中的事件处理
【发布时间】:2008-10-22 05:22:16
【问题描述】:

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

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

请提出您的建议或建议。

提前致谢。


(编辑 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 ?? ) 

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

【问题讨论】:

    标签: embedded event-handling


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案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);
      }
      

      【讨论】:

      • handleEvents 它是处理系统所有事件的函数吗?如果是这种情况,我认为它不是很有用,因为您复制了可以在 ISR 中完成的工作,在我看来如果我们正在使用操作系统 ISR 应该设置事件,并且任务总是准备好处理新的即将到来的事件,不需要监听事件并分配任务来完成工作的任务。
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        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 中进行冗长或容易出错的活动。

        希望对您有所帮助!

        【讨论】:

        • 一种很好的模式,但有一点需要注意:如果您从中断和主程序循环访问相同的字段,则应将它们声明为 volatile。
        • 好点,谢谢!但要小心:“volatile”并不意味着“atomic”,我们可能需要它们两个,具体取决于程序的细节。如果一个例程可以在另一个例程访问它时修改它,则该变量应该由互斥锁保护。否则它们会互相争斗,并可能产生难以调试的微妙竞争条件。将变量声明为 volatile 告诉编译器不要假设如果当前线程不修改它,它将保持不变。所以如果你总是需要最新的值,你就会让它变得易变,就像你在循环中轮询它并等待它改变一样。
        【解决方案5】:

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

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

        【讨论】:

        • 请删除此内容并将说明放入问题本身(您可以为此目的编辑您的问题)。
        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 2012-11-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-17
            • 2011-12-25
            • 1970-01-01
            • 2017-04-02
            • 2013-08-08
            相关资源
            最近更新 更多