Zigbee之SampleApp学习笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
SampleApp学习笔记
1. 初始化
系统上电是将会调用SampleApp_Init()函数。
SampleApp_TaskID = task_id; //指定ID号
注意OSAL通过函数参数为SampleApp分配任务ID。SampleApp必须通过他的这个
任务ID为它自己设定一个定时器,设置一个事件,发送一个OSAL消息。完成以上提
及的操作是为了将大的处理过程分解为小的处理过程,在OSAL定义的时间片里执行
而不是花费太多时间来做一件事情。当一个任务被分成了许多小块然后在实际那片
里执行的时候,一个任务必定会影响到OSAL层的任务设计。
SampleApp_NwkState = DEV_INIT; //指定网络状态
保存网络状态标志是很有用的。网络在刚上电时的状态为“未连接”或者
DEV_INIT。当网络子啊在上电或上电后处于这样一个默认的状态时,OSAL层的任务
将不会获取ZDO_STATE_CHANGE消息,所以网络状态必须在本地(手动)进行初始化。
一旦获得一个新的网络状态,任务捡回获取ZDO_STATE_CHANGE消息。注意,如果设
备是非易失性存储器,在上电时连接到网络,ZDO_STATE_CHANGE消息会在上电后立
刻接收到,但不会通过OTA得到,因为"network connected"状态已经储存在非易失
性存储器中了。
SampleApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
SampleApp_DstAddr.endPoint = 0;
SampleApp_DstAddr.addr.shortAddr = 0;
默认的目的地址已经被初始化,以便于消息能过以受约束的消息来发送;使用
AddrNotPresent将会强制查找目的地址的绑定表。如果不存在匹配的绑定,消息将
会被丢弃。
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_epDesc.task_id = &SampleApp_TaskID;
SampleApp_epDesc.simpleDesc =
SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
SampleApp_tencyReq = noLatencyReqs;
// Register the endpoint description with the AF
//使用AF注册终端节点描述
afRegister( &SampleApp_epDesc );
SampleApp应用对象被以上代码实例化。这允许AF层知道如何为
SAMPLEAPP_PROFID / SAMPLEAPP_ENDPOINT发送收到的数据包,它是通过发送一个
OSAL SYS_EVENT_MSG-message (如AF_INCOMING_MSG_CMD)消息给SampleApp_TaskID
来实现的。
RegisterForKeys( SampleApp_TaskID );
SampleApp为按键按下通知注册专门的系统服务。
SampleApp_Group.ID = 0x0001;
osal_memcpy( SampleApp_, “Group1” );
aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );
闪存控制消息发送个组1,同时为组1接收消息,这个设备将会为组1自动启动。
按下SW1,将会触发加入到组1。
2. 事件处理
每当SampleApp_TaskID的OSAL事件发生时,SampleApp的处理函数,
SampleApp_ProcessEvent()都会在OSAL的任务处理循环中被调用。
SampleApp_ProcessEvent()函数的参数是一个16
位的掩码(事件标志);一个或多个
位都可以在任何调用该函数的时候被设置。如果不止一个时间标志位被设置了,强
烈建议一个任务最好只响应一个事件标志位(可能是时序要求最严格的一个,或者
一直这样,设置SYS_EVENT_MSG优先级为最高。)
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive(SampleApp_TaskID);
while ( MSGpkt )
{
…
注意,尽管建议一个任务在一次调用消息处理函数中只响应一个事件,同样也
建议(在例子程序中执行的)在OSLA的一个时间片中处理所有可能的处于等待状态
的SYS_EVENT_MSG消息。
switch ( MSGpkt->hdr.event )
上边展示了如何如何去查看SYS_EVENT_MSG消息的类型。推荐一个任务只实现
一个所有可能的SYS_EVENT_MSG类型的消息的一个子集。这个最小子集在SampleApp
中的实现描述如下:
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
((keyChange_t *)MSGpkt)->keys );
break;
如果一个OSAL任务注册了按键通知,则所有的按键按下的消息都将会收到一个
KEY_CHANGE的系统消息。程序流有两种可能的途径导致一个任务接收到KEY_CHANGE
消息。
程序流由物理按键按下导致的结果如下:
-HAL检测按键的按下状态(通过H/W中断或H/W查询模式)
-HAL OSAL任务检测按键状态变化并调用OSAL按键变化回调函数
-OSAL按键变化回调函数发送一个OSAL系统事件消息(KEY_CHANGE)给注册了按
键改变事件通知的任务ID(RegisterForKeys().)
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()函数并返回ZSuccess将导致一个通过
AF_DATA_CONFIRM_CMD系统事件消息的回调。
发送事件Id(sentTransID)是一种确认消息的方法。尽管SampleApp
仅仅只使用了一个Transaction Id(SampleApp_TransID),但是它很有用,他可以
保持一个单独的终端节点Transaction Id,甚至在是网络簇ID中来确认终端节点的
消息,重试,卸载或重组等。注意,任何事件ID状态变量会增加在AF_DataRequst()
上成功(因此它是通过参数来传递引用,而不是至传递)。
AF_DataRequst()的返回值为ZSuccess表示消息已经被网络层接受了,消息将会
被传送到MAC层,在传送到OTA。发送状态(sentStatus)就是OTA消息的结果。ZSuccess
表示消息已经被递交给网络中下一跳地址的Zigbee设备。如果AF_DataRequst()函
数
是被AF_ACK_REQUEST标志所调用,然后ZSuccess代表消息已经递交给了目的地址。除
非地址模式是间接的(例如消息是通过其他路径到达目的地址的),这种情况下ZSuccess
就代表消息已经被递交给了网络reflactor(反射器),一下是几种可能的表示错误的
发送状态:
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的消息通知。
注意SampleApp实现了在设备一成功加入网络就启动一个运行中的定时器。当网络状
态变为"joined",它可以开始自动查询并绑定设备而不需要用户的干预。
// Release the memory 释放内存
osal_msg_deallocate( (uint8 *)MSGpkt );
注意OSAL的系统消息设计需要接收到的任务回收动态为消息分配的内存。如果
OSAL本能将一个消息放入消息队列(任务ID不存在或者消息头不正确),那么就会回
收该消息占用的内存。
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在SampleApp.h文件中定义了一个15位的可用的任务事件掩码:
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001
SampleApp当要为它的SAMPLEAPP_SEND_MSG_EVT事件设定一个定时器时用它自
己的任务ID隐含的调用了osal_start_timer()函数。当收到成功计入网络的通知后
(ZDO_STATE_CHANGE)定时器会自动启动。如上所述,定时器在每一个定时结束期都
会重启。每一个SAMPLEAPP_SEND_PERIODIC_MSG_EVT消息的间隔中,在
SampleApp_SendPeriodicMessage()函数中都会有一个数据请求。
3. 消息流
通过使用OSAL定时器,SampleApp周期性的发送消息到OTA。
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)sampleAppPeriodicCount
er++,
(uint8 *)&sampleAppPeriodCounter,
&SampleApp_TransID, AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
// Successfully requested to be sent.
}
else
{
// Error occurred in request to send.
}
}
应用对象的OTA数据是一个简单的计数器,当接收到该数据就会显示到接收端的
LCD上。
AF_DataRequest()的调用启动了将用户消息传递给协议栈的下层的线程,以便于
准备到OTA的消息。如果该函数返回ZSuccess,一个完整的消息已经被组装完成,整
个消息包包含消息头、消息尾,以及可选的加密应用,而且这个组装好的消息被放入
到缓冲区的消息队列中等待被发送出去。网络层不会试图传递这个新消息到MAC层,
直到网络任务在OSAL任务处理循环中再一次被调用运行。甚至当网络任务再一次运行,
新的OTA消息也会等待直到消息队列中以前的消息全部发送到MAC以及OTA。
AF_DataRequest()返回的所有的失败的消息表示在协议栈的某一层失败了,而且几乎
总是由于缺少足够的空间来将另外一个消息加入到队列中;因此,这个消息是没有机
会被发送出去的。
当网络层成功将消息发送到MAC层,而且MAC层成功将消息发送到OTA,消息按照指
定路径发送,一级又一级,到达指定的目的地址调用AF_DataRequest()函数。当消息
最终到达目标网络地址,较低的层会首先对数据解密,然后就将应用对象的的有效数
据发送到指定目的地址的目标终端节点作为第一个参数来调用AF_DataRequest()。接
收的应用对象将会发出AF_INCOMING_MSG_CMD消息来进行SYS_EVENT_MSG消息的通知。
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
如上所述,SampleApp通过SampleApp_ProcessEvent()接收SYS_EVENT_MSG消息,
向下,处理用户数据并发送到OTA。
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;
}
}
传送的OTA数据的内容是没有用的。SampleApp中假定OTA数据包含一个计数器和一
个闪存超时(闪存控制消息)