查看: 5368|回复: 2

SimpleApp绑定回调的分析

[复制链接]
大愚 发表于 2012-10-1 22:01:05 | 显示全部楼层 |阅读模式
本帖最后由 大愚 于 2012-10-1 22:03 编辑

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 );  //设备启动成功
            HalLedSet(HAL_LED_3, HAL_LED_MODE_ON);
          }
          else  if (pMsg->status == DEV_HOLD ||
                  pMsg->status == DEV_INIT)
          {
            SAPI_StartConfirm( ZB_INIT );  //设备启动不成功
          }
          break;


void SAPI_StartConfirm( uint8 status )
{
//…省略代码
  {
#if ( SAPI_CB_FUNC )
    zb_StartConfirm( status );
#endif
  }
}

void zb_StartConfirm( uint8 status )
{
  if ( status == ZB_SUCCESS )//成功,
  {
    myAppState = APP_START;
    osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, myBindRetryDelay );//查找采聚节点
  }
  else
  {
    // Try joining again later with a delay 设备启动不成功,则延时一段时间重启设备
    osal_start_timerEx( sapi_TaskID, MY_START_EVT, myStartRetryDelay );
  }
}

接下来我们来看一下系统是如何处理MY_FIND_COLLECTOR_EVT事件的。
注意在有如下宏定义
#define MY_START_EVT                0x0001
#define MY_REPORT_TEMP_EVT          0x0002
#define MY_REPORT_BATT_EVT          0x0004
#define MY_FIND_COLLECTOR_EVT       0x0008
#define ZB_USER_EVENTS                0x00FF

在应用层的事件处理函数SAPI_ProcessEvent里有
if ( events & ( ZB_USER_EVENTS ) )
  {
#if ( SAPI_CB_FUNC )
    zb_HandleOsalEvent( events );
#endif
  }
由上可知MY_START_EVT、MY_REPORT_TEMP_EVT、MY_REPORT_BATT_EVT、MY_FIND_COLLECTOR_EVT这四个事件与ZB_USER_EVENTS进行“按位与”操作时,都不为0,故这四个事件都属于ZB_USER_EVENTS事件,会进入zb_HandleOsalEvent( events )函数进行处理。
void zb_HandleOsalEvent( uint16 event )
{
//省略代码

  if ( event & MY_FIND_COLLECTOR_EVT )
  {
    // Find and bind to a collector device
    zb_BindDevice( TRUE, SENSOR_REPORT_CMD_ID, (uint8 *)NULL );
  }
}

