泰凌微sampleLight代码流程分析

下面我们分析一下telink泰凌微的zigbee SDK中的sampleLight这个demo的代码流程。
1、
我们从main函数开始看

apps/common/main.c

main()
-> user_init()
   -> user_app_init()
	  -> zcl_register(SAMPLE_LIGHT_ENDPOINT, SAMPLELIGHT_CB_CLUSTER_NUM, (zcl_specClusterInfo_t *)g_sampleLightClusterList)

从命名可以看出,g_sampleLightClusterList是light设备所有cluster的一个列表

apps/sampleLight/sampleLightEpCfg.c

/**
 *  @brief Definition for simple light ZCL specific cluster
 */
const zcl_specClusterInfo_t g_sampleLightClusterList[] =
{
	{ZCL_CLUSTER_GEN_BASIC,				 ZCL_BASIC_ATTR_NUM, 	basic_attrTbl,  		zcl_basic_register,			 sampleLight_basicCb},
	{ZCL_CLUSTER_GEN_IDENTIFY,			 ZCL_IDENTIFY_ATTR_NUM,	identify_attrTbl,		zcl_identify_register,		 sampleLight_identifyCb},
#ifdef ZCL_GROUP
	{ZCL_CLUSTER_GEN_GROUPS,			 ZCL_GROUP_ATTR_NUM, 	group_attrTbl,  		zcl_group_register,			 NULL},
#endif
#ifdef ZCL_SCENE
	{ZCL_CLUSTER_GEN_SCENES,			 ZCL_SCENE_ATTR_NUM,	scene_attrTbl,			zcl_scene_register,			 sampleLight_sceneCb},
#endif
#ifdef ZCL_ON_OFF
	{ZCL_CLUSTER_GEN_ON_OFF,			 ZCL_ONOFF_ATTR_NUM,	onOff_attrTbl,			zcl_onOff_register,			 sampleLight_onOffCb},
#endif
#ifdef ZCL_LEVEL_CTRL
	{ZCL_CLUSTER_GEN_LEVEL_CONTROL,		 ZCL_LEVEL_ATTR_NUM,	level_attrTbl,			zcl_level_register,			 sampleLight_levelCb},
#endif
#ifdef ZCL_LIGHT_COLOR_CONTROL
	{ZCL_CLUSTER_LIGHTING_COLOR_CONTROL, ZCL_COLOR_ATTR_NUM,	lightColorCtrl_attrTbl,	zcl_lightColorCtrl_register, sampleLight_colorCtrlCb},
#endif
};

u8 SAMPLELIGHT_CB_CLUSTER_NUM = (sizeof(g_sampleLightClusterList)/sizeof(g_sampleLightClusterList[0]));

而zcl_register()函数实际是调用g_sampleLightClusterList里面各项的clusterRegisterFunc

zigbee/zcl/zcl.c

_CODE_ZCL_ void zcl_register(u8 endpoint, u8 clusterNum, zcl_specClusterInfo_t *info)
{
	zcl_specClusterInfo_t *p = info;
	for(u32 i = 0; i < clusterNum; i++){
		if(p->clusterRegisterFunc){
			if(p->clusterRegisterFunc(endpoint, p->attrNum, p->attrTbl, p->clusterAppCb) == ZCL_STA_INSUFFICIENT_SPACE){
				return;
			}
		}
		p++;
	}
}

比如ZCL_CLUSTER_GEN_ON_OFF这个cluster的zcl_onOff_register:

zigbee/zcl/general/zcl_onoff.c

_CODE_ZCL_ status_t zcl_onOff_register(u8 endpoint, u8 attrNum, const zclAttrInfo_t attrTbl[], cluster_forAppCb_t cb)
{
	u8 status = ZCL_STA_SUCCESS;

    status = zcl_registerCluster(endpoint, ZCL_CLUSTER_GEN_ON_OFF, attrNum, attrTbl, zcl_onOff_cmdHandler, cb);

    if(status == ZCL_STA_SUCCESS){
    	zcl_onOff_startUpOnOff(endpoint);
    }

    return status;
}

