从零开始学Z-Stack之4
-----------------Sample Application分析(下)
上节介绍了建立一个应用需要做的几个必须的事情,现在就来通过分析Sample Application来具体看看需要做哪些事情,才能建立一个ZIGBEE应用功能。当然这里只是做点简单的必须的工作。
The Sample Application (SampleApp)
1、 介绍
主要是介绍一个应用建立的结构及需要进行的程序流程。
1.1、程序流程
1.1.1、初始化
首先需要调用初始化函数SampleApp_Init()。
SampleApp_TaskID = task_id;
初始化应用建立的任务ID号,其实用过OS的人都应该晓得这个是干啥的,我没用过,不是很理解,但是我知道是必须的,就相当于一个任务的标识,这样才能区分运行过程中不同任务中的不同事件。我是这么认为的,ID说白了就是给该任务取了各名字,就向人名字一样,区分不同的人,就是一个代号。人名可以重复,重复了有时候叫起来就容易混淆;所以才程序中为了避免这种混淆,就强制性的规定任务ID不能重复。要是哪天国家或者联合国姓名管理委员会规定,人民不能重复,那么这个人名就需要全球统一管理了。那给娃取个名字就要向联合国姓名管理委员会申请了。呵呵!?
SampleApp_NwkState = DEV_INIT;
初始化应用设备的网络状态。怎么说呢,据说是设备类型的改变都要产生一个事件,叫ZDO_STATE_CHANGE,从字面理解为ZDO状态发生了改变。所以在设备初始化的时候一定要把它初始化为什么状态都没有。那么它就要去检测整个环境,看是否能重新建立或者加入存在的网络。但是有一种情况例外,就是当NV_RESTORE被设置的时候(NV_RESTORE是把信息保存在非易失存储器中),那么当设备断电或者某种意外重启时,由于网络状态存储在非易失存储器中,那么此时就只需要恢复其网络状态,而不需要重新建立或者加入网络了。我也是从文档中这么理解的,至于为什么只有有待进一步考证。
SampleApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
SampleApp_DstAddr.endPoint = 0;
SampleApp_DstAddr.addr.shortAddr = 0;
看见这几句话从字面理解为:初始化不标设备地址模式及目标设备EP号和网络地址。从代码可以看出,这些地址或EP均为0。也就是说目标设备为协调者的ZDO,这个意义就很明显了,就是设备建立后可以直接与协调器的ZDO交互信息。
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
----- SampleApp EP描述符的EP号
SampleApp_epDesc.task_id = &SampleApp_TaskID;------ SampleApp EP描述符的任务ID
SampleApp_epDesc.simpleDesc =------------------ SampleApp EP简单描述符
SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
SampleApp_epDesc.latencyReq = noLatencyReqs;
//在AF层中登记注册改应用EP
afRegister( &SampleApp_epDesc );
这里其实是对SampleApp的EP描述符进行初始化。
本人理解:要对改应用进行初始化并在AF进行登记,告诉应用层有这么一个EP已经可以使用,那么下层要是有关于改应用的信息或者应用要对下层做哪些操作,就自动得到下层的配合,至于这个配合是怎么回事,那么就需要好好研究下层的协议了。当然在这里肯定是没那时间精力和能力研究了!?
其实在这个应用中,只是让AF配合SAMPLEAPP_PROFID / SAMPLEAPP_ENDPOINT这两个应用。那么通过什么呢,通过发送OSAL SYS_EVENT_MSG消息中的(AF_INCOMING_MSG_CMD)事件到SampleApp任务ID。
RegisterForKeys( SampleApp_TaskID );
登记按键事件到SampleApp_TaskID,在前面已经说了按键这个是唯一的,也就是所有任务中有且只有各任务能登记键盘事件。前面还说了还有一个也是唯一,你猜是什么??
SampleApp_Group.ID = 0x0001;
osal_memcpy( SampleApp_Group.name, “Group1” );
aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );
闪灯信息被发送到组1,同样也只有在组1的设备才能接收这个信息。设备启动时已经被设定为组1设备了,但是可以通过按SW1推出/加入组1。这里提到了组的概念,我反正暂时不是很清楚这个是什么东西,在程序中怎么实现也很模糊,但是应用中的好处还是不难想象的,不外呼是就是想控制谁可以事先规定好,还可以动态更改。
1.2、事件处理
玩过OS的人都知道,OS中最重要的概念不外呼就是任务啦,消息啦,事件啦等。从我们自己平时的工作中也不难想象,如果老板安排了某项工作,那么我们就需要做的,这个工作可能是预先计划好的,也有可能是临时的,那么这些预先定好或者临时的工作可以称之为事件。而老板让您做的方式,比如通过文件下达,或者叫:某某你把XXX做下。那么让老板下达的文件内容或者说的内容我这里可以称之为消息。老板给了你不同的消息那么就需要干不同的事件,至于任务可以理解为公司的不同的员工,呵呵!我简直是理解的天才,这样举例居然也能忽悠通过!!!o(∩_∩)o…哈哈
言归正传
在Z-Stack中,每个应用任务都通过SampleApp_ProcessEvent()函数来处理任务中的事件。一旦SampleApp_TaskID任务的某个OSAL事件发生,那么就可以通过调用SampleApp_ProcessEvent()函数来处理。在SampleApp_ProcessEvent()中有一个事件处理循环,循环检测是哪个事件发生。
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
…………………………………..
可以看到是通过检测SYS_EVENT_MSG是否有事件信息发生。
switch ( MSGpkt->hdr.event )
这里是判断SYS_EVENT_MSG事件类型,不同的SYS_EVENT_MSG类型需要不同的处理。
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
((keyChange_t *)MSGpkt)->keys );
break;
比如这里判断是否是键盘事件,如果键盘事件就调用键盘处理函数。
如果一个OSAL任务已经被登记组侧,那么任何键盘事件都将接受一个KEY_CHANGE事件信息。可能有如下几种方式得到键盘事件信息
1)、HAL检测到键盘按下(中断或者查询检测)
2)、HAL的OSAL任务检测到一个键盘状态改变调用回叫函数产生
3)、OSAL键盘改变回叫函数发送一个OSAL系统事件信息(KEY_CHANGE)。
case AF_DATA_CONFIRM_CMD:
// The status is of ZStatus_t type [defined in ZComDef.h]
// The message fields are defined in AF.h
afDataConfirm = (afDataConfirm_t *)MSGpkt;
sentEP = afDataConfirm->endpoint;
sentStatus = afDataConfirm->hdr.status;
sentTransID = afDataConfirm->transID;
任何AF_DataRequest()数据请求函数调用后,都通过AF_DATA_CONFIRM_CMD系统事件信息回叫返回成功Zsuccess。
case ZDO_STATE_CHANGE:
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (SampleApp_NwkState == DEV_ZB_COORD)
||(SampleApp_NwkState == DEV_ROUTER)
||(SampleApp_NwkState == DEV_END_DEVICE) )
{
// Update the LCD’s network indicator
// Start sending "the" message in a regular interval.
osal_start_timer( SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
break;
这里就是前面介绍的设备状态改变事件处理了。
只要网络状态发生改变,那么通过ZDO_STATE_CHANGE事件通知所有的任务。注意:在这个例子中,一旦设备成功加入网络,是通过定时运行的方式运行的。一旦网络状态为加入”JOINED”,那么它可能不需要任何的认为操作就能绑定其他设备,因为设置为自动发现并绑定的。
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
释放存储空间。
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )
{
// Send "the" message
SampleApp_SendPeriodicMessage();
// Setup to send message again
osal_start_timer( SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_MSG_TIMEOUT );
// return unprocessed events
return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
这里检测事件是否为周期发送信息事件。
在SampleApp.h中定义了:
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001
在这个应用中,调用了osal_start_timer()函数来定时产生发送周期信息事件。而定时器的运行是设备一旦加入网络就不停的在运行。从上面可以看到,用函数SampleApp_SendPeriodicMessage()发送周期信息,而用函数osal_start_timer( SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_MSG_TIMEOUT )来继续运行定时器定时发送这个周期信息。关于这个osal_start_timer可以多了解下,第一个参数SAMPLEAPP_SEND_PERIODIC_MSG_EVT四信息时间,也就是事件到了产生一个什么事件。第二各参数SAMPLEAPP_SEND_MSG_TIMEOUT是需要定时的时间,这里就是发送周期信息的时间周期。
1.3、消息流程
通过OSAL定时器,这个应用定时发送一个周期信息:
void SampleApp_SendPeriodicMessage( void )
{
afAddrType_t dstAddr;
dstAddr. addrMode = afAddrBroadcast;
dstAddr.addr.shortAddr = 0xFFFF; // 广播发送
dstAddr. endpoint = SAMPLEAPP_ENDPOINT;
if ( AF_DataRequest( & dstAddr, &SampleApp_epDesc,
SAMPLEAPP_PERIODIC_CLUSTERID,
(uint8)sampleAppPeriodicCounter++,
(uint8 *)&sampleAppPeriodCounter,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
// Successfully requested to be sent.----发送成功处理
}
else
{
// Error occurred in request to send.---发送失败处理
}
}
在这里调用了AF_DataRequest()函数用来发送数据。关于发送数据的具体过程这里就不做深入研究,不外乎就是把数据从应用层传到网络层,在传到MAC,在传到无力层,最后通过OTA发送出去。接收数据就是相反的过程了,那么接收之后,在应用层有什么反应呢,最直观的反应就是会发送一个AF_INCOMING_MSG_CMD消息事件。
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
这里表示收到某个信息,然后在里面调用了收到信息的信息处理函数SampleApp_MessageMSGCB( MSGpkt )。
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
case SAMPLEAPP_PERIODIC_CLUSTERID:
// Display and increment a counter on the LCD in the periodic space
break;
case SAMPLEAPP_FLASH_CLUSTERID:
flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
break;
}
}
这里判断了两种信息:
周期信息
闪灯信息
不同的信息就相当于收到了不同的命令,然后根据不同的命令做出了不同的处理。是个会写程序都明白!!!!
到这里,我就基本上把这个应用文档看完了,至于理解了多少我迷糊,理解正确了多少我更加迷糊,反正我按照我自己的方式理解了!
文章评论(0条评论)
登录后参与讨论