void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination )
{
  zAddrType_t destination;
  uint8 ret = ZB_ALREADY_IN_PROGRESS;

  if ( create )//TRUE 建立绑定
  {
    if (sapi_bindInProgress == 0xffff)//sapi_init函数里有:sapi_bindInProgress = 0xffff
    {
      if ( pDestination )//知道目的地址
      {
        destination.addrMode = Addr64Bit;
        osal_cpyExtAddr( destination.addr.extAddr, pDestination );
        ret = APSME_BindRequest( sapi_epDesc.endPoint, commandId,
                                            &destination, sapi_epDesc.endPoint );
              //因为知道目的地址,故直接建立绑定表
        if ( ret == ZSuccess )
        {
          // Find nwk addr查找网络地址
          ZDP_NwkAddrReq(pDestination, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );
          osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );//更新网络地址,这一步我没有继续分析,有兴趣的哥们可以自己研究。
        }
      }
      else//不知道目的节点地址
      {
        ret = ZB_INVALID_PARAMETER;
        destination.addrMode = Addr16Bit;
        destination.addr.shortAddr = NWK_BROADCAST_SHORTADDR;   // 0xFFFF
        if ( ZDO_AnyClusterMatches( 1, &commandId, sapi_epDesc.simpleDesc->AppNumOutClusters,                                      sapi_epDesc.simpleDesc->pAppOutClusterList ) )
          //如果要匹配的簇在输出簇列表里
        {
          // Try to match with a device in the allow bind mode
          ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR,
              sapi_epDesc.simpleDesc->AppProfId, 1, &commandId, 0, (cId_t *)NULL, 0 );
        }
        else if ( ZDO_AnyClusterMatches( 1, &commandId, sapi_epDesc.simpleDesc->AppNumInClusters,
                                                sapi_epDesc.simpleDesc->pAppInClusterList ) ) //如果要匹配的簇在输入簇列表里
        {
          ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR,
              sapi_epDesc.simpleDesc->AppProfId, 0, (cId_t *)NULL, 1, &commandId, 0 );
        }

        if ( ret == ZB_SUCCESS )
        {
//ret指信息是否从传感节点发送出去,不管采集节点是否收到本消息,只要信息发送成功了,则ret == ZB_SUCCESS,只有信息没有发送出去时,才不为ZB_SUCCESS。如果信息发送出去了,则设备绑定延时。
//省略代码
        osal_start_timerEx(sapi_TaskID, ZB_BIND_TIMER, zgApsDefaultMaxBindingTime);
          sapi_bindInProgress = commandId;// sapi_bindInProgress为oxffff时,本端点才能向其它端点发起绑定,现在设置为commandId,则表明本端点暂时不能再向其它端点发起绑定了。在后面的分析中可知,当收到匹配描述符响应信息,绑定成功后,sapi_bindInProgress的值又恢复为0xffff,那时本端点又可以向其它端点发起绑定请求了。这里也表明一个源端点是可以和多个目标端点绑定的。
          return;   // dont send cback event 不需要发送回调事件,本句非常重要,因为它拦//截了下面的这一句SAPI_SendCback( SAPICB_BIND_CNF, ret, commandId );
        }
      }
    }

    SAPI_SendCback( SAPICB_BIND_CNF, ret, commandId ); //只有信息发送不成功时,才会调用本句
  }
  else
  {
    // Remove local bindings for the commanded 本分支删除绑定
    BindingEntry_t *pBind;

    // Loop through bindings an remove any that match the cluster
    while ( pBind = bindFind( sapi_epDesc.simpleDesc->EndPoint, commandId, 0 ) )
    {
      bindRemoveEntry(pBind);
    }
    osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );
  }
  return;
}

接下来分不同的分支来讨论
一、  传感节点绑定请求信息没有发送出去时,如上分析,会调用下面这一句:SAPI_SendCback( SAPICB_BIND_CNF, ret, commandId ); ret的值不为ZB_SUCCESS
void SAPI_SendCback( uint8 event, uint8 status, uint16 data )
{
  sapi_CbackEvent_t *pMsg;
  pMsg = (sapi_CbackEvent_t *)osal_msg_allocate( sizeof(sapi_CbackEvent_t) );
  if( pMsg )
  {
    pMsg->hdr.event = event;
    pMsg->hdr.status = status;
    pMsg->data = data;
    osal_msg_send( sapi_TaskID, (uint8 *)pMsg );
  }
}
上面函数将事件(SAPICB_BIND_CNF),状态(不为ZB_SUCCESS),数据(commandId)封装在一个消息里,然后交由应用层进行处理。接下来我们来看应用层是如何处理SAPICB_BIND_CNF这个事件的。

case SAPICB_BIND_CNF:
          SAPI_BindConfirm( ((sapi_CbackEvent_t *)pMsg)->data,
                              ((sapi_CbackEvent_t *)pMsg)->hdr.status );
          break;

void SAPI_BindConfirm( uint16 commandId, uint8 status )
{
//省略代码
  {
#if ( SAPI_CB_FUNC )
    zb_BindConfirm( commandId, status );
#endif
  }
}


void zb_BindConfirm( uint16 commandId, uint8 status )
{
  (void)commandId;
  if ( ( status == ZB_SUCCESS ) && ( myAppState == APP_START ) )
  {
    myAppState = APP_BOUND;
    //Start reporting sensor values
    myApp_StartReporting();//绑定成功,开始报告数据
  }
  else
  {
    // Continue to discover a collector //绑定不成功,继续发现采集节点
    osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, myBindRetryDelay );
  }
}
后面两个流程都得用到这个函数

二、  由上面的分析可知,如果发送绑定请求(匹配描述符请求)信息成功,则会设置绑定超时事件,找不到匹配的描述符信息时,则绑定超时,应用层进入对应的事件处理过程。
  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);
  }
