Zstack之例程GenericApp分析笔记1
来源:互联网 发布:网易博客软件 编辑:程序博客网 时间:2024/06/14 18:04
这两天我左看看TI的官方文档,右看看网上的各种Zigbee教程,再看下TI的Zstack文件夹中提供的几个例程,头都大了都没太明白例程代码的作用和意义。后来在网上一个教程中有一句话算是把我给点明白了。“我这个人就是喜欢把代码分析个透彻”,一般觉得这句话也是不错的,至少说明爱钻研,有那股劲儿。但是仔细一想,我个人觉得这种思想还是不太适合我。
TI的Zstack文件夹的Sample子文件夹中有TI提供的三个例程,最近主要琢磨第一个例程:GenericApp。这个例程是用于介绍OSAL任务初始化流程和消息处理流程的,是最基本的应用任务范例。首先,芯片上电首先从Zmain.c中的main.c函数中执行的,所有的例程的Zmain.c都是共用的,不管打开哪个例程都是这个Zmain.c文件,因为一般App部分需要修改的文件时app_name.h,app_name.c和OSAL_app_name.c三个文件。main函数一进行执行就首先开始不断初始化各种外设和数据结构,这些都不用我们操心,我们只需要关心任务初始化中的应用任务初始化,这也是我们自己使用Zstack需要自己码代码的部分。对于前面的各种初始化函数,和后面的开始运行系统函数网上都有一大堆的教程和资料,就不再赘述了。
TI的官方文档:Z-Stack Sample Applications:SWRA201中对三个例程的详细介绍,而其中又专门对GenericApp初始化过程进行了详细的介绍。
任务初始化流程代码如图1所示:
图1
在任务初始化里面,前面先对各个任务进行了初始化,包括初始化mac,网络层,hal硬件抽象层之类的,注意初始化的时候除了初始化各抽象层对应的数据和模块之外,还有一个非常重要的作用,为各个层对应的任务分配一个task_ID。这个ID是非常重要的,每个ID对应一个不同的任务,用于调用或获取系统资源和信息的,比如调用用定时器,获取消息等。学过操作系统的估计一看就明白了,这是个类似于PID(process id)的东西,这也是每个OSAL任务与其他任务的区别所在。同时要注意到最优先的任务号为0,且用户任务的优先级是最低的了,一般情况这个优先级顺序是不能变动的。下面来看void GenericApp_Init( byte task_id )这个函数是如何进行的。如图2所示:
图2
注意该函数有一个传入参数task_id,这就是应用任务的任务ID了。在该函数里将以之用于设置任务对应的数据,并对需要使用的资源进行注册。
GenericApp_TaskID = task_id; //将task_id传入参数赋值给本地变量,并在之后被使用,在整个OSAL后续的运行中,这个值应该是不能被改变的,因为在后续的事件处理中就只能通过这个变量才能指定该任务了。
GenericApp_NwkState = DEV_INIT;//指定该设备的联网状态,通过查找该变量的定义,可以发现该变量被定义如下:
devStates_t GenericApp_NwkState;
而devStates_t又是一个什么数据类型呢,继续深入查找,发现其是一个枚举类型:
typedef enum
{
DEV_HOLD, // Initialized - not started automatically
DEV_INIT, // Initialized - not connected to anything
DEV_NWK_DISC, // Discovering PAN's to join
DEV_NWK_JOINING, // Joining a PAN
DEV_NWK_REJOIN, // ReJoining a PAN, only for end devices
DEV_END_DEVICE_UNAUTH, // Joined but not yet authenticated by trust center
DEV_END_DEVICE, // Started as device after authentication
DEV_ROUTER, // Device joined, authenticated and is a router
DEV_COORD_STARTING, // Started as Zigbee Coordinator
DEV_ZB_COORD, // Started as Zigbee Coordinator
DEV_NWK_ORPHAN // Device has lost information about its parent..
} devStates_t;
所以GenericApp_NwkState = DEV_INIT;这句即是初始化设备联网状态,可以看出DEV_INIT意味着不进行任何连接。作为该例程,也确实不在于联网介绍上。根据TI官方手册上对此的介绍,DEV_INIT即device not connected是设备上电的默认状态,在该状态下是不会接收到ZDO_STATE_CHANGE这个消息,这个消息根据后面的介绍是用于指示联网状态改变用的,所以系统上电一定需要直接或间接地对其联网状态进行设置。至于其他的联网状态设置,在后面的例程分析中逐渐分析。不过可以从注释看出这些devState的意义。
接下来的三句代码:
GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
GenericApp_DstAddr.endPoint = 0;
GenericApp_DstAddr.addr.shortAddr = 0;
通过查看该变量的定义发现:afAddrType_t GenericApp_DstAddr;
那afAddrType_t又是一个什么数据类型呢?
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
afAddrMode_t addrMode;
byte endPoint;
uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
这个结构变量的定义大家没晕吧,很简单的一个struct结构,只有4个成员,只是其中的一个成员是联合体,用于指示设备短地址或者扩展地址的。
第一句: GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
这里面的afAddrMode_t通过定义查找发现其也是一个枚举类型,如下所示
typedef enum
{
afAddrNotPresent = AddrNotPresent,//绑定后的点播
afAddr16Bit = Addr16Bit,//短地址点播
afAddr64Bit = Addr64Bit,//长地址点播
afAddrGroup = AddrGroup,//组播
afAddrBroadcast = AddrBroadcast//全播
} afAddrMode_t;
用于指定用于发送信息的方式。
后面两句则是指定目标设备端点号为0,短地址为0。这儿关于endpoint端点号的分配还有待进一步查看TI的说明。
接下来的几句是:
GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;
GenericApp_epDesc.task_id = &GenericApp_TaskID;
GenericApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;
GenericApp_epDesc.latencyReq = noLatencyReqs;
GenericApp_epDesc是个什么类型的变量呢?通过查找定义发现:
endPointDesc_t GenericApp_epDesc;好吧,继续查找endPointDesc_t是什么变量类型,如下所示:
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
里面包含4个成员变量,其中第一个又是endpoint。这里我们首先要能够理解一点,一个zigbee设备,上电之后,不管是作为router,还是enddevice,总得有一个方法来描述自己是个什么东西吧?就像每个人都有档案一样,通过查询一个人的档案就能够知道这个人的具体情况。同样的,该结构体就是zstack中用于设备描述的数据结构,其中endpoint是指定自身的端点号,取值范围根据官方手册貌似是0-240?第二个是指向OSAL应用任务的id变量指针,第三个成员变量又是一个结构体变量SimpleDescriptionFormat_t,根据TI的文档介绍,该变量是用于该zigbee设备的功能描述的,该变量的具体结构如下所示:
typedef struct
{
byte EndPoint;
uint16 AppProfId;
uint16 AppDeviceId;
byte AppDevVer:4;
byte Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
byte AppNumInClusters;
cId_t *pAppInClusterList;
byte AppNumOutClusters;
cId_t *pAppOutClusterList;
} SimpleDescriptionFormat_t;
这个结构体里面的成员又引申出了两个新概念,profile和cluster,两个让人头大的概念,目前还不是搞得很清楚,不过大体上profile是指一种应用配置,比如智能家居HA对应一个profileID,如果该设备是作为智能家居终端的话,那么AppProfId就应该是0x104(这个是zigbee联盟规定好了的),至于cluster,则类似于设备属性之类的概念。
在该例程中,该设备功能描述结构体是通过一个预定义好的数据结构进行加载的:
GenericApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;
通过定义查找GenericApp_SimpleDesc,可以发现如下:
const SimpleDescriptionFormat_t GenericApp_SimpleDesc =
{
GENERICAPP_ENDPOINT, // int Endpoint;
GENERICAPP_PROFID, // uint16 AppProfId[2];
GENERICAPP_DEVICEID, // uint16 AppDeviceId[2];
GENERICAPP_DEVICE_VERSION, // int AppDevVer:4;
GENERICAPP_FLAGS, // int AppFlags:4;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList, // byte *pAppInClusterList;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList // byte *pAppInClusterList;
};
此为一个const即静态变量,且该结构体中的各个成员变量都已经定义好了,而该例程代码就由此完成了对simpleDesc这个成员变量的赋值。
另外要提一点的是,设备描述这个结构体endPointDesc_t,既可以上电后进行自定义初始化该结构体,也可作为一个静态结构体在代码中固定好。这两种方式各有各的好处,在此只是略提一下。
最后一个成员变量afNetworkLatencyReq_t latencyReq;一般都定义为noLatencyReqs;,至于原因,仍待求解中。
好了,完成了参数设置之后,接下来进入一个很有操作系统特色的地方:注册
注册组件代码如下:
// Register the endpoint description with the AF
afRegister( &GenericApp_epDesc );//
该行代码向AF层注册该终端描述,这样一是能向周围的zigbee设备描述本设备特征,二是应用任务才能够进行数据处理和端点管理。这也是AF层的作用范围所在。
// Register for all key events - This app will handle all key events
RegisterForKeys( GenericApp_TaskID );
同样的,这儿注册对按键的使用,只有这样注册之后,当有按键事件发生(按下或松开),才会通知这个task_id对应的任务处理程序task_event_process进行处理。
这里再详细说下 按键注册函数: RegisterForKeys( GenericApp_TaskID ),其具体代码如下:
uint8 RegisterForKeys( uint8 task_id )
{
// Allow only the first task
if ( registeredKeysTaskID == NO_TASK_ID )
{
registeredKeysTaskID = task_id;
return ( true );
}
else
return ( false );
}
在这个函数中,可以看出对按键的注册其实是对registeredKeysTaskID这个全局变量进行注册,当HAL层检测到按键事件时,将会发送消息CHANGE_KEY(前面分析过的SYS_EVENT_MSG子事件之一)到registeredKeysTaskID这个变量所指向的task_id中去。这个我们就不更加详细地去理解了,至少现在不用。值得注意是一点,按键注册是对一个变量进行赋值的,那么很显然同一时间只能对一个task_id对应的任务发送该消息,也就是说OSAL中的注册是只允许一个的。
操作系统中,注册这个概念的作用本质主要在于通知和调用,对于Zstack来说,你这个task登记了你需要关于按键的事件通知,那么有按键的事件我才知道我需要通知你。
任务初始化函数的最后两句代码是:
ZDO_RegisterForZDOMsg( GenericApp_TaskID, End_Device_Bind_rsp );
ZDO_RegisterForZDOMsg( GenericApp_TaskID, Match_Desc_rsp );
要了解这两句代码起什么作用,有什么样的意义,我们还是需要首先来了解下
ZDO_RegisterForZDOMsg这个函数,如图3所示为该函数在ZstackAPI手册上的说明:
图3
首先,该函数属于一个callback,即回调函数。该类函数在Zstack中的意义以及运作方式暂时还是未知,但是根据手册的说明,ZDO message callbacks这类函数的作用是为一个应用任务注册,然后可以接收到指定的zigbee设备之间的传播信息。
函数原型为:ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID );
其中taskID--the application’s task ID. This will be used to send the OSAL message. 即设定需要接收该信息的应用任务ID
clusterID – the over the air message’s clusterID that you would like to receive (example: NWK_addr_rsp). These are defined in ZDProfile.h. clusteer这个概念是Zstack中特有的,这个概念类似于一个对象的一个类。这儿引申出来就太多了,待后续进一步深入分析。
在这儿,该例程函数所指定的End_Device_Bind_rsp和Match_Desc_rsp是ZDO层设备绑定命令中的两个,料想应该是用于指定接收终端绑定响应和地址匹配响应的。
所以由上的分析可以知道,这儿使用ZDO_RegisterForZDOmsg函数可以为Generic_App这个应用进程进行注册需要接收的指定cluster(簇)的Zigbee设备之间的传输信息(OTA信息,over the air)。
在手册上,关于使用该函数注册后的效果还有这样的说明:在进使用了该函数进行注册之后,如果zigbee设备有接收到指定的cluster消息,OSAL会向该应用进程添加一个系统消息:ZDO_CB_MSG,通过该消息,在后续的Generic_Process_event中就可以根据消息种类进行对应的处理了。
这里再对上面的分析进行一个大体的回顾:应用任务初始化,首先进行任务ID,网络状态等本地变量初始化,以便之后使用,然后设置信息要传输的目的地址,对设备自身的描述结构变量进行设置,然后进行各种注册,注册AF,注册按键,注册指定消息接收。这就是Generic例程的任务初始化函数流程,同时也是TI建议的参考书写方式。
- Zstack之例程GenericApp分析笔记1
- Zstack之例程GenericApp分析笔记2
- zigbee学习笔记---通信例程之GenericApp
- 通信例程之GenericApp
- zigbee学习笔记3-通信例程之GenericApp
- Zstack之SerialApp分析笔记1
- Zstack之HAL层增加新的按键key的分析笔记1
- Zstack杂乱笔记1
- zstack SimpleApp和GenericApp实例绑定程序流程
- Zstack之HAL层增加新的按键key的分析笔记2
- Zstack之HAL层增加新的按键key的分析笔记3
- TI协议栈例程GenericApp SampleApp SimpleAp 区别
- TI协议栈例程GenericApp SampleApp SimpleAp 区别
- zstack学习笔记2--点对点通信1
- ZStack消息流分析
- Zstack 笔记(一)
- Zstack杂乱笔记2
- Zstack杂乱笔记3
- 解决JDBC中文乱码问题
- 3.跟我学solr---使用solrj添加索引
- Eclipse中修改SVN用户名和密码方法
- 回车和换行
- res资源获取
- Zstack之例程GenericApp分析笔记1
- 为Tomcat添加启动内存
- Source Insight使用总结
- git基础学习(一):创建仓库,add和commit
- IOS——随机数
- 直接插入排序
- Windows CE SDHC驱动简析(1)-驱动架构(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- 数据库知识点总结
- window.location.hash属性介绍