然后调用到zcl层的公共接口

zigbee/zcl/zcl.c

/*********************************************************************
 * @fn      zcl_registerCluster
 *
 * @brief   Register the cluster to ZCL
 *
 * @param   endpoint  Specified endpoint
 * @param   clusterId Specified cluster ID
 * @param   attrNum   Specified attribute number in the cluster
 * @param   pAttrTbl  Specified attributes
 *
 * @return  ZCL Status @ref zcl_error_codes
 */
_CODE_ZCL_ status_t zcl_registerCluster(u8 endpoint, u16 clusterId, u8 attrNum, const zclAttrInfo_t *pAttrTbl, cluster_cmdHdlr_t cmdHdlrFn, cluster_forAppCb_t cb)
{
	if(zcl_vars.clusterNum >= ZCL_CLUSTER_NUM_MAX){
		return ZCL_STA_INSUFFICIENT_SPACE;
	}

	if(zcl_findCluster(endpoint, clusterId)){
		return ZCL_STA_DUPLICATE_EXISTS;
	}

	zcl_vars.clusterList[zcl_vars.clusterNum].endpoint = endpoint;
	zcl_vars.clusterList[zcl_vars.clusterNum].clusterID = clusterId;
	zcl_vars.clusterList[zcl_vars.clusterNum].attrNum = attrNum;
	zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn;
	zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb;
	zcl_vars.clusterList[zcl_vars.clusterNum++].attrTable = pAttrTbl;

	return ZCL_STA_SUCCESS;
}

这里重点关注以下这两项赋值

zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn; //zcl_onOff_cmdHandler
zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb; //sampleLight_onOffCb

1、从函数命名来看,cmdHandler就是处理各种命令的函数,每个cluster都有很多的command,这里就是处理收到的各种command

zigbee/zcl/general/zcl_onoff.c

_CODE_ZCL_ static status_t zcl_onOff_clientCmdHandler(zclIncoming_t *pInMsg)
{
	u8 status = ZCL_STA_SUCCESS;
	apsdeDataInd_t *pApsdeInd = (apsdeDataInd_t*)pInMsg->msg;
	u8 *pData = pInMsg->pData;
	
	zcl_onoff_cmdPayload_t cmdPayload;
	memset((u8 *)&cmdPayload, 0, sizeof(zcl_onoff_cmdPayload_t));

	switch(pInMsg->hdr.cmd)
	{
		case ZCL_CMD_ONOFF_OFF:
		case ZCL_CMD_ONOFF_ON:
		case ZCL_CMD_ONOFF_TOGGLE:
		case ZCL_CMD_ON_WITH_RECALL_GLOBAL_SCENE:

			break;
		case ZCL_CMD_OFF_WITH_EFFECT:
			cmdPayload.offWithEffect.effectId = pData[0];
			cmdPayload.offWithEffect.effectVariant = pData[1];
			break;
		case ZCL_CMD_ON_WITH_TIMED_OFF:
			cmdPayload.onWithTimeOff.onOffCtrl.onOffCtrl = *pData++;
			cmdPayload.onWithTimeOff.onTime = BUILD_U16(pData[0], pData[1]);
			pData += 2;
			cmdPayload.onWithTimeOff.offWaitTime = BUILD_U16(pData[0], pData[1]);
			pData += 2;
			break;
		default:
			status = ZCL_STA_UNSUP_CLUSTER_COMMAND;
			break;
	}

	if(status == ZCL_STA_SUCCESS){
		if(pInMsg->clusterAppCb){
			zclIncomingAddrInfo_t addrInfo;
			addrInfo.dirCluster = pInMsg->hdr.frmCtrl.bf.dir;
			addrInfo.profileId = pApsdeInd->indInfo.profile_id;
			addrInfo.srcAddr = pApsdeInd->indInfo.src_short_addr;
			addrInfo.dstAddr = pApsdeInd->indInfo.dst_addr;
			addrInfo.srcEp = pApsdeInd->indInfo.src_ep;
			addrInfo.dstEp = pApsdeInd->indInfo.dst_ep;

			pInMsg->clusterAppCb(&addrInfo, pInMsg->hdr.cmd, &cmdPayload);

#ifdef ZCL_SCENE
			u16 attrLen = 0;
			u8 sceneValid = 0;
			if(zcl_getAttrVal(pInMsg->msg->indInfo.dst_ep, ZCL_CLUSTER_GEN_SCENES, ZCL_ATTRID_SCENE_SCENE_VALID, &attrLen, (u8 *)&sceneValid) == ZCL_STA_SUCCESS){
				sceneValid = 0;
				zcl_setAttrVal(pInMsg->msg->indInfo.dst_ep, ZCL_CLUSTER_GEN_SCENES, ZCL_ATTRID_SCENE_SCENE_VALID, (u8 *)&sceneValid);
			}
#endif
		}
	}

	return status;
}

