CC2431 code review : CC2431 狂轰滥炸

在上一篇中的最后我们分析到CC2431 开始喊出第一声,这里我们逐步分析从第一声到后面的狂轰滥炸!

上代码

/*********************************************************************
* @fn startBlast
*
* @brief Start a sequence of blasts and calculate position.
*
* @param none
*
* @return none
*/
static void startBlast( void )
{
uint8 idx;
afAddrType_t dstAddr;
dstAddr.addrMode = afAddrBroadcast;
dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVALL;
dstAddr.endPoint = LOCATION_REFNODE_ENDPOINT; if ((ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE) == 0)
{
// Turn the receiver on while idle - temporarily.
idx = true;
ZMacSetReq( ZMacRxOnIdle, &idx );
}
SampleApp_Sleep( FALSE ); for ( idx = 0; idx < BLINDNODE_MAX_REF_NODES; idx++ )
{
refNodes[idx].addr = INVALID_NODE_ADDR;
} (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_RSSI_BLAST, 0,
NULL, &transId,
AF_SKIP_ROUTING, 1 );
rspCnt = 0;
blastCnt = BLINDNODE_BLAST_COUNT;
state = eBnBlastOut;
osal_start_timerEx( BlindNode_TaskID, BLINDNODE_BLAST_EVT, BLINDNODE_BLAST_DELAY );
}

上面函数主要内容

a.  dstAddr 这个结构体

  通过赋值,我们可以看出这个地址是一个广播地址,就是向网络中的所有节点都发送信息。还有一个需要注意的事项就是Endpoint,这个先留意是 LOCATION_REFNODE_ENDPOINT,其实这个就指明了虽然我是广播信息,但是需要REFNODE,也就是参考节点处理。(任何节点都可能收到信息,但是只有参考节点需要处理)。

b.  if ((ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE) == 0)

  这个里面调用MAC层的函数,我们暂时可以认为它没有执行。(其实也就是接收器控制)

  后面的 SampleApp_Sleep( FALSE );其实也很好理解,不要睡眠进入低功耗模式,你喊出一嗓子后,参考节点会回信息给你,如果睡眠下去怎么接收信息(虽然等到醒来可以接收,但是会导致延时,代码这里就一气呵成,等定完位再可以休眠)。

c 清理地址

  for ( idx = 0; idx < BLINDNODE_MAX_REF_NODES; idx++ )
{
refNodes[idx].addr = INVALID_NODE_ADDR;
}

因为现在还不知道喊出一嗓子后有没有节点会回应,所以把保存地址的数组先清理掉,等接收到信息后把对应的地址都在放进去。

d 喊一嗓子 函数

  (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_RSSI_BLAST, 0,
NULL, &transId,
AF_SKIP_ROUTING, 1 );

AF_DataRequest 函数就是一个无线发送函数,这个需要有两点注意的是

1. LOCATION_RSSI_BLAST --这个表明了这个是什么事,接收到这条信息的节点,也就是参考节点会根据它进行具体处理,我们后续会继续看这个问题。

2.  AF_SKIP_ROUTING -- 这个是AF_DataRequest 的一个参数,表明发出的信息不能被路由,我们知道在Zigbee 网络中信息是可以被路由到很远的地方,通过这个参数控制该信息是不能被路由的,传一步就停了。 为何有这样的考虑呢? 其实也很简单,当盲节点喊出一嗓子,旁边的节点收到信息后根据距离不同RSSI值不同。但是如果路由了,那么喊出这一嗓子的就不是盲节点了。再举个例子,军训站成一排,第一个人报数,教官通过声音就知道第一个人大概离自己有多远;第二个人听到第一个人报数后再喊一嗓子,对于教官来说无法通过第二个人的声音判断出第一个人在哪里,所以没必要通过连续报数获得第一个人的位置,所以也不需要路由。

 rspCnt = 0;
blastCnt = BLINDNODE_BLAST_COUNT;
state = eBnBlastOut;

