本帖最后由 大愚 于 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)转载请注明出处。 |