...

_CODE_ZCL_ static status_t zcl_onOff_cmdHandler(zclIncoming_t *pInMsg)
{
	if(pInMsg->hdr.frmCtrl.bf.dir == ZCL_FRAME_CLIENT_SERVER_DIR){
		return zcl_onOff_clientCmdHandler(pInMsg);
	}else{
		return ZCL_STA_UNSUP_CLUSTER_COMMAND;
	}
}

上面最终调用了pInMsg->clusterAppCb(&addrInfo, pInMsg->hdr.cmd, &cmdPayload),其实就是回调了前面赋值的cb函数

zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb; //sampleLight_onOffCb

apps/sampleLight/zcl_onOffCb.c

/*********************************************************************
 * @fn      sampleLight_onOffCb
 *
 * @brief   Handler for ZCL ONOFF command. This function will set ONOFF attribute first.
 *
 * @param	pAddrInfo
 * @param   cmdId - onoff cluster command id
 * @param	cmdPayload
 *
 * @return  status_t
 */
status_t sampleLight_onOffCb(zclIncomingAddrInfo_t *pAddrInfo, u8 cmdId, void *cmdPayload)
{
	zcl_onOffAttr_t *pOnOff = zcl_onoffAttrGet();
	
	if(pAddrInfo->dstEp == SAMPLE_LIGHT_ENDPOINT){
		switch(cmdId){
			case ZCL_CMD_ONOFF_ON:
			case ZCL_CMD_ONOFF_OFF:
			case ZCL_CMD_ONOFF_TOGGLE:
				sampleLight_onoff(cmdId);
				break;
			case ZCL_CMD_OFF_WITH_EFFECT:
				if(pOnOff->globalSceneControl == TRUE){
					/* TODO: store its settings in its global scene */

					pOnOff->globalSceneControl = FALSE;
				}
				sampleLight_onoff_offWithEffectProcess((zcl_onoff_offWithEffectCmd_t *)cmdPayload);
				break;
			case ZCL_CMD_ON_WITH_RECALL_GLOBAL_SCENE:
				if(pOnOff->globalSceneControl == FALSE){
					sampleLight_onoff_onWithRecallGlobalSceneProcess();
					pOnOff->globalSceneControl = TRUE;
				}
				break;
			case ZCL_CMD_ON_WITH_TIMED_OFF:
				sampleLight_onoff_onWithTimedOffProcess((zcl_onoff_onWithTimeOffCmd_t *)cmdPayload);
				break;
			default:
				break;
		}
	}

	return ZCL_STA_SUCCESS;
}

上面的函数比较简单,不同的command调用不同的接口去点灯,这里就不深究了,就是操作一些GPIO。

2、
上面的分析是针对收到消息后的处理流程,不过在这之前,我们还需要注册endpoint到AF层,还是回到user_app_init()函数:

apps/sampleLight/sampleLight.c

void user_app_init(void)
{
	...
	/* Register endPoint */
	af_endpointRegister(SAMPLE_LIGHT_ENDPOINT, (af_simple_descriptor_t *)&sampleLight_simpleDesc, zcl_rx_handler, NULL);
	...
    /* Register ZCL specific cluster information */
	zcl_register(SAMPLE_LIGHT_ENDPOINT, SAMPLELIGHT_CB_CLUSTER_NUM, (zcl_specClusterInfo_t *)g_sampleLightClusterList);
    ...
}

