Sample Application工程概况描述.docx
《Sample Application工程概况描述.docx》由会员分享,可在线阅读,更多相关《Sample Application工程概况描述.docx(12页珍藏版)》请在冰豆网上搜索。
SampleApplication工程概况描述
一、SampleApplication工程概况描述
SampleApplication是ZStack协议栈提供的一个非常简单的演示实例,实例中的每个设备都可以发送和接收两种信息:
周期信息和闪烁信息。
周期信息---------当设备加入该网络后,所有设备每隔5S(加上一个随机数,毫秒mS为单位)发送一个周期信息,该信息的数据载荷为发送信息的次数。
闪烁信息---------通过按下按键SW1发送一个控制LED灯闪烁的广播信息,该广播信息只针对组1内的所有设备。
所有设备初始化都被加入组1,所以网络一旦建立完成便可执行LED灯闪烁实验。
可以通过按下设备的SW2退出组1,如果设备退出组1则不再接收来自组1的消息,其按键SW1发送的消息也不再控制组1LED灯的闪烁。
通过再次按下SW2便可让设备再次加入到组1,从而又可以接受来自组1的消息,其SW1也可以控制组1内设备的LED灯闪烁了。
当设备接收到闪烁信息会闪烁LED灯,而当接收到周期信息时协议栈没有提供具体的实验现象,留给了用户自行处理,可以根据实际需要自行更改实验代码。
在该工程中使用了两个按键SW1和SW2。
即ZStack协议栈中的HAL_KEY_SW_1和HAL_KEY_SW_2。
同时工程中也定义了一个事件用来处理周期信息事件,即SAMPLEAPP_SEND_PERIODIC_MSG_EVT[SampleApp.h]。
二、一般工程说明:
在学习ZStack协议栈的时候我们要把握一个重点就是事件的产生和事件的处理。
任务的初始化为事件的产生制造了“温床”,是事件产生的前提,任何工程都需要先初始化。
当有事件产生OS就会调用相应的处理函数进行处理。
在OS循环那一节我们可以看到在任务初始化的最后一项就是应用层的初始化,而在指向处理函数的指针数组中最后一项是对应的应用层的处理函数。
应用层相关事件会由应用层处理函数进行处理。
每一层都是相互对应,各司其职。
三、SampleApplication工程初始化与事件的处理
3.1、SampleApplication工程初始化如下:
voidSampleApp_Init(uint8task_id)
{
SampleApp_TaskID=task_id;//OS通过数参数的传递为每一层分发任务ID,
SampleApp_NwkState=DEV_INIT;//设定设备的网络状态为“初始化”
SampleApp_TransID=0;
#ifdefined(SOFT_START)
/*SOFT_START是一个编译选项,如果一个网络中没有协调
器可以让设备以协调器的形式启动*/
//这里我们根据跳线决定设备是路由器或者是协调器,如果检测到
//跳线则为协调器否则为路由器,在设备启动提及如果定义了SOFT_START
//则设备初始化时设备的类型为可选类型。
当程序执行到这里就明确了具
//体是什么类型的设备
if(readCoordinatorJumper())//如果检测到跳线则设备为协调器
zgDeviceLogicalType=ZG_DEVICETYPE_COORDINATOR;
else//如果没有检测到跳线则设备为路由器
zgDeviceLogicalType=ZG_DEVICETYPE_ROUTER;
#endif//SOFT_START
#ifdefined(HOLD_AUTO_START)
//如果编译了HOLD_AUTO_START则执行以下函数
ZDOInitDevice(0);
#endif
//设定周期信息的地址,此地址为广播地址0xFFFF
SampleApp_Periodic_DstAddr.addrMode=(afAddrMode_t)AddrBroadcast;
SampleApp_Periodic_DstAddr.endPoint=SAMPLEAPP_ENDPOINT;
SampleApp_Periodic_DstAddr.addr.shortAddr=0xFFFF;
//设定闪烁信息的地址,此地址为组1的地址
SampleApp_Flash_DstAddr.addrMode=(afAddrMode_t)afAddrGroup;
SampleApp_Flash_DstAddr.endPoint=SAMPLEAPP_ENDPOINT;
SampleApp_Flash_DstAddr.addr.shortAddr=SAMPLEAPP_FLASH_GROUP;
//对端点SAMPLEAPP_ENDPOINT进行描述
SampleApp_epDesc.endPoint=SAMPLEAPP_ENDPOINT;
SampleApp_epDesc.task_id=&SampleApp_TaskID;
SampleApp_epDesc.simpleDesc
=(SimpleDescriptionFormat_t*)&SampleApp_SimpleDesc;
SampleApp_epDesc.latencyReq=noLatencyReqs;
//注册端点描述符
afRegister(&SampleApp_epDesc);
//注册按键,在按键机制详细解释
RegisterForKeys(SampleApp_TaskID);
//默认情况,所有的设备都加入组1
SampleApp_Group.ID=0x0001;//设定组ID
osal_memcpy(SampleApp_Group.name,"Group1",7);//设定组名
aps_AddGroup(SAMPLEAPP_ENDPOINT,&SampleApp_Group);//加入组
//如果编译了LCD_SUPPORTED,在液晶上显示"SampleApp"。
注意:
需要底层硬//件的支持
#ifdefined(LCD_SUPPORTED)
HalLcdWriteString("SampleApp",HAL_LCD_LINE_1);
#endif
}
3.2、SampleApplication工程初始化事件处理函数如下:
uint16SampleApp_ProcessEvent(uint8task_id,uint16events)
{
afIncomingMSGPacket_t*MSGpkt;
if(events&SYS_EVENT_MSG)
{
//从信息列表中获取SampleApp_TaskID相关的信息
MSGpkt=(afIncomingMSGPacket_t)osal_msg_receive(SampleApp_TaskID);
while(MSGpkt)//不为空,说明有信息
{
switch(MSGpkt->hdr.event)//消息的事件
{
//按键事件
caseKEY_CHANGE:
SampleApp_HandleKeys(((keyChange_t*)MSGpkt)->state,((keyChange_t*)MSGpkt)->keys);
break;
//OTA消息事件
caseAF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB(MSGpkt);
break;
//设备状态改变事件
caseZDO_STATE_CHANGE:
SampleApp_NwkState=(devStates_t)(MSGpkt->hdr.status);
if((SampleApp_NwkState==DEV_ZB_COORD)
||(SampleApp_NwkState==DEV_ROUTER)
||(SampleApp_NwkState==DEV_END_DEVICE))
{
//Startsendingtheperiodicmessageinaregularinterval.
osal_start_timerEx(SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT);
}
else
{
//Deviceisnolongerinthenetwork
}
break;
default:
break;
}
//释放内存以防内存泄露
osal_msg_deallocate((uint8*)MSGpkt);
//在列表中检索下一条信息
MSGpkt=(afIncomingMSGPacket_t*)osal_msg_receive(SampleApp_TaskID);
}
//返回没有处理的事件
return(events^SYS_EVENT_MSG);
}
//周期信息事件
if(events&SAMPLEAPP_SEND_PERIODIC_MSG_EVT)
{
//发送周期信息
SampleApp_SendPeriodicMessage();
//Setuptosendmessageagaininnormalperiod(+alittlejitter)
osal_start_timerEx(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT+(osal_rand()&0x00FF)));
//返回没有处理的事件
return(events^SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
//Discardunknownevents
return0;
}
三、SampleApplication工程流程:
1、周期信息:
在上一节我们看到设备上电就会自动启动(SampleApplication没有编译HOLD_AUTO_START),当设备启动成功最终触发了事件ZDO_STATE_CHANGE,而此事件会向所有注册过的端点(除ZDO)发送。
在SampleApplication的初始化代码中SampleApp_epDesc调用函数afRegister()进行了注册,所有OS会调用SampleApplication的处理函数SampleApp_ProcessEvent()进行处理。
处理代码如下:
程序代码:
caseZDO_STATE_CHANGE:
SampleApp_NwkState=(devStates_t)(MSGpkt->hdr.status);
if((SampleApp_NwkState==DEV_ZB_COORD)
||(SampleApp_NwkState==DEV_ROUTER)
||(SampleApp_NwkState==DEV_END_DEVICE))
{
//Startsendingtheperiodicmessageinaregularinterval.
osal_start_timerEx(SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT);
}
else
{
//Deviceisnolongerinthenetwork
}
break;
处理ZDO_STATE_CHANGE:
如果设备的网络状态为DEV_ZB_COORD、DEV_ROUTER或者DEV_END_DEVICE表明设备启动成功。
网络状态在设备启动时被设定。
如果设备启动成功,则调用了函数osal_start_timerEx()定时触发了事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT。
该事件的任务ID为SampleApp_TaskID,即该事件还是由SampleApplication的处理函数进行处理。
定时长度为SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT(5000mS[SampleApp.h])。
处理代码如下:
if(events&SAMPLEAPP_SEND_PERIODIC_MSG_EVT)
{
//发送周期信息
SampleApp_SendPeriodicMessage();
//定时再次触发事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT
osal_start_timerEx(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT+(osal_rand()&0x00FF)));
//返回没有处理完成的事件
return(events^SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
在处事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT时,协议栈调用了函数SampleApp_SendPeriodicMessage()。
在SampleApp_SendPeriodicMessage()处理完成后再次定时触发了事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT,其任务ID依旧是SampleApp_TaskID,定时长度SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT(5000mS[SampleApp.h])。
可以看出周期信息就是这样被周期性地触发SAMPLEAPP_SEND_PERIODIC_MSG_EVT产生的。
间隔时间就是定时长度SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT(5000mS[SampleApp.h])。
下面我们看SAMPLEAPP_SEND_PERIODIC_MSG_EVT事件的处理函数SampleApp_SendPeriodicMessage()。
程序代码:
voidSampleApp_SendPeriodicMessage(void)
{
if(AF_DataRequest(&SampleApp_Periodic_DstAddr,&SampleApp_epDesc,
SAMPLEAPP_PERIODIC_CLUSTERID,
1,
(uint8*)&SampleAppPeriodicCounter,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS)==afStatus_SUCCESS)
{
}
else
{
//Erroroccurredinrequesttosend.
}
}
在SampleApp_SendPeriodicMessage()函数中调用了数据发送函数AF_DataRequest()函数发送数据。
参数中地址为SampleApp_Periodic_DstAddr在SampleApp_Init()被初始化。
其中的参数簇ID为SAMPLEAPP_PERIODIC_CLUSTERID,数据载体为SampleAppPeriodicCounter。
当接收端接收到该信息后会触发事件AF_INCOMING_MSG_CMD进行处理,根据簇ID接收端做出响应的处理。
事件AF_INCOMING_MSG_CMD事件的处理如下:
程序代码:
caseAF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB(MSGpkt);
break;
在处理AF_INCOMING_MSG_CMD事件时调用了事件处理函数SampleApp_MessageMSGCB()进行处理。
SampleApp_MessageMSGCB()函数如下:
程序代码:
voidSampleApp_MessageMSGCB(afIncomingMSGPacket_t*pkt)
{
switch(pkt->clusterId)
{
caseSAMPLEAPP_PERIODIC_CLUSTERID:
break;
……
}
}
在SampleApp_MessageMSGCB()函数中根据簇ID的不同进行处理。
但是SampleApplication对事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT的处理时什么都没有做,用户可以根据实际需要自行添加代码。
2、闪烁信息:
当按键SW1被按下时发送控制灯闪烁的广播信息,该广播信息只针对组1内的所有设备。
按键会触发事件KEY_CHANGE。
关于KEY_CHANGE事件会在按键机制详细讲解。
协议栈会调用SampleApp_HandleKeys()对事件KEY_CHANGE。
程序代码:
caseKEY_CHANGE:
SampleApp_HandleKeys(((keyChange_t*)MSGpkt)->state,
((keyChange_t*)MSGpkt)->keys);
break;
SampleApp_HandleKeys()处理函数如下:
voidSampleApp_HandleKeys(uint8shift,uint8keys)
{
if(keys&HAL_KEY_SW_1)//如果是SW1被按下
{
SampleApp_SendFlashMessage(SAMPLEAPP_FLASH_DURATION);
}
……
}
由上面的程序可以看出在处理按键SW1时调用了函数SampleApp_SendFlashMessage()。
函数详细代码如下:
程序代码:
voidSampleApp_SendFlashMessage(uint16flashTime)
{
uint8buffer[3];
buffer[0]=(uint8)(SampleAppFlashCounter++);
buffer[1]=LO_UINT16(flashTime);
buffer[2]=HI_UINT16(flashTime);
if(AF_DataRequest(&SampleApp_Flash_DstAddr,&SampleApp_epDesc,
SAMPLEAPP_FLASH_CLUSTERID,
3,
buffer,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS)==afStatus_SUCCESS)
{
}
else
{
//Erroroccurredinrequesttosend.
}
}
在SampleApp_SendFlashMessage()函数中调用了数据发送函数AF_DataRequest()函数发送数据。
参数中地址为SampleApp_Flash_DstAddr在SampleApp_Init()被初始化,该地址为组地址,AF_DataRequest()会将相关信息发送到属于该组的所有设备。
其中的参数簇ID为SAMPLEAPP_FLASH_CLUSTERID,数据载体为buffer,包括了发送信息次数和LED灯闪烁时间。
当接收端接收到该信息后会触发事件AF_INCOMING_MSG_CMD进行处理,根据簇ID接收端做出响应的处理。
事件AF_INCOMING_MSG_CMD事件的处理如下:
程序代码:
caseAF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB(MSGpkt);
break;
在处理AF_INCOMING_MSG_CMD事件时调用了事件处理函数SampleApp_MessageMSGCB()进行处理。
SampleApp_MessageMSGCB()函数如下:
程序代码:
voidSampleApp_MessageMSGCB(afIncomingMSGPacket_t*pkt)
{
uint16flashTime;
switch(pkt->clusterId)
{
……
caseSAMPLEAPP_FLASH_CLUSTERID:
flashTime=BUILD_UINT16(pkt->cmd.Data[1],pkt->cmd.Data[2]);
HalLedBlink(HAL_LED_4,4,50,(flashTime/4));
break;
}
}
在SampleApp_MessageMSGCB()函数中根据簇ID的不同进行处理。
SampleApplication对事件SAMPLEAPP_FLASH_CLUSTERID的处理时调用了灯闪烁函数
HalLedBlink()控制LED灯的闪烁,闪烁时间由发送端设定值决定。
3、组的加入与退出
组可以将设备按一定的逻辑加以区分。
向一个组发送一条信息则组内的所有设备都会收到这条信息。
设备可以利用函数aps_AddGroup()加入组,利用函数aps_RemoveGroup()退出组。
协议栈里组结构体定义:
typedefstruct
{
uint16ID;//组ID
uint8name[APS_GROUP_NAME_LEN];//组名
}aps_Group_t;
由以上结构体可以看出一个组由组ID和组名唯一确定。
在SampleApplication工程中通过SW2按键来加入或者退出组1。
代码如下:
按键首先触发事件KEY_CHANGE
程序代码:
caseKEY_CHANGE:
SampleApp_HandleKeys(((keyChange_t*)MSGpkt)->state,
((keyChange_t*)MSGpkt)->keys);
break;
SampleApp_HandleKeys()处理函数如下:
程序代码:
voidSampleApp_HandleKeys(uint8shift,uint8keys)
{
……
if(keys&HAL_KEY_SW_2)
{
aps_Group_t*grp;
grp=aps_FindGroup(SAMPLEAPP_ENDPOINT,SAMPLEAPP_FLASH_GROUP);
if(grp)
{
aps_RemoveGroup(SAMPLEAPP_ENDPOINT,SAMPLEAPP_FLASH_GROUP);
}
else
{
aps_AddGroup(SAMPLEAPP_ENDPOINT,&SampleApp_Group);
}
}
通过函数aps_FindGroup()查找SAMPLEAPP_ENDPOINT是否加入了组1,如果加入组1则突出组1,否则