Page 1 of 3
首页 | 收藏 | About Us
HomeNewsTechnologyDownloadProductContact
站点首页技术专题STM32(Cortex-M3) ZigBee无线网络技术 无线传感器网络-WSN 推荐产品 XBeePRO模块 XBee模块 ZigBee模块(ZIC2410) STM32(Cortex-M3) XSBase-WSNSEARCH新闻资讯技术专题资料下载推荐产品联系我们
首页 / 技术专题
从零开始学习ZStack之4
作者:YYYtech 发布时间:2008-07-07 23:37:06 已阅读1587次
从零开始学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()中有一个事件处理循环,循环检测是哪个事件发生。
Search在线交流Skype通话QQ在线http://www.yyytech.cn/tech/Read.aspx?id=2562009-9-22从零开始学习ZStack之4-3Y科技
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:
Page 2 of 3
http://www.yyytech.cn/tech/Read.aspx?id=2562009-9-22从零开始学习ZStack之4-3Y科技
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; } } 这里判断了两种信息: 周期信息 闪灯信息 不同的信息就相当于收到了不同的命令,然后根据不同的命令做出了不同的处理。是个会写程序都明白!!!! 到这里,我就基本上把这个应用文档看完了,至于理解了多少我迷糊,理解正确了多少我更加迷糊,反正我按照我自己的方式理解了! yyytech科技 2008年7月7日23:35于成都 Page 3 of 3
Copyright © 2008 3Y科技版权所有 管理员
http://www.yyytech.cn/tech/Read.aspx?id=2562009-9-22
因篇幅问题不能全部显示,请点此查看更多更全内容