全部变量的设定,rspCnt 是统计收到回应的个数,刚刚发出去信息当时没有收到,先清零,后面收到信息会有累加

        blastCnt 这个是表明CC2431 一次要喊多少次,刚刚才是第一嗓子,真正要喊 BLINDNODE_BLAST_COUNT, 其实这也是一个可以优化的参数,因为我们后面可以看出,其实在参考节点手打盲节点的信息后,将多次的RSSI会求均值然后发回到CC2431 中,再我们看来,次数越多,均值也就也接近真实稳定的值。

         state = eBnBlastOut; 之前我们遇到过这个state,当时是idle状态,目前状态是eBnBlastOut,也是就是喊出去了,后面还会多次修改。

 osal_start_timerEx( BlindNode_TaskID, BLINDNODE_BLAST_EVT, BLINDNODE_BLAST_DELAY );

函数的最后一句,定时执行一个事件BLINDNODE_BLAST_EVT,我们也可以猜出来,通过这个事件会进一步狂轰滥炸。

2 BLINDNODE_BLAST_EVT 狂轰滥炸

 if ( events & BLINDNODE_BLAST_EVT )
{
if ( blastCnt == 0 )
{
state = eBnBlastOff;
finishCollection();
}
else
{
afAddrType_t dstAddr;
uint8 stat, delay; dstAddr.addrMode = afAddrBroadcast;
dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVALL;
dstAddr.endPoint = LOCATION_REFNODE_ENDPOINT; if ( --blastCnt == 0 )
{
stat = AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_XY_RSSI_REQUEST, 0, NULL,
&transId, AF_SKIP_ROUTING, 1 ); state = eBnBlastIn;
delay = config.timeout;
}
else
{
stat = AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_RSSI_BLAST, 0, NULL,
&transId, AF_SKIP_ROUTING, 1 );
delay = BLINDNODE_BLAST_DELAY;
}
if ( stat != afStatus_SUCCESS )
{
blastCnt++;
}
osal_start_timerEx( BlindNode_TaskID, BLINDNODE_BLAST_EVT, delay );
} return ( events ^ BLINDNODE_BLAST_EVT );
}

 

咋一看代码还挺长的,其实我们通过代码的判断条件分析,每次执行很少的code,是的,没看错,是每次。 这个函数或者这段代码被执行了好多次。我们一点一点的看。

首先进来判断blastCnt 变量是否等于0,我们上面刚刚对这个变量赋值。

blastCnt = BLINDNODE_BLAST_COUNT;

我们进一步发现它是个宏定义

#define BLINDNODE_BLAST_COUNT       8

直接执行下面的else 部分

 else
{
afAddrType_t dstAddr;
uint8 stat, delay; dstAddr.addrMode = afAddrBroadcast;
dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVALL;
dstAddr.endPoint = LOCATION_REFNODE_ENDPOINT;

看着是否很熟悉,是的,也是定义了一个地址结构体,地址的赋值方法和上面是一样一样的,这也就是为何本节叫做“狂轰滥炸”了。

   if ( --blastCnt == 0 )
{
...
}
else
{
stat = AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_RSSI_BLAST, 0, NULL,
&transId, AF_SKIP_ROUTING, 1 );
delay = BLINDNODE_BLAST_DELAY;

上面的code 判断是否等于0,显然刚刚是8,--blasCnt 的结果是不为0 的,再一次执行了后面的AF_DataRequest发送,和上面的对比是一样的,这里就不解释了。

 if ( stat != afStatus_SUCCESS )
{
blastCnt++;
}
osal_start_timerEx( BlindNode_TaskID, BLINDNODE_BLAST_EVT, delay );

随后这个if 是判断是否发送成功了,如果失败了就把blastCnt 再给我加回来,我一定要执行够BLINDNODE_BLAST_COUNT次数,这是我的使命!

后面再次定时执行BLINDNODE_BLAST_EVT,和之前代码一样一样的,结果自然就明白了,然后在重新执行刚刚执行过的代码。

但是有区别,因为有很多if 判断,我们刚刚执行过一次blastCnt为7,然后 6  5 4 3 2 1 当blastCnt等于1的时候执行的代码就会成为下面

     if ( --blastCnt == 0 )
{
stat = AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_XY_RSSI_REQUEST, 0, NULL,
&transId, AF_SKIP_ROUTING, 1 ); state = eBnBlastIn;
delay = config.timeout;
}