跟第一个分支类似,会重新发起绑定请求。
如果绑定成功,是如何取消绑定超时事件的呢?下面会分析的。

三、  如果发送绑定请求(匹配描述符请求)信息成功,并且找到了匹配的描述符信息,则传感节点会收到匹配描述符响应事件,然后进入该事情进行处理。这一过程涉及传感节点与采集结点信息的交换,在这里为了突出响应的这一中心,省略了这一过程,如有疑问 ,请留言。
void SAPI_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )
{
  switch ( inMsg->clusterID )
  {
//省略代码

    case Match_Desc_rsp:
      {
        zAddrType_t dstAddr;
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );

        if ( sapi_bindInProgress != 0xffff )
        {
          dstAddr.addrMode = Addr16Bit;
          dstAddr.addr.shortAddr = pRsp->nwkAddr;
// Create a binding table entry 创建绑定表
          if ( APSME_BindRequest( sapi_epDesc.simpleDesc->EndPoint,
                     sapi_bindInProgress, &dstAddr, pRsp->epList[0] ) == ZSuccess )
          {
            osal_stop_timerEx(sapi_TaskID,  ZB_BIND_TIMER);
//上面句取消了绑定超时事件,也就是说只要能正确绑定就不会发生绑定超时//事件。
            osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );

// Find IEEE addr  查询目标节点的IEEE 地址,这一过程涉及传感
//节点与采集节点的信息交换,过程不再进一步分析。
            ZDP_IEEEAddrReq( pRsp->nwkAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );

//省略代码
            // Send bind confirm callback to application  进入绑定确认处理
#if ( SAPI_CB_FUNC )
            zb_BindConfirm( sapi_bindInProgress, ZB_SUCCESS );
#endif
            sapi_bindInProgress = 0xffff;              //绑定一个目标节点后,将值恢复为0xffff,表明该节点还可以向其它节点发起绑定
          }
        }
      }
      break;
  }
}
由上面代码可知在处理匹配描述符响应用事件时,做了三件事情:取消绑定超时事件,查询IEEE地址,进入绑定确认处理,这里进入绑定确认处理传送的状态为ZB_SUCCESS,表明绑定成功,接下来会进入传送数据过程了。

说明:
(1)协议栈版本号为ZStack-CC2530-2.3.1-1.4.不同的版本号,有一些细微的地方可能不一样;
(2)错误之处请指正;
(3)转载请注明出处。
tlk214 发表于 2013-8-10 15:35:05 | 显示全部楼层
这么好的绑定分析竟然没有顶!!!
Zigzagbee 发表于 2013-8-22 17:15:38 | 显示全部楼层
看了文章,获益颇多啊!
通过设置sapi_bindinprogress可以实现一个源设备绑定到多个设备,那么,可以将多个设备绑定到一个设备么?就是利用Collector来收集多个传感器节点发来的温度、电压信息。将多个传感器节点绑定到收集节点。
我目前将一个传感器节点成功绑定后,可以发送数据,但是在加入第二个传感器节点是,就无法成功绑定,必须将第一个传感器关掉,才能实现绑定。我不知道是什么问题,是不是sapi_bindinprogress参数在多对一的绑定中发生了变化。我的程序中,匹配响应如下:
case Match_Desc_rsp:
      {
        zAddrType_t dstAddr;
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );

        if ( sapi_bindInProgress != 0xffff )
        {
          // Create a binding table entry
          dstAddr.addrMode = Addr16Bit;
          dstAddr.addr.shortAddr = pRsp->nwkAddr;

          if ( APSME_BindRequest( sapi_epDesc.simpleDesc->EndPoint,
                     sapi_bindInProgress, &dstAddr, pRsp->epList[0] ) == ZSuccess )
          {
            osal_stop_timerEx(sapi_TaskID,  ZB_BIND_TIMER);
            osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );
            sapi_bindInProgress = 0xffff;

            // Find IEEE addr
            ZDP_IEEEAddrReq( pRsp->nwkAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );

            // Send bind confirm callback to application
            zb_BindConfirm( sapi_bindInProgress, ZB_SUCCESS );
          }
        }
      }
      break;

这个问题向您请教下!!!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表