应用框架(AF)及OSAL处理来自AF的数据包流程
2014-11-27 17:54
274 查看
AF(应用框架层)是应用到APS层(应用支持子层)的OTA(空中下载技术)数据接口。此层也接收数据消息的终端多路复用器。
AF为应用提供以下功能:
· 终端(Endpoint)管理
· 发送和接收数据
终端管理
每个设备都是Zigbee中的节点,每个节点有长地址和短地址,短地址被其他设备用来发送数据。每个节点又241个终端(0保留,1-240 可分配给应用)。每个终端可以独立设置地址;当设备发送数据时必须指定目标设备的短地址和接收终端。一个应用必须注册一个或多个终端用来接收或者发送数据。
包含的子函数有:
void afInit( void );
简单描述符-SimpleDescriptionFormat_t
每个终端都必须有一个Zigbee简单描述。这些描述对Zigbee网络刻画了这个终端,其他设备可以询问这个终端以知道这个设备的类型。
typedef struct
{
byte EndPoint;
uint16 AppProfId;
uint16 AppDeviceId;
byte AppDevVer:4;
byte Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
byte AppNumInClusters;
cId_t *pAppInClusterList;
byte AppNumOutClusters;
cId_t *pAppOutClusterList;
} SimpleDescriptionFormat_t;
EndPoint – 终端号:1-240 这是节点的子地址,用来接收数据
AppProfId – 定义了这个终端上支持的Profile ID(剖面ID), ID最好遵循由ZigBee联盟的分配。
AppDeviceId –终端支持的设备ID,ID最好遵循ZigBee联盟的分配。
AppDevVer –此终端上设备执行的设备描述的版本:0x00为Version 1.0.
Reserved – 保留
AppNumInClusters – 终端支持的输入簇数目
pAppInClusterList – 指向输入Cluster ID列表的指针
AppNumOutClusters – 终端支持的输出簇数目
pAppOutClusterList – 指向输出Cluster ID列表的指针
终端描述符-endPointDesc_t
节点中的每一个终端都必须有一个终端描述符
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
task_id -任务ID指针,当接收到消息时,此任务ID将指示消息传递目的。接收到的消息是以OSAL消息形式包装的,将发送到一个任务
simpleDesc -指向这个终端的ZigBee简单描述
latencyReq -必须用noLatencyReqs来填充
afStatus_t afRegister( endPointDesc_t *epDesc );
为设备注册一个新的终端
epDesc -指向终端描述符
返回值:afStatus_t -若成功则返回ZSuccess,否则返回ZComDef.h中定义的错误
epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn );
在上面函数功能的基础上增加了回调函数,当终端的简单描述符被查询时将调用此回调函数。这样应有就可以动态改变简单描述符而不用RAM/ROM来储存描述符了。
descFn -回调函数指针。相关函数必须为简单描述符分配足够的空间,填充简单描述符,然后返回指向简单描述符的指针,调用者将释放为描述符分配空间。
epList -指向终端列表元件的指针,如果失败则为NULL
endPointDesc_t *afFindEndPointDesc( byte endPoint );
从一个终端找到终端描述符
endPoint -要寻找的终端描述符的终端号
endPointDesc_t -指向终端描述符的指针,若失败则为NULL
byte afFindSimpleDesc( SimpleDescriptionFormat_t **ppDesc, byte EP );
从一个终端找到终端描述符。若返回值非零则必须调用osal_mem_free()来释放描述符的内存占用。
ppDesc -指向指向简单描述符的指针。
EP –终端简单描述符需要
uint8 afGetMatch( uint8 ep );
默认情况下,设备将响应ZDO匹配描述符请求。用这个函数来获得ZDO匹配描述符应答的设置。
ep -用来获得ZDO匹配描述符响应行为的终端
返回值:TRUE-允许响应,FALSE-不允许或者终端未找到
uint8 afSetMatch( uint8 ep, uint8 action );
默认情况下,设备将响应ZDO匹配描述符。可以用这个函数来改变这个行为,比如ep为1,action为FALSE,ZDO将不响应终端1的ZDO匹配面述符请求。
ep -用来获得ZDO匹配描述符响应行为的终端
action -TRUE-允许响应,FALSE-不允许或者终端未找到
返回值:TRUE-成功,FALSE-失败或者终端未找到
byte afNumEndPoints( void );
查找已注册的终端数目,返回此设备上已注册的终端数目,包括终端0
void afEndPoints( byte *epBuf, byte skipZDO );
返回包含已注册的终端的一个数组。
epBuf – 指向存放终端的数组,每个终端占一个字节
skipZDO -设置为TRUE,则不包含ZDO终端(终端0)
发送数据
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius );
dstAddr -目标地址指针。
afAddrNotPresent 由反射器(邦定源,也即路由器或者协调器)指定
afAddrGroup 发送到组
afAddrBroadcast 发送广播消息
afAddr16Bit 直接发送到节点(单播)
srcEP -发送终端的终端描述符指针
cID -簇ID,cluster ID如同消息ID,并且在剖面(profile)中各不相同
len -要发送的字节数
buf -指向要发送的数据缓存的指针
transID -事务序列号指针。如果消息缓存发送,这个函数将增加这个数字
options -发送选项,可以由下面一项,或几项相或得到
AF_ACK_REQUEST 0x10 要求APS应答,这是应用层的应答,支在直接发送(单播)时使用。
AF_DISCV_ROUTE 0x20 总要包含这个选项
AF_SKIP_ROUTING 0x80 设置这个选项将导致设备跳过路由而直接发送消息。终点设备将不向其父亲发送消息。在直接发送(单播)和广播消息时很好用。
radius – 最大的跳数,用默认值AF_DEFAULT_RADIUS
afStatus_t – 成功则为ZSuccess(defined in ZComDef.h). 否则 Errors(defined in ZComDef.h)
uint8 afDataReqMTU( afDataReqMTU_t* fields );
找出基于输入参数的最大可发送字节数,返回能发送的最大字节数
fields -要发送的消息类型参数
typedef struct
{
uint8 kvp;
APSDE_DataReqMTU_t aps;
} afDataReqMTU_t;
kvp – 设为false.
typedef struct
{
uint8 secure;
} APSDE_DataReqMTU_t;
aps.secure – 设为false.如果在一个安全网络中此位将自动设置
以下为系统处理来自AF层数据包的大致流程,
afIncomingData() ——afBuildMSGIncoming() ——osal_msg_send() —— osal_set_event()——
根据task_id调用事件处理函数(如SampleApp_ProcessEvent()) ——判断具体事件类型调用相应回调函数(如SampleApp_MessageMSGCB()) ——实现具体现象
afIncomingData()函数用来从APS层传递一个ASDU到AF层;中间调用了afBuildMSGIncoming()函数,这个函数是用来为APS层建立一个特定格式的消息包,然后再调用osal_msg_send()把消息(包含了ASDU)传往AF层.
AF层规定接收的数据包的类型如下:
typedef struct
{
osal_event_hdr_t hdr;
uint16 groupId;
uint16 clusterId;
afAddrType_t srcAddr;
byte endPoint;
byte wasBroadcast;
byte LinkQuality;
byte SecurityUse;
uint32 timestamp;
afMSGCommandFormat_t cmd;
} afIncomingMSGPacket_t;
首先看一下afIncomingData()函数
/*********************************************************************
* @fn afIncomingData
*
* @brief Transfer a data PDU (ASDU) from the APS sub-layer to the AF.
*
* @param aff - pointer to APS frame format
* @param SrcAddress - Source address
* @param LinkQuality - incoming message's link quality
* @param SecurityUse - Security enable/disable
*
* @return none
*/
//传输数据:APS---->AF
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress,
uint8 LinkQuality, byte SecurityUse, uint32 timestamp )
{
endPointDesc_t *epDesc = NULL;
uint16 epProfileID = 0xFFFF; // Invalid Profile ID
epList_t *pList;
uint8 grpEp;
//-----------
/*如果这个帧传递模式是组传递*/
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found,没找到终端
epDesc = afFindEndPointDesc( grpEp ); //找到终端,接着找终端描述符
if ( epDesc == NULL )
return; // Endpoint descriptor not found,没找到终端描述符
pList = afFindEndPointDescList( epDesc->endPoint ); //找到终端描述符
} //pList指向终端列表中的元素
//-----------
/*广播到各端点*/
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( (pList!=NULL) )
{
epDesc = pList->epDesc;
}
}
//-----------
/*单播到特定端点*/
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
//-----------
while ( epDesc )
{
if ( pList->pfnDescCB ) //如果有回叫函数
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc ) //简单描述符
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ) //符合各条件
{
{
//建立信息传递,注意,这里调用afBuildMSGIncoming()!!
afBuildMSGIncoming( aff, epDesc, SrcAddress, LinkQuality, SecurityUse, timestamp );
}
}
/*组传递模式,找下一个终端*/
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint ); //epDesc != NULL
}
/*广播传递模式,找下一个终端*/
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
/*单播模式,无下一终端*/
else
epDesc = NULL;
}
}
//----------------------------------------------------------------------------------------
afBuildMSGIncoming( aff, epDesc, SrcAddress, LinkQuality, SecurityUse, timestamp )
afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint8 LinkQuality, byte SecurityUse,
uint32 timestamp )
实参——形参
Aff——*aff
epDesc——*epDesc
SrcAddress——*SrcAddress
LinkQuality—— LinkQuality
SecurityUse—— SecurityUse
Timestamp—— timestamp
看一下afBuildMSGIncoming()函数
/*********************************************************************
* @fn afBuildMSGIncoming
*
* @brief Build the message for the app
*
* @param
*
* @return pointer to next in data buffer
*/*********************************************************************
//Build the message for the app
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint8 LinkQuality, byte SecurityUse,
uint32 timestamp )
{
afIncomingMSGPacket_t *MSGpkt; //AF层需要接收这种结构体类型的信息包
//下面就通过本函数来为接收到的信息构造这种类型
//信息包,从而可以发送到AF层去
const byte len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength; //长度
byte *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len ); //分配内存
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD; //事件类型
MSGpkt->groupId = aff->GroupID; //组ID
MSGpkt->clusterId = aff->ClusterID; //簇ID
afCopyAddress( &MSGpkt->srcAddr, SrcAddress ); //源地址
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast; //广播
MSGpkt->LinkQuality = LinkQuality; //链路质量
MSGpkt->SecurityUse = SecurityUse; //安全使能
MSGpkt->timestamp = timestamp; //时间
MSGpkt->cmd.TransSeqNumber = 0; //传送序号
MSGpkt->cmd.DataLength = aff->asduLength; //长度
if ( MSGpkt->cmd.DataLength ) //aff->asduLength
{
MSGpkt->cmd.Data = (byte *)(MSGpkt + 1); //空间
//把长为 MSGpkt->cmd.DataLength数据从asdu赋给MSGpkt->cmd.Data
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else //无数据
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If MT has subscribed for this callback, don't send as a message.
if AFCB_CHECK(MSGpkt->endPoint, *(epDesc->task_id), SPI_CB_AF_DATA_IND)
{
af_MTCB_IncomingData( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
//数据包构造好后,就要发送到AF层,这里调用osal_msg_send()
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
/*********************************************************************
看下osal_msg_send()函数
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
byte osal_msg_send( byte destination_task, byte *msg_ptr )
实参——形参
*(epDesc->task_id)—— destination_task
(uint8 *)MSGpkt——*msg_ptr
/*********************************************************************
* @fn osal_msg_send
*
* @brief
*
* This function is called by a task to send a command message to
* another task or processing element. The sending_task field must
* refer to a valid task, since the task ID will be used
* for the response message. This function will also set a message
* ready event in the destination tasks event list.
*
* @param byte destination task - Send msg to? Task ID 目的任务
* @param byte *msg_ptr - pointer to new message buffer 指向消息
* @param byte len - length of data in message 消息中的数据长度
*
* @return ZSUCCESS, INVALID_SENDING_TASK, INVALID_DESTINATION_TASK,
* INVALID_MSG_POINTER, INVALID_LEN
*/*********************************************************************
byte osal_msg_send( byte destination_task, byte *msg_ptr )
{
//--------------------------------
if ( msg_ptr == NULL ) //无消息
return ( INVALID_MSG_POINTER );
//--------------------------------
if ( destination_task >= tasksCnt ) //不在任务条目范围内???任务不合法
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_TASK );
}
//--------------------------------
// Check the message header
if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK ) //检查到指针不合法
{
osal_msg_deallocate( msg_ptr ); //释放这个消息内存
return ( INVALID_MSG_POINTER );
}
OSAL_MSG_ID( msg_ptr ) = destination_task; //检查到含有合法任务的消息,
//则把目的任务的ID赋给消息结构体的dest_id
//OSAL_MSG_ID()参见前面
//--------------------------------
// queue message 把当前消息(msg_ptr所指)加入到系统消息列表中
osal_msg_enqueue( &osal_qHead, msg_ptr );
//--------------------------------
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG ); //设置事件发生标志函数!!
return ( ZSUCCESS );
}
/*********************************************************************
看下osal_set_event()函数
osal_set_event( destination_task, SYS_EVENT_MSG )
osal_set_event( byte task_id, UINT16 event_flag )
实参——形参
destination_task—— task_id
SYS_EVENT_MSG—— event_flag
/*********************************************************************
* @fn osal_set_event
*
* @brief
*
* This function is called to set the event flags for a task. The
* event passed in is OR'd into the task's event variable.
*
* @param byte task_id - receiving tasks ID
* @param byte event_flag - what event to set
*
* @return ZSUCCESS, INVALID_TASK
*/*********************************************************************/
byte osal_set_event( byte task_id, UINT16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s) 相应任务有事件发生
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
}
else
return ( INVALID_TASK );
return ( ZSUCCESS );
}
/*********************************************************************
从上面的tasksEvents[task_id] |= event_flag;系统主循环函数中会轮询到相应任务有事件发生(这点请参照OSAL系统主循环流程),因而调用相应的任务事件处理函数.比如说这个消息是从另一个节点发过来的flash消息(簇ID为SAMPLEAPP_FLASH_CLUSTERID),那么是用户应用任务的消息事件,则task_id=6,
Event_flag在设置事件发生标志函数中被定义为SYS_EVENT_MSG,因而当系统主循环函数轮询到用户应用任务有事件发生时,就调用其事件处理函数SampleApp_ProcessEvent(),来看下这个函数:
函数中对应的task_id=6,events=SYS_EVENT_MSG,而在构造AF信息包的时候,MSGpkt->hdr.event
= AF_INCOMING_MSG_CMD; 因而函数最终调用SampleApp_MessageMSGCB( MSGpkt )进行处理.
/*********************************************************************
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt; //接收到的消息
/*如果是系统消息*///判断OSAL层的消息类型
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); //接收属于本用户应用SampleApp的
消息(SampleApp_TaskID来标志)
while ( MSGpkt ) //接收到着
{ //属于这个应用的消息osal_msg_receive(
MApp_TaskID );
switch ( MSGpkt->hdr.event ) //判断数据包事件类型
{
// Received when a key is pressed
/*事件:按键事件*/
case KEY_CHANGE: //#define
KEY_CHANGE 0xC0 --Key Events
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break; //执行按键处理函数
// Received when a messages is received (OTA:over the air) for this endpoint
/*事件:收到信息事件*/
case AF_INCOMING_MSG_CMD: // #define AF_INCOMING_MSG_CMD 0x 1A --Incoming MSG type message
SampleApp_MessageMSGCB( MSGpkt );
break;
// Received whenever the device changes state in the network
/*事件:端点状态变化事件*/
case ZDO_STATE_CHANGE: //#define ZDO_STATE_CHANGE 0xD1 --ZDO has changed the device's network state
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (SampleApp_NwkState == DEV_ZB_COORD)
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
// Start sending the periodic message in a regular interval.
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in the network
}
break;
default:
break;
}
// Release the memory
//释放消息占用的内存
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next - if one is available
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID
);
} //end: while
( MSGpkt )
// return unprocessed events
// 判断是否有未处理的系统消息,有则接收返回没有处理的事件
return (events ^ SYS_EVENT_MSG); //注意!这里 return到osal_start_system()下
}
//--------------------------
// Send a message out - This event is generated by a timer
// (setup in SampleApp_Init()).
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) //发送周期消息
{
// Send the periodic message
SampleApp_SendPeriodicMessage();
// Setup to send message again in normal period (+ a little jitter)
osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );
// return unprocessed events
return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
// Discard unknown events
return 0;
}
/*********************************************************************
最后我们再来看下SampleApp_MessageMSGCB()函数
SampleApp_MessageMSGCB( MSGpkt )
SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
实参——形参
MSGpkt——*pkt
通过判断信息包中的簇ID,为SAMPLEAPP_FLASH_CLUSTERID,因而最终执行小灯闪烁四下.
/*********************************************************************
* @fn SampleApp_MessageMSGCB
*
* @brief Data message processor callback. This function processes
* any incoming data - probably from other devices. So, based
* on cluster ID, perform the intended action.
*
* @param none
*
* @return none
*/
//SampleApp_MessageMSGCB()功能是处理接收数据,函数
//的输入为接收到的数据,而输出为小灯闪烁的时间。
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint16 flashTime;
switch ( pkt->clusterId )//判断簇ID
{
case SAMPLEAPP_PERIODIC_CLUSTERID: //periodic
break;
case SAMPLEAPP_FLASH_CLUSTERID: //flash
flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) ); //小灯闪烁四次
break;
}
}
/*********************************************************************
AF为应用提供以下功能:
· 终端(Endpoint)管理
· 发送和接收数据
终端管理
每个设备都是Zigbee中的节点,每个节点有长地址和短地址,短地址被其他设备用来发送数据。每个节点又241个终端(0保留,1-240 可分配给应用)。每个终端可以独立设置地址;当设备发送数据时必须指定目标设备的短地址和接收终端。一个应用必须注册一个或多个终端用来接收或者发送数据。
包含的子函数有:
void afInit( void );
简单描述符-SimpleDescriptionFormat_t
每个终端都必须有一个Zigbee简单描述。这些描述对Zigbee网络刻画了这个终端,其他设备可以询问这个终端以知道这个设备的类型。
typedef struct
{
byte EndPoint;
uint16 AppProfId;
uint16 AppDeviceId;
byte AppDevVer:4;
byte Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
byte AppNumInClusters;
cId_t *pAppInClusterList;
byte AppNumOutClusters;
cId_t *pAppOutClusterList;
} SimpleDescriptionFormat_t;
EndPoint – 终端号:1-240 这是节点的子地址,用来接收数据
AppProfId – 定义了这个终端上支持的Profile ID(剖面ID), ID最好遵循由ZigBee联盟的分配。
AppDeviceId –终端支持的设备ID,ID最好遵循ZigBee联盟的分配。
AppDevVer –此终端上设备执行的设备描述的版本:0x00为Version 1.0.
Reserved – 保留
AppNumInClusters – 终端支持的输入簇数目
pAppInClusterList – 指向输入Cluster ID列表的指针
AppNumOutClusters – 终端支持的输出簇数目
pAppOutClusterList – 指向输出Cluster ID列表的指针
终端描述符-endPointDesc_t
节点中的每一个终端都必须有一个终端描述符
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
task_id -任务ID指针,当接收到消息时,此任务ID将指示消息传递目的。接收到的消息是以OSAL消息形式包装的,将发送到一个任务
simpleDesc -指向这个终端的ZigBee简单描述
latencyReq -必须用noLatencyReqs来填充
afStatus_t afRegister( endPointDesc_t *epDesc );
为设备注册一个新的终端
epDesc -指向终端描述符
返回值:afStatus_t -若成功则返回ZSuccess,否则返回ZComDef.h中定义的错误
epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn );
在上面函数功能的基础上增加了回调函数,当终端的简单描述符被查询时将调用此回调函数。这样应有就可以动态改变简单描述符而不用RAM/ROM来储存描述符了。
descFn -回调函数指针。相关函数必须为简单描述符分配足够的空间,填充简单描述符,然后返回指向简单描述符的指针,调用者将释放为描述符分配空间。
epList -指向终端列表元件的指针,如果失败则为NULL
endPointDesc_t *afFindEndPointDesc( byte endPoint );
从一个终端找到终端描述符
endPoint -要寻找的终端描述符的终端号
endPointDesc_t -指向终端描述符的指针,若失败则为NULL
byte afFindSimpleDesc( SimpleDescriptionFormat_t **ppDesc, byte EP );
从一个终端找到终端描述符。若返回值非零则必须调用osal_mem_free()来释放描述符的内存占用。
ppDesc -指向指向简单描述符的指针。
EP –终端简单描述符需要
uint8 afGetMatch( uint8 ep );
默认情况下,设备将响应ZDO匹配描述符请求。用这个函数来获得ZDO匹配描述符应答的设置。
ep -用来获得ZDO匹配描述符响应行为的终端
返回值:TRUE-允许响应,FALSE-不允许或者终端未找到
uint8 afSetMatch( uint8 ep, uint8 action );
默认情况下,设备将响应ZDO匹配描述符。可以用这个函数来改变这个行为,比如ep为1,action为FALSE,ZDO将不响应终端1的ZDO匹配面述符请求。
ep -用来获得ZDO匹配描述符响应行为的终端
action -TRUE-允许响应,FALSE-不允许或者终端未找到
返回值:TRUE-成功,FALSE-失败或者终端未找到
byte afNumEndPoints( void );
查找已注册的终端数目,返回此设备上已注册的终端数目,包括终端0
void afEndPoints( byte *epBuf, byte skipZDO );
返回包含已注册的终端的一个数组。
epBuf – 指向存放终端的数组,每个终端占一个字节
skipZDO -设置为TRUE,则不包含ZDO终端(终端0)
发送数据
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius );
dstAddr -目标地址指针。
afAddrNotPresent 由反射器(邦定源,也即路由器或者协调器)指定
afAddrGroup 发送到组
afAddrBroadcast 发送广播消息
afAddr16Bit 直接发送到节点(单播)
srcEP -发送终端的终端描述符指针
cID -簇ID,cluster ID如同消息ID,并且在剖面(profile)中各不相同
len -要发送的字节数
buf -指向要发送的数据缓存的指针
transID -事务序列号指针。如果消息缓存发送,这个函数将增加这个数字
options -发送选项,可以由下面一项,或几项相或得到
AF_ACK_REQUEST 0x10 要求APS应答,这是应用层的应答,支在直接发送(单播)时使用。
AF_DISCV_ROUTE 0x20 总要包含这个选项
AF_SKIP_ROUTING 0x80 设置这个选项将导致设备跳过路由而直接发送消息。终点设备将不向其父亲发送消息。在直接发送(单播)和广播消息时很好用。
radius – 最大的跳数,用默认值AF_DEFAULT_RADIUS
afStatus_t – 成功则为ZSuccess(defined in ZComDef.h). 否则 Errors(defined in ZComDef.h)
uint8 afDataReqMTU( afDataReqMTU_t* fields );
找出基于输入参数的最大可发送字节数,返回能发送的最大字节数
fields -要发送的消息类型参数
typedef struct
{
uint8 kvp;
APSDE_DataReqMTU_t aps;
} afDataReqMTU_t;
kvp – 设为false.
typedef struct
{
uint8 secure;
} APSDE_DataReqMTU_t;
aps.secure – 设为false.如果在一个安全网络中此位将自动设置
以下为系统处理来自AF层数据包的大致流程,
afIncomingData() ——afBuildMSGIncoming() ——osal_msg_send() —— osal_set_event()——
根据task_id调用事件处理函数(如SampleApp_ProcessEvent()) ——判断具体事件类型调用相应回调函数(如SampleApp_MessageMSGCB()) ——实现具体现象
afIncomingData()函数用来从APS层传递一个ASDU到AF层;中间调用了afBuildMSGIncoming()函数,这个函数是用来为APS层建立一个特定格式的消息包,然后再调用osal_msg_send()把消息(包含了ASDU)传往AF层.
AF层规定接收的数据包的类型如下:
typedef struct
{
osal_event_hdr_t hdr;
uint16 groupId;
uint16 clusterId;
afAddrType_t srcAddr;
byte endPoint;
byte wasBroadcast;
byte LinkQuality;
byte SecurityUse;
uint32 timestamp;
afMSGCommandFormat_t cmd;
} afIncomingMSGPacket_t;
首先看一下afIncomingData()函数
/*********************************************************************
* @fn afIncomingData
*
* @brief Transfer a data PDU (ASDU) from the APS sub-layer to the AF.
*
* @param aff - pointer to APS frame format
* @param SrcAddress - Source address
* @param LinkQuality - incoming message's link quality
* @param SecurityUse - Security enable/disable
*
* @return none
*/
//传输数据:APS---->AF
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress,
uint8 LinkQuality, byte SecurityUse, uint32 timestamp )
{
endPointDesc_t *epDesc = NULL;
uint16 epProfileID = 0xFFFF; // Invalid Profile ID
epList_t *pList;
uint8 grpEp;
//-----------
/*如果这个帧传递模式是组传递*/
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found,没找到终端
epDesc = afFindEndPointDesc( grpEp ); //找到终端,接着找终端描述符
if ( epDesc == NULL )
return; // Endpoint descriptor not found,没找到终端描述符
pList = afFindEndPointDescList( epDesc->endPoint ); //找到终端描述符
} //pList指向终端列表中的元素
//-----------
/*广播到各端点*/
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( (pList!=NULL) )
{
epDesc = pList->epDesc;
}
}
//-----------
/*单播到特定端点*/
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
//-----------
while ( epDesc )
{
if ( pList->pfnDescCB ) //如果有回叫函数
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc ) //简单描述符
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ) //符合各条件
{
{
//建立信息传递,注意,这里调用afBuildMSGIncoming()!!
afBuildMSGIncoming( aff, epDesc, SrcAddress, LinkQuality, SecurityUse, timestamp );
}
}
/*组传递模式,找下一个终端*/
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint ); //epDesc != NULL
}
/*广播传递模式,找下一个终端*/
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
/*单播模式,无下一终端*/
else
epDesc = NULL;
}
}
//----------------------------------------------------------------------------------------
afBuildMSGIncoming( aff, epDesc, SrcAddress, LinkQuality, SecurityUse, timestamp )
afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint8 LinkQuality, byte SecurityUse,
uint32 timestamp )
实参——形参
Aff——*aff
epDesc——*epDesc
SrcAddress——*SrcAddress
LinkQuality—— LinkQuality
SecurityUse—— SecurityUse
Timestamp—— timestamp
看一下afBuildMSGIncoming()函数
/*********************************************************************
* @fn afBuildMSGIncoming
*
* @brief Build the message for the app
*
* @param
*
* @return pointer to next in data buffer
*/*********************************************************************
//Build the message for the app
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint8 LinkQuality, byte SecurityUse,
uint32 timestamp )
{
afIncomingMSGPacket_t *MSGpkt; //AF层需要接收这种结构体类型的信息包
//下面就通过本函数来为接收到的信息构造这种类型
//信息包,从而可以发送到AF层去
const byte len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength; //长度
byte *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len ); //分配内存
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD; //事件类型
MSGpkt->groupId = aff->GroupID; //组ID
MSGpkt->clusterId = aff->ClusterID; //簇ID
afCopyAddress( &MSGpkt->srcAddr, SrcAddress ); //源地址
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast; //广播
MSGpkt->LinkQuality = LinkQuality; //链路质量
MSGpkt->SecurityUse = SecurityUse; //安全使能
MSGpkt->timestamp = timestamp; //时间
MSGpkt->cmd.TransSeqNumber = 0; //传送序号
MSGpkt->cmd.DataLength = aff->asduLength; //长度
if ( MSGpkt->cmd.DataLength ) //aff->asduLength
{
MSGpkt->cmd.Data = (byte *)(MSGpkt + 1); //空间
//把长为 MSGpkt->cmd.DataLength数据从asdu赋给MSGpkt->cmd.Data
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else //无数据
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If MT has subscribed for this callback, don't send as a message.
if AFCB_CHECK(MSGpkt->endPoint, *(epDesc->task_id), SPI_CB_AF_DATA_IND)
{
af_MTCB_IncomingData( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
//数据包构造好后,就要发送到AF层,这里调用osal_msg_send()
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
/*********************************************************************
看下osal_msg_send()函数
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
byte osal_msg_send( byte destination_task, byte *msg_ptr )
实参——形参
*(epDesc->task_id)—— destination_task
(uint8 *)MSGpkt——*msg_ptr
/*********************************************************************
* @fn osal_msg_send
*
* @brief
*
* This function is called by a task to send a command message to
* another task or processing element. The sending_task field must
* refer to a valid task, since the task ID will be used
* for the response message. This function will also set a message
* ready event in the destination tasks event list.
*
* @param byte destination task - Send msg to? Task ID 目的任务
* @param byte *msg_ptr - pointer to new message buffer 指向消息
* @param byte len - length of data in message 消息中的数据长度
*
* @return ZSUCCESS, INVALID_SENDING_TASK, INVALID_DESTINATION_TASK,
* INVALID_MSG_POINTER, INVALID_LEN
*/*********************************************************************
byte osal_msg_send( byte destination_task, byte *msg_ptr )
{
//--------------------------------
if ( msg_ptr == NULL ) //无消息
return ( INVALID_MSG_POINTER );
//--------------------------------
if ( destination_task >= tasksCnt ) //不在任务条目范围内???任务不合法
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_TASK );
}
//--------------------------------
// Check the message header
if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK ) //检查到指针不合法
{
osal_msg_deallocate( msg_ptr ); //释放这个消息内存
return ( INVALID_MSG_POINTER );
}
OSAL_MSG_ID( msg_ptr ) = destination_task; //检查到含有合法任务的消息,
//则把目的任务的ID赋给消息结构体的dest_id
//OSAL_MSG_ID()参见前面
//--------------------------------
// queue message 把当前消息(msg_ptr所指)加入到系统消息列表中
osal_msg_enqueue( &osal_qHead, msg_ptr );
//--------------------------------
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG ); //设置事件发生标志函数!!
return ( ZSUCCESS );
}
/*********************************************************************
看下osal_set_event()函数
osal_set_event( destination_task, SYS_EVENT_MSG )
osal_set_event( byte task_id, UINT16 event_flag )
实参——形参
destination_task—— task_id
SYS_EVENT_MSG—— event_flag
/*********************************************************************
* @fn osal_set_event
*
* @brief
*
* This function is called to set the event flags for a task. The
* event passed in is OR'd into the task's event variable.
*
* @param byte task_id - receiving tasks ID
* @param byte event_flag - what event to set
*
* @return ZSUCCESS, INVALID_TASK
*/*********************************************************************/
byte osal_set_event( byte task_id, UINT16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s) 相应任务有事件发生
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
}
else
return ( INVALID_TASK );
return ( ZSUCCESS );
}
/*********************************************************************
从上面的tasksEvents[task_id] |= event_flag;系统主循环函数中会轮询到相应任务有事件发生(这点请参照OSAL系统主循环流程),因而调用相应的任务事件处理函数.比如说这个消息是从另一个节点发过来的flash消息(簇ID为SAMPLEAPP_FLASH_CLUSTERID),那么是用户应用任务的消息事件,则task_id=6,
Event_flag在设置事件发生标志函数中被定义为SYS_EVENT_MSG,因而当系统主循环函数轮询到用户应用任务有事件发生时,就调用其事件处理函数SampleApp_ProcessEvent(),来看下这个函数:
函数中对应的task_id=6,events=SYS_EVENT_MSG,而在构造AF信息包的时候,MSGpkt->hdr.event
= AF_INCOMING_MSG_CMD; 因而函数最终调用SampleApp_MessageMSGCB( MSGpkt )进行处理.
/*********************************************************************
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt; //接收到的消息
/*如果是系统消息*///判断OSAL层的消息类型
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); //接收属于本用户应用SampleApp的
消息(SampleApp_TaskID来标志)
while ( MSGpkt ) //接收到着
{ //属于这个应用的消息osal_msg_receive(
MApp_TaskID );
switch ( MSGpkt->hdr.event ) //判断数据包事件类型
{
// Received when a key is pressed
/*事件:按键事件*/
case KEY_CHANGE: //#define
KEY_CHANGE 0xC0 --Key Events
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break; //执行按键处理函数
// Received when a messages is received (OTA:over the air) for this endpoint
/*事件:收到信息事件*/
case AF_INCOMING_MSG_CMD: // #define AF_INCOMING_MSG_CMD 0x 1A --Incoming MSG type message
SampleApp_MessageMSGCB( MSGpkt );
break;
// Received whenever the device changes state in the network
/*事件:端点状态变化事件*/
case ZDO_STATE_CHANGE: //#define ZDO_STATE_CHANGE 0xD1 --ZDO has changed the device's network state
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (SampleApp_NwkState == DEV_ZB_COORD)
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
// Start sending the periodic message in a regular interval.
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in the network
}
break;
default:
break;
}
// Release the memory
//释放消息占用的内存
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next - if one is available
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID
);
} //end: while
( MSGpkt )
// return unprocessed events
// 判断是否有未处理的系统消息,有则接收返回没有处理的事件
return (events ^ SYS_EVENT_MSG); //注意!这里 return到osal_start_system()下
}
//--------------------------
// Send a message out - This event is generated by a timer
// (setup in SampleApp_Init()).
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) //发送周期消息
{
// Send the periodic message
SampleApp_SendPeriodicMessage();
// Setup to send message again in normal period (+ a little jitter)
osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );
// return unprocessed events
return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
// Discard unknown events
return 0;
}
/*********************************************************************
最后我们再来看下SampleApp_MessageMSGCB()函数
SampleApp_MessageMSGCB( MSGpkt )
SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
实参——形参
MSGpkt——*pkt
通过判断信息包中的簇ID,为SAMPLEAPP_FLASH_CLUSTERID,因而最终执行小灯闪烁四下.
/*********************************************************************
* @fn SampleApp_MessageMSGCB
*
* @brief Data message processor callback. This function processes
* any incoming data - probably from other devices. So, based
* on cluster ID, perform the intended action.
*
* @param none
*
* @return none
*/
//SampleApp_MessageMSGCB()功能是处理接收数据,函数
//的输入为接收到的数据,而输出为小灯闪烁的时间。
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint16 flashTime;
switch ( pkt->clusterId )//判断簇ID
{
case SAMPLEAPP_PERIODIC_CLUSTERID: //periodic
break;
case SAMPLEAPP_FLASH_CLUSTERID: //flash
flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) ); //小灯闪烁四次
break;
}
}
/*********************************************************************
相关文章推荐
- ZIGBEE--OSAL处理来自AF的数据包流程
- OSAL处理来自AF的数据包流程
- OSAL处理来自AF的数据包流程
- OSAL处理来自AF的数据包流程
- OSAL处理接收数据包流程
- 谈谈各大框架/产品的流程应用
- Linux内核bridge中的数据包处理流程
- ACE Reactor框架处理事件及多个I/O流,应用举例。
- Asp.Net 构架(Http请求处理流程) - Part.1 (来自张子阳的博客)
- Portlet框架请求处理流程
- linux ipsec 数据包处理流程
- Linux内核bridge中的数据包处理流程(转载)
- linux kernel 处理网络数据包流程
- 从应用框架AF角度看ListView和Adapter by 高焕堂
- iPhone企业应用实例分析之二:程序处理流程
- AOP应用实例--Spring事务处理及其AOP框架的内幕 选择自 jwsh1984 的 Blog
- BCM芯片数据包转发基本流程--ingress处理之vlan处理
- 学贯前后-WebWork框架的处理流程(2)