咋一看也不就是一个AF_DataRequest 函数发送数据,而且地址还和上面的一样,非也非也! 这里有个重要的参数已经变了 LOCATION_XY_RSSI_REQUEST,之前我们上面的code分析这个参数是 LOCATION_RSSI_BLAST,这参数在参考节点的应用层是可以看到的,应用层会根据这个参数进行不同的处理,我们下一节会分析CC2430 参考节点。

可以这样理解这两个不同,LOCATION_RSSI_BLAST, 当CC2431 发送数据带这个参数的时候,也就是告诉参考节点,我现在在发送数据,你收到我这个数据包,把RSSI给我记住了!

而 LOCATION_XY_RSSI_REQUEST的意思是各位哥们把刚才我发数据信号强度RSSI都给我发回来吧。参考节点就这样一直听命于盲节点。

好了分析到这里这个部分马上就要完事了。 我们可以看到执行到这里后面还有定时执行BLINDNODE_BLAST_EVT,是的,还有最后一次,当再一次进入这个事件的处理函数的时候,执行的是最上面的部分,因为这是blastCnt 等于0

  if ( blastCnt == 0 )
{
state = eBnBlastOff;
finishCollection();
}
else

code中,我们再次遇到了state这个全局变量,此时被赋值eBnBlastOff表明此时狂轰滥炸已经结束了,一切基本over 了。

后面的函数   finishCollection(); 从字面上理解“完成收集",也就是CC2430 收集各个参考节点发送来的数据了。

/*********************************************************************
* @fn finishCollection
*
* @brief Sends the next Bind Node Response message
*
* @param none
*
* @return none
*/
static void finishCollection( void )
{
if ((ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE) == 0)
{
uint8 x; // Turn the receiver back off while idle
x = false;
ZMacSetReq( ZMacRxOnIdle, &x );
} // Send the Blind node response
sendRsp(); if ( config.mode == NODE_MODE_AUTO )
{
// set up next auto response
osal_start_timerEx( BlindNode_TaskID, BLINDNODE_FIND_EVT, config.cycle );
defAddr.addrMode = afAddr16Bit;
defAddr.addr.shortAddr = config.dstAddr;
defAddr.endPoint = config.dstEp;
} state = eBnIdle;
}

依旧,我们认为 if ((ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE) == 0) 没有执行。

从上面的代码可以看出这个函数主要内容是 sendRsp(),执行完这个函数if 判断后就把state 设置为idle了,我们直观上认为sendRsp() 完成了RSSI收集以及距离计算定位等,但字面理解是"发送应答",貌似有点出入。 是的,如果我们进一步追这个函数,会发现真的没有计算定位这个部分,而是直接发送数据给协调器了。

可能这时会郁闷,我们是一点一点追下来的,怎么刚刚还准备收集RSSI,这个时候调用函数准备发送坐标给协调器了。 其实这就是OS 与 非 OS的区别, 我们再想想,当我们blast 最后一个时

if ( --blastCnt == 0 )
{
stat = AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc,
LOCATION_XY_RSSI_REQUEST, 0, NULL,
&transId, AF_SKIP_ROUTING, 1 ); state = eBnBlastIn;
delay = config.timeout;
}

全部变量state 设置为eBnBlastIn,也就是等待接收数据。

还有这里delay很特别,这个时间是比较长的(具体可以配置,我们后期说明),在下次执行 BLINDNODE_BLAST_EVT 的时候,也就是

  if ( events & BLINDNODE_BLAST_EVT )
{
if ( blastCnt == 0 )
{
state = eBnBlastOff;
finishCollection();
}

在这个间隔,CC2431 其实完成了RSSI的收集以及坐标的计算工作!在上面的分析中我们一直在给CC2430 参考节点发送数据,忽略了它的动作,CC2431 促使 参考节点做一些事情,发过来参考节点也会促使CC2431盲节点做一些事情。

CC2431 的代码第一部分先分析到这里,要记住,我们分析到了sendRsp()这个函数。

从下一节我们将分析可怜的CC2430 参考节点的内容。

CC2431定位套餐推荐:https://item.taobao.com/item.htm?id=527836022363

CC2431 代码分析②-CC2431狂轰滥炸-LMLPHP

05-04 03:44