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的消息队列中发送消息。当执行处理事件后,处理事件首先从消息队列中取出消息,然后对消息进行处理,消息执行完毕后将该消息从消息队列中删除。

  • 相关阅读:
    Hello World!
    Nginx加权轮询算法
    git常用命令
    linux命令
    sql 表值函数与标量值函数
    数据查询和操纵时连接的打开状态
    插入一条和上一条数据关联的数据
    C# 输出24小时格式时间
    c#中用sql存储过程
    AndroidManifest.xml文件解析
  • 原文地址:https://www.cnblogs.com/farbeyond/p/5204603.html
Copyright © 2011-2022 走看看