zigbee/zdo/app_framework.c

/****************************************************************************************************
 * @brief	AF layer endpoint descriptor register interface, call this function to add ep descriptor to the AF layer
 *
 * @param	ep: end point number
 * 			simple_desc: af_simple_descriptor_t
 * 			cb: call back function
 *
 * @return	TRUE: add success
 * 			FALSE: endpoint full, no space, add fail
 */
_CODE_ZDO_ bool af_endpointRegister(u8 ep, af_simple_descriptor_t *simple_desc, af_endpoint_cb_t rx_cb, af_dataCnf_cb_t cnfCb)
{
	if(ep == ZDO_EP){
		zdo_epDesc.cb_cnf = cnfCb;
		zdo_epDesc.cb_rx = rx_cb;
		zdo_epDesc.correspond_simple_desc = (af_simple_descriptor_t *)simple_desc;
		return TRUE;
	}

	for(u8 i = 0; i < MAX_ACTIVE_EP_NUMBER; i++){
		if(aed[i].ep == ep){
			return FALSE;
		}
	}

	if (available_active_ep_num < MAX_ACTIVE_EP_NUMBER) {
		aed[available_active_ep_num].ep = ep;
		aed[available_active_ep_num].correspond_simple_desc = (af_simple_descriptor_t *)simple_desc;
		aed[available_active_ep_num].cb_cnf = cnfCb;
		aed[available_active_ep_num++].cb_rx = rx_cb;
	}else{
		return FALSE;
	}
	return TRUE;
}

最终就是将zcl_rx_handler函数注册进AF层,后面用来处理下层传上来的command消息

zigbee/zcl/zcl.c

/*********************************************************************
 * @fn      zcl_cmdHandler
 *
 * @brief   ZCL command handler
 *
 * @param   pCmd
 *
 * @return  None
 */
