zoukankan      html  css  js  c++  java
  • 有关zstack的osal机制的理解

            本文就浅谈一下有关osal的事件驱动消息响应的机制。我是菜鸟,没有做过什么软件,没有搞过VC,没有学过linux,所以理解起来就有点吃力,所以现在感觉没有不懂软件的it民工真可怕。ok,切入正题。

          一、windows OS的事件驱动消息响应机制

           osal是面向多用户的操作系统,现在活着的操作系统基本上都是多用户的,所以我们现在先来了解一下windows OS的基于事件驱动消息响应的机制,先以基于windows窗口程序为例吧。

           假设现在有一个按键按下,windows OS就会首先知道有这个按键事件按下,那么这个时候windows OS就会向这个窗口发送消息,告诉这个这个窗口程序现在又按键事件发生了,并调用相应的事件处理函数来处理这个事件,并且windows OS会向这个事件发送相应的消息信息,并将这个消息放到这个窗口的消息队列中。消息信息中包含相关参数信息,比如是哪一个按键按下。在这里我们可以看到很多这里有很多的事情是windows OS来为这个窗口程序来处理的,而不是窗口程序本身,也就是说是windows OS为调用了窗口程序而不是窗口程序调用了windows OS的API函数来处理事件。

           在windows OS调用了相应事件的处理函数以后,那么就会执行这个函数,并对消息作出相应的处理。当进入到事件处理函数以后,首先会读取从消息队列中读取消息,然后作出处理,看看是鼠标按键按下了还是数字键按下等等。

           二、ZStack中消息响应的过程

           有了上面的理解基础之后,我们来看看zstack中是如何来进行事件驱动消息响应的。

           前面的一篇“ZStack任务”文章讲过如下一段话:Any OSAL Task must implement two methods:one to perform task initialization and the other to handle task events. 也就是说任何一个任务包含两个必需成分:初始化和处理。初始化主要是进行任务id,端点的配置等等。处理部分就是一个处理函数,用于处理事件发生的事情。

    void osalInitTasks( void )

    {

      uint8 taskID = 0;

      tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);

      osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

      ...

      SAPI_Init( taskID );

    }

           这是初始化函数,这里实现了两个功能,第一个是初始化任务,并且将任务加入到任务队列中。这里主要是依据后面的一个tasksArr[]数组中对应的顺序决定哪一个初始化函数对已哪一个事件处理函数。

    接着看下面的。在完成各类初始化以后,系统就调用这个osal_start_system();这就意味着进入到了操作系统的死循环中,也就是任务调度中。先看代码,亲们。

    void osal_start_system( void )

    {

    #if !defined ( ZBIT ) && !defined ( UBIT )

      for(;;)  // Forever Loop

    #endif

      {

        uint8 idx = 0;

        osalTimeUpdate();

        Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().

       

        do {

          if (tasksEvents[idx])  // Task is highest priority that is ready.

          {

            break;

          }

        } while (++idx < tasksCnt);

        if (idx < tasksCnt)

        {

          uint16 events;

          halIntState_t intState;

          HAL_ENTER_CRITICAL_SECTION(intState);

          events = tasksEvents[idx];

          tasksEvents[idx] = 0;  // Clear the Events for this task.

          HAL_EXIT_CRITICAL_SECTION(intState);

         events = (tasksArr[idx])( idx, events );

          HAL_ENTER_CRITICAL_SECTION(intState);

          tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.

          HAL_EXIT_CRITICAL_SECTION(intState);

        }

    #if defined( POWER_SAVING )

        else  // Complete pass through all task events with no activity?

        {

          osal_pwrmgr_powerconserve();  // Put the processor/system into sleep

        }

    #endif

      }

    }

    首先执行这两个,  osalTimeUpdate();   Hal_ProcessPoll();

           第一个函数是进行系统时间更新的,相当于说系统会有自己的一个个时钟频率,就是定时来更新,系统不是时刻在苏醒者,她也要休息,要睡觉,这点可能是为了节能,为了体现zigbee的优势。第二就是系统醒了之后就来查询各种资源的使用情况,例如串口,跟踪进去就看到了如下的代码:

    void Hal_ProcessPoll ()

    {

      /* Timer Poll */

    #if (defined HAL_TIMER) && (HAL_TIMER == TRUE)

      HalTimerTick();

    #endif

      /* UART Poll */

    #if (defined HAL_UART) && (HAL_UART == TRUE)

      HalUARTPoll();

    #endif 

      /* SPI Poll */

    #if (defined HAL_SPI) && (HAL_SPI == TRUE)

      HalSpiPoll();

    #endif

      /* HID poll */

    #if (defined HAL_HID) && (HAL_HID == TRUE)

      usbHidProcessEvents();

    #endif

    }

    大家看一下就清楚,具体就不纠结了。

            现在来看红色字体的部分,首先从tasksEvents[idx]这个数组中取出事件,并判断其优先级,如果这个事件已经准备好了,就跳出循环来处理。现在看蓝色部分的代码。

           events = tasksEvents[idx];

          tasksEvents[idx] = 0;  // Clear the Events for this task

             首先取出事件,并将这个时间的ID清零。现在大家注意了,最重要的东西来了。

    events = (tasksArr[idx])( idx, events );

            这个执行后,就是系统去调用了相应的事件了。大家将这点结合上面讲的windows OS的处理机制来理解不很容易了。这里就是osal调用处理事件了。上面那个语句 (tasksArr[idx])( idx, events );实际就是利用一个指向函数的指针来调用相应的事件了。来看看这个tasksArr的定义就清楚了。

    const pTaskEventHandlerFn tasksArr[] = {

      macEventLoop,

      nwk_event_loop,

      Hal_ProcessEvent,

    #if defined( MT_TASK )

      MT_ProcessEvent,

    #endif

      APS_event_loop,

      ZDApp_event_loop,

      SAPI_ProcessEvent

    };

    在这里就看到了我们初始化的函数了,当我们的任务来临了调用这个函数来执行相应的事件。ok,现在来看看事件是如何利用消息处理的事件的。

    UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )

    {

      osal_event_hdr_t *pMsg;

      afIncomingMSGPacket_t *pMSGpkt;

      afDataConfirm_t *pDataConfirm;

      if ( events & SYS_EVENT_MSG )

      {

       pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );

        while ( pMsg )

        {

          switch ( pMsg->event )

          {

            case ZDO_CB_MSG:

              SAPI_ProcessZDOMsgs( (zdoIncomingMsg_t *)pMsg );

              break;

            case AF_DATA_CONFIRM_CMD:

              // This message is received as a confirmation of a data packet sent.

              // The status is of ZStatus_t type [defined in ZComDef.h]

              // The message fields are defined in AF.h

              pDataConfirm = (afDataConfirm_t *) pMsg;

              SAPI_SendDataConfirm( pDataConfirm->transID, pDataConfirm->hdr.status );

              break;

            case AF_INCOMING_MSG_CMD:

              pMSGpkt = (afIncomingMSGPacket_t *) pMsg;

              SAPI_ReceiveDataIndication( pMSGpkt->srcAddr.addr.shortAddr, pMSGpkt->clusterId,

                                        pMSGpkt->cmd.DataLength, pMSGpkt->cmd.Data);

              break;

            case ZDO_STATE_CHANGE:

              // If the device has started up, notify the application

              if (pMsg->status == DEV_END_DEVICE ||

                  pMsg->status == DEV_ROUTER ||

                  pMsg->status == DEV_ZB_COORD )

              {

                SAPI_StartConfirm( ZB_SUCCESS );

              }

              else  if (pMsg->status == DEV_HOLD ||

                      pMsg->status == DEV_INIT)

              {

                SAPI_StartConfirm( ZB_INIT );

              }

              break;

            case ZDO_MATCH_DESC_RSP_SENT:

              SAPI_AllowBindConfirm( ((ZDO_MatchDescRspSent_t *)pMsg)->nwkAddr );

              break;

            case KEY_CHANGE:

    #if ( SAPI_CB_FUNC )

              zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );

    #endif

              break;

            case SAPICB_DATA_CNF:

              SAPI_SendDataConfirm( (uint8)((sapi_CbackEvent_t *)pMsg)->data,

                                        ((sapi_CbackEvent_t *)pMsg)->hdr.status );

              break;

            case SAPICB_BIND_CNF:

              SAPI_BindConfirm( ((sapi_CbackEvent_t *)pMsg)->data,

                                  ((sapi_CbackEvent_t *)pMsg)->hdr.status );

              break;

            case SAPICB_START_CNF:

              SAPI_StartConfirm( ((sapi_CbackEvent_t *)pMsg)->hdr.status );

              break;

            default:

              // User messages should be handled by user or passed to the application

              if ( pMsg->event >= ZB_USER_MSG )

              {

              }

              break;

          }

          // Release the memory

          osal_msg_deallocate( (uint8 *) pMsg );

          // Next

          pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );

        }

        // Return unprocessed events

        return (events ^ SYS_EVENT_MSG);

      }

      if ( events & ZB_ALLOW_BIND_TIMER )

      {

        afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE);

        return (events ^ ZB_ALLOW_BIND_TIMER);

      }

      if ( events & ZB_BIND_TIMER )

      {

        // Send bind confirm callback to application

        SAPI_BindConfirm( sapi_bindInProgress, ZB_TIMEOUT );

        sapi_bindInProgress = 0xffff;

        return (events ^ ZB_BIND_TIMER);

      }

      if ( events & ZB_ENTRY_EVENT )

      {

        uint8 startOptions;

        // Give indication to application of device startup

    #if ( SAPI_CB_FUNC )

        zb_HandleOsalEvent( ZB_ENTRY_EVENT );

    #endif

        // LED off cancels HOLD_AUTO_START blink set in the stack

        HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);

        zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );

        if ( startOptions & ZCD_STARTOPT_AUTO_START )

        {

          zb_StartRequest();

        }

        else

        {

          // blink leds and wait for external input to config and restart

          HalLedBlink(HAL_LED_2, 0, 50, 500);

        }

        return (events ^ ZB_ENTRY_EVENT );

      }

      // This must be the last event to be processed

      if ( events & ( ZB_USER_EVENTS ) )

      {

        // User events are passed to the application

    #if ( SAPI_CB_FUNC )

        zb_HandleOsalEvent( events );

    #endif

        // Do not return here, return 0 later

      }

      // Discard unknown events

      return 0;

    }

    在这里先看红色的语句,

    pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );

    这个语句就是利用任务id来从取出消息。接下来的switch语句就是处理消息了,这里有各种消息,看蓝色字体的部分。以KEY_CHANGE为例,当消息为按键改变的时候,那么就会调用相应的函数去进行处理,调用的函数就是:

    zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );

    当处理完消息之后就会向消息将该消息从消息队列中删除:

          pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );

    小结:

           事件驱动消息响应是为多任务的处理服务的。OSAL首先进行进行处理任务的初始化,并将任务添加到任务队列中。当任务发生以后,OSAL会首先知道有这个事件发生,并调用相应的处理事件,同时OSAL会向这个OSAL的消息队列中发送消息。当执行处理事件后,处理事件首先从消息队列中取出消息,然后对消息进行处理,消息执行完毕后将该消息从消息队列中删除。

  • 相关阅读:
    LeetCode 79. 单词搜索
    LeetCode 1143. 最长公共子序列
    LeetCode 55. 跳跃游戏
    LeetCode 48. 旋转图像
    LeetCode 93. 复原 IP 地址
    LeetCode 456. 132模式
    LeetCode 341. 扁平化嵌套列表迭代器
    LeetCode 73. 矩阵置零
    LeetCode 47. 全排列 II
    LeetCode 46. 全排列
  • 原文地址:https://www.cnblogs.com/farbeyond/p/5204603.html
Copyright © 2011-2022 走看看