这节主要分析CC2430的代码,是参考节点的代码,协调器代码我们放到最后分析。
代码分析的原则是事件为导向,我们从CC2431 盲节点code的分析中发现CC2431 向CC2430参考节点发送的信息包含了两种内容
LOCATION_RSSI_BLAST
LOCATION_XY_RSSI_REQUEST
其中LOCATION_RSSI_BLAST 发送了多次,而最后一次发送的是LOCATION_XY_RSSI_REQUEST,我们现在就开始CC2430 code中找相关的关键字。
PS:CC2430 参考节点code文件RefNode.c
由于CC2430 参考节点的代码实在太过于简单了,我们先把CC2430 参考节点事件Event相关的代码全部先贴上来。
/********************************************************************* * @fn RefNode_ProcessEvent * * @brief Generic Application Task event processor. * * @param task_id - The OSAL assigned task ID. * @param events - Bit map of events to process. * * @return none */ uint16 RefNode_ProcessEvent( byte task_id, uint16 events ) { if ( events & SYS_EVENT_MSG ) { afIncomingMSGPacket_t *MSGpkt; while ((MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(RefNode_TaskID))) { switch ( MSGpkt->hdr.event ) { case KEY_CHANGE: //LocationHandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; case AF_DATA_CONFIRM_CMD: break; case AF_INCOMING_MSG_CMD: processMSGCmd( MSGpkt ); break; case ZDO_STATE_CHANGE: osal_start_timerEx( RefNode_TaskID, ANNCE_EVT, ANNCE_DELAY ); break; default: break; } osal_msg_deallocate( (uint8 *)MSGpkt ); } // Return unprocessed events. return ( events ^ SYS_EVENT_MSG ); } if ( events & ANNCE_EVT ) { /* Broadcast the X,Y location for any passive listeners in order to * register this node. */ afAddrType_t dstAddr; dstAddr.addrMode = afAddrBroadcast; dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVALL; dstAddr.endPoint = LOCATION_DONGLE_ENDPOINT; (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc, LOCATION_REFNODE_CONFIG, REFNODE_CONFIG_LEN, rspMsg, &transId, 0, AF_DEFAULT_RADIUS ); return ( events ^ ANNCE_EVT ); } return 0; // Discard unknown events }
可以看到CC2430参考节点的事件太少了,我们简单说一下
KEY_CHANGE 和 AF_DATA_CONFIRM_CMD 两个是按键触发以及发送完成确认,本来 KEY_CHANGE 还有点作用,但是我们还是可以先去掉这个功能,两个就不用看了。
AF_INCOMING_MSG_CMD 当收到无线信号的时候就会触发这个,所以我们在之前分析CC2431的code时向CC2430参考节点 发送的信息都会触发到这,我们后面详细分析。
ZDO_STATE_CHANGE 这个可以认为是设备启动成功加入网络后触发,在code中定时触发了一个人任务ANNCE_EVT,我们在上面的code中也可以看到这个任务的处理方法
if ( events & ANNCE_EVT ) { /* Broadcast the X,Y location for any passive listeners in order to * register this node. */ afAddrType_t dstAddr; dstAddr.addrMode = afAddrBroadcast; dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVALL; dstAddr.endPoint = LOCATION_DONGLE_ENDPOINT; (void)AF_DataRequest( &dstAddr, (endPointDesc_t *)&epDesc, LOCATION_REFNODE_CONFIG, REFNODE_CONFIG_LEN, rspMsg, &transId, 0, AF_DEFAULT_RADIUS ); return ( events ^ ANNCE_EVT ); }
由 dstAddr.endPoint = LOCATION_DONGLE_ENDPOINT;可以看到这条信息是发送给协调器的,我们分析协调器的时候再分析这个信息的作用。
那么我们下面就主要分析AF_INCOMING_MSG_CMD 相关的内容
case AF_INCOMING_MSG_CMD: processMSGCmd( MSGpkt ); break;
可以看到当接受到无线信息的时候,就会调用 processMSGCmd( MSGpkt ) 这个函数处理,其中MSGpkt 这个是非常复杂庞大的结构体,里面包含了非常非常多的内容,有兴趣可以自己查看这个结构体的定义,后面我们看到的code它有什么我们就确信它就是有什么了。
/********************************************************************* * @fn processMSGCmd * * @brief Data message processor callback. * * @param none * * @return none */ static void processMSGCmd( afIncomingMSGPacket_t *pkt ) { switch ( pkt->clusterId ) { case LOCATION_XY_RSSI_REQUEST: rssiRsp( pkt ); break; case LOCATION_REFNODE_CONFIG: parseConfig( pkt->cmd.Data ); break; case LOCATION_REFNODE_REQUEST_CONFIG: pkt->srcAddr.addrMode = afAddr16Bit; (void)AF_DataRequest( &pkt->srcAddr, (endPointDesc_t *)&epDesc, LOCATION_REFNODE_CONFIG, REFNODE_CONFIG_LEN, rspMsg, &transId, 0, AF_DEFAULT_RADIUS ); break; case LOCATION_RSSI_BLAST: addBlast( pkt->srcAddr.addr.shortAddr, pkt->LinkQuality ); break; default: break; } }
我们可以看到这部分代码是会根据 pkt->clusterId 进行分别处理的,我们在之前分析AF_DataRequest 函数中强调的一个参数是否还记得,我们在CC2431code 分析的时候有两种信息LOCATION_RSSI_BLAST LOCATION_XY_RSSI_REQUEST,我们至于对应就可以知道,当CC2431 开始发送LOCATION_RSSI_BLAST 的时候CC2430 参考节点是调用
addBlast( pkt->srcAddr.addr.shortAddr, pkt->LinkQuality );处理的。
当发送LOCATION_XY_RSSI_REQUEST 的时候,是调用 rssiRsp( pkt )。
还有一点,我们在CC2431发送信息的时候后当时要留意一个参数叫Endpoint,当时设置LOCATION_REFNODE_ENDPOINT, 为何这里没有看到呢?
其实这个Endpoint是在初始化的时候配置的,我们这里再简单贴一点CC2430 参考节点初始化的代码
void RefNode_Init( byte task_id ) { // Register the endpoint/interface description with the AF. afRegister( (endPointDesc_t *)&epDesc ); // Register for all key events - This app will handle all key events. // RegisterForKeys( RefNode_TaskID );
可以看到有个afRegister( (endPointDesc_t *)&epDesc );其中epDesc 是一个全局变量的结构体,我们在code中找到其定义
static const endPointDesc_t epDesc = { LOCATION_REFNODE_ENDPOINT, &RefNode_TaskID, (SimpleDescriptionFormat_t *)&RefNode_SimpleDesc, noLatencyReqs };
好了,我们终于在CC2430 的参考节点的code中找到了LOCATION_REFNODE_ENDPOINT,这个问题分析到这里。
下面我们在回到 processMSGCmd( MSGpkt ),我们可以到看一种有四种处理方法,也就是一种接收到四种消息,其中两种来源于CC2431盲节点,而另外两种则是来源于CC2430协调器,协调器发送信息配置CC2430参考节点的坐标等信息是一种,另一种是读取CC2430参考节点的坐标信息。 这两种我们在分析CC2430 协调器code的时候再分析。
以事件为导向,我们先分析
case LOCATION_RSSI_BLAST: addBlast( pkt->srcAddr.addr.shortAddr, pkt->LinkQuality );
这个是在CC2431最开始发送信息是需要处理的,也就是CC2431 广播让各个CC2430 参考节点记录自己的RSSI值。我们看addBlast函数,函数名就可以看出”加blast“,就是累加RSSI,后面传输的参数是一个short地址一个信号质量。具体函数如下
/********************************************************************* * @fn addBlast * * @brief Add or initialize an RSSI blast for the given Network Address. * * @param uint16 - Network address of the Blind Node blasting. * byte - RSSI of the blast message received. * * @return none */ static void addBlast( uint16 addr, byte rssi ) { blastAcc_t *ptr = blastPtr; while ( ptr ) { if ( ptr->addr == addr ) { break; } ptr = ptr->next; } if ( ptr ) { ptr->acc += rssi; ptr->cnt++; } else { ptr = (blastAcc_t *)osal_mem_alloc( sizeof( blastAcc_t ) ); if ( ptr ) { ptr->next = blastPtr; blastPtr = ptr; ptr->addr = addr; ptr->acc = rssi; ptr->cnt = 1; } } }
从这个函数的描述 Add or initialize an RSSI blast for the given Network Address.,对一个给定的网络地址初始化其信号质量或者累加信号质量,当一个网络地址第一次发送信息过来时由0到一个值叫初始化,后面再收到这个地址的时候就是累加过程。 还有为何是一个给定的网络地址,我们不是就一个CC2431吗? 其实这个协议栈实现了可以同时多个CC2431盲节点的定位系统,我们一般套件只有一个CC2431盲节点罢了。
这个函数实现很简单,就是一个对指针链表赋值的过程,每次收到信号后累加信号强度,我们稍微看家用到的结构体
typedef struct _blastAcc_t { struct _blastAcc_t *next; uint16 addr; uint16 acc; byte cnt; } blastAcc_t;
从结构体可以看到,包含了addr就是指明了这个结构体内容是哪个CC2431盲节点的,acc 是信号强度的累加,而cnt是一种累加了几次,通过acc 和cnt 就可以求出平均值,在这里我们知道CC2431 一共发送了8次,所以追踪code的时候,如果CC2430参考节点全部接收到信息,那么cnt应该是8.
这个函数的具体内容我们就不解释了。
CC2431在发送blast 信息之后还发送了一个信息,里面的cluster ID是LOCATION_XY_RSSI_REQUEST,我们再看下CC2430参考节点接到这个信息是如何处理的。
case LOCATION_XY_RSSI_REQUEST: rssiRsp( pkt ); break;
先把rssiRsp这个函数全部贴出来
/********************************************************************* * @fn rssiRsp * * @brief Respond to requester with average of their RSSI blasts. * * @param uint16 - Network address of the Blind Node requesting. * byte - Endpoint of the Blind Node requesting. * byte - RSSI of the blast message received. * * @return none */ static void rssiRsp( afIncomingMSGPacket_t *pkt ) { blastAcc_t *ptr = blastPtr; blastAcc_t *prev = NULL; byte options, radius; while ( ptr ) { if ( ptr->addr == pkt->srcAddr.addr.shortAddr ) { break; } prev = ptr; ptr = ptr->next; } if ( ptr ) { rspMsg[LOCATION_XY_RSSI_RSSI_IDX] = (ptr->acc + pkt->LinkQuality) / (ptr->cnt + 1); if ( prev ) { prev->next = ptr->next; } else { blastPtr = ptr->next; } osal_mem_free( ptr ); options = AF_SKIP_ROUTING; radius = 1; } else { rspMsg[LOCATION_XY_RSSI_RSSI_IDX] = pkt->LinkQuality; options = AF_TX_OPTIONS_NONE; radius = AF_DEFAULT_RADIUS; } pkt->srcAddr.addrMode = afAddr16Bit; (void)AF_DataRequest( &pkt->srcAddr, (endPointDesc_t *)&epDesc, LOCATION_XY_RSSI_RESPONSE, LOCATION_XY_RSSI_LEN, rspMsg, &transId, options, radius ); } /********************************************************************* *********************************************************************/
从函数的描述中Respond to requester with average of their RSSI blasts,我们可以看出这个是返回blast的平均RSSI值,函数可以分为三个部分分析,和上面一样,由于使用了链表,所以需要根据这条信息的短地址进行匹配,上面说过,这个网络中支持多个盲节点的定位。
if ( ptr ) { rspMsg[LOCATION_XY_RSSI_RSSI_IDX] = (ptr->acc + pkt->LinkQuality) / (ptr->cnt + 1); if ( prev ) { prev->next = ptr->next; } else { blastPtr = ptr->next; } osal_mem_free( ptr ); options = AF_SKIP_ROUTING; radius = 1;
上面这段代码的大概意思是匹配到短地址,然后求了一个平均值放到数组 rspMsg[LOCATION_XY_RSSI_RSSI_IDX],
但是也可能没有找到,下面就是没有找到的情况,这种情况出现在类似情形:运动的盲节点在开始发送blast的时候没有发送到这个参考节点,但是发送请求信号的时候正好运动到一个位置把请求信号发送到了这个节点,所以这个参考节点就没有之前的记录。但是得到请求不回复一下不好,所以在这个协议栈的设计是把这个请求信息的RSSI作为平均RSSI回复过去,正如下面的代码所示。
rspMsg[LOCATION_XY_RSSI_RSSI_IDX] = pkt->LinkQuality; options = AF_TX_OPTIONS_NONE; radius = AF_DEFAULT_RADIUS;
但是我们可以比较上面的两段code发现两个参数option 和radius 的设定是不一样的,这个是为何呢? 我们留在后面再说。
好了,RSSI的“均值”已经找到,那么就需要一个发送函数将这个RSSI发到CC2431盲节点了。
pkt->srcAddr.addrMode = afAddr16Bit; (void)AF_DataRequest( &pkt->srcAddr, (endPointDesc_t *)&epDesc, LOCATION_XY_RSSI_RESPONSE, LOCATION_XY_RSSI_LEN, rspMsg, &transId, options, radius );
上面发送的数据是rspMsg,发送长度是 LOCATION_XY_RSSI_LEN, 数据不仅仅包括了RSSI,还有这个参考节点的X Y坐标等信息,具体这个数据可自行查看代码,我们在后期解释节点配置的时候会再次说明。
好了,CC2430 参考节点在定位中充当的角色就这些,CC2430 盲节点发送blast 和request,CC2430 收集RSSI并将均值RSSI返回给盲节点。我们追到这里后面需要再返回到CC2431 了,因为CC2430这段代码执行的时机是在
if ( events & BLINDNODE_BLAST_EVT ) { if ( blastCnt == 0 ) { state = eBnBlastOff; finishCollection(); }
之前完成的,下面我们将接着分析CC2431 的finishCollection(); 具体参见下节分析。
CC2431定位套餐推荐:https://item.taobao.com/item.htm?id=527836022363