_CODE_ZCL_ void zcl_cmdHandler(u8 *pCmd)
{
	apsdeDataInd_t *pApsdeInd = (apsdeDataInd_t*)pCmd;
	u8 status = ZCL_STA_SUCCESS;
	u8 toAppFlg = 0;
	printf("ljm : zcl_cmdHandler\n\r");

	zclIncoming_t inMsg;
	TL_SETSTRUCTCONTENT(inMsg,0);
	inMsg.msg = pApsdeInd;

	/* Parse Header */
	if(pApsdeInd->asdu[0] & ZCL_FRAME_CONTROL_MANU_SPECIFIC){
		if(pApsdeInd->asduLen >= 5){
			inMsg.hdr.frmCtrl.byte = pApsdeInd->asdu[0];
			inMsg.hdr.manufCode = BUILD_U16(pApsdeInd->asdu[1], pApsdeInd->asdu[2]);
			inMsg.hdr.seqNum = pApsdeInd->asdu[3];
			inMsg.hdr.cmd = pApsdeInd->asdu[4];
			inMsg.pData = &pApsdeInd->asdu[5];
			inMsg.dataLen = pApsdeInd->asduLen - 5;
		}else{
			status = ZCL_STA_FAILURE;
		}
	}else{
		if(pApsdeInd->asduLen >= 3){
			inMsg.hdr.frmCtrl.byte = pApsdeInd->asdu[0];
			inMsg.hdr.manufCode = 0;
			inMsg.hdr.seqNum = pApsdeInd->asdu[1];
			inMsg.hdr.cmd = pApsdeInd->asdu[2];
			inMsg.pData = &pApsdeInd->asdu[3];
			inMsg.dataLen = pApsdeInd->asduLen - 3;
		}else{
			status = ZCL_STA_FAILURE;
		}
	}

	if(status == ZCL_STA_FAILURE){
		ev_buf_free(pCmd);
		return;
	}

	u16 devEnableAttrLen = 0;
	bool devEnable = TRUE;

	/* Command dispatch */
	if(inMsg.hdr.frmCtrl.bf.type == ZCL_FRAME_TYPE_PROFILE_CMD){
		/* Foundation type message */
		if(inMsg.hdr.frmCtrl.bf.manufSpec){
			// We don't support any manufacturer specific command
			status = ZCL_STA_UNSUP_MANU_GENERAL_COMMAND;
		}else if(inMsg.hdr.cmd > ZCL_CMD_MAX){
			// Unsupported message
			status = ZCL_STA_UNSUP_GENERAL_COMMAND;
		}else{
			status = zcl_foundationCmdHandler(&inMsg);
			if((status != ZCL_STA_SUCCESS) && (status != ZCL_STA_CMD_HAS_RESP)){
				status = ZCL_STA_FAILURE;
			}
			toAppFlg = 1;
		}
	}else{
		/* Cluster specific command */
		if(inMsg.hdr.frmCtrl.bf.manufSpec){
			// We don't support any manufacturer specific command
			status = ZCL_STA_UNSUP_MANU_CLUSTER_COMMAND;
		}else{
			clusterInfo_t *pCluster = zcl_findCluster(pApsdeInd->indInfo.dst_ep, pApsdeInd->indInfo.cluster_id);
			if(!pCluster){
				status = ZCL_STA_UNSUP_CLUSTER_COMMAND;
			}else{
				/* Check if basic device enable support */
				zcl_getAttrVal(pApsdeInd->indInfo.dst_ep, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_DEV_ENABLED, &devEnableAttrLen, &devEnable);

				if(devEnable || (pCluster->clusterID == ZCL_CLUSTER_GEN_IDENTIFY)){
					inMsg.clusterAppCb = pCluster->clusterAppCb;
					status = pCluster->cmdHandlerFunc(&inMsg);

					devEnable = TRUE;
				}
			}
		}
	}

	if(devEnable){
		if((inMsg.hdr.frmCtrl.bf.disDefResp == 0 || status != ZCL_STA_SUCCESS) && UNICAST_MSG(inMsg.msg) && (status != ZCL_STA_CMD_HAS_RESP)){
			/* send default response */
			zcl_sendDfltRsp(&inMsg, inMsg.hdr.cmd, status);
		}
	}

	if(zcl_vars.hookFn && toAppFlg && inMsg.attrCmd){
		zcl_vars.hookFn(&inMsg);
		ev_buf_free(inMsg.attrCmd);
	}

	ev_buf_free(pCmd);
}

/*********************************************************************
 * @fn      zcl_task
 *
 * @brief   ZCL layer maintenance
 *
 * @param   arg
 *
 * @return  None
 */
_CODE_ZCL_ void zcl_task(void *arg)
{
	if(zcl_vars.zb2zclQ.curNum == 0){
		return;
	}

	u8 *pCmd = ev_queue_pop(&zcl_vars.zb2zclQ);
	if(!pCmd){
		return;
	}

	zcl_cmdHandler(pCmd);
}

/*********************************************************************
 * @fn      zcl_rx_handler
 *
 * @brief   Receive handler for data from APS/AF layer
 *
 * @param   pData     Received data
 *
 * @return  None
 */
_CODE_ZCL_ void zcl_rx_handler(void *pData)
{
	ev_queue_push(&zcl_vars.zb2zclQ, pData);
	TL_SCHEDULE_TASK(zcl_task, NULL);
}

从zcl_rx_handler到zcl_task,到最终的zcl_cmdHandler,函数里面调用的pCluster->cmdHandlerFunc(&inMsg),其实就回调了在zcl_registerCluster()中注册的zcl_onOff_cmdHandler()

_CODE_ZCL_ status_t zcl_registerCluster(u8 endpoint, u16 clusterId, u8 attrNum, const zclAttrInfo_t *pAttrTbl, cluster_cmdHdlr_t cmdHdlrFn, cluster_forAppCb_t cb)
{
	..
	zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn; //zcl_onOff_cmdHandler
	...

	return ZCL_STA_SUCCESS;
}

至此,处理消息的流程就分析到这里。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值