协议栈的使用
1. 为了让ZigBee的开发更加简单高效,TI公司推出了基于CC2530芯片的协议栈-Z-Stack。协议栈实质上就是能实现各个功能的示例框架代码,我们要实现自己想要的功能,只需要在协议栈的基础上修改或添加即可。
2. Z-Stack的发展 由0.0.1 -2.5.1a
Mesh:之前版本的升级 HA:智能家居 SE:智能能源
他们的区别是应用部分不同
工程打开路径\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Projects\zstack\Samples\GenericApp\CC2530DB\GenericApp.eww,既可以打开工程文件。
选择不同的选项卡能够生成不同的模块程序
CoordinatorEB 协调器
RouterEB 路由器
EndDeviceEB 终端
注:有Pro的版本比没有Pro的版本功能强大一点,所以我们一般使用额是有Pro的版本。
ZIGBEE简介
ZigBee协议栈建立在IEEE802.154的PHY层和MAC子层规范之上。它实现了网络层(networklayer,NWK)和应用层(applicationlayer,APL)。在应用层内提供了应用支持子层(applicationsupportsub—layer,APS)和ZigBee设备对象(ZigBeeDeviceObject,ZDO)。应用框架中则加入了用户自定义的应用对象
ZigBee的体系结构由称为层的各模块组成。每一层为其上层提供特定的服务:即由数据服务实体提供数据传输服务;管理实体提供所有的其他管理服务。每个服务实体通过相应的服务接入点(SAP)为其上层提供一个接口,每个服务接入点通过服务原语来完成所对应的功能。
ZigBee协议栈的核心部分在网络层。网络层主要实现节点加入或离开网络、接收或抛弃其他节点、路由查找及传送数据等功能。
应用层(APL)ZigBee应用层框架包括应用支持层(APS)、ZigBee设备对象(ZDO)和制造商所
应用支持层的功能包括:维持绑定表、在绑定的设备之间传送消息。ZigBee设备对象的功能包括:定义设备在网络中的角色(如ZigBee协调器和终端设备),发起和响应绑定请求,在网络设备之间建立安全机制。ZigBee设备对象还负责发现网络中的设备,并且决定向他们提供何种应用服务。ZigBee应用层除了提供一些必要函数以及为网络层提供合适的服务接口外,一个重要的功能是应用者可在这层定义自己的应用对象,开发人员一般进行开发也只是在应用层的基础上面进行开发,因为我们不能去动zigbee协议栈里面的内容。
设备类型
在ZigBee网络中存在三种逻辑设备类型:Coordinator(协调器),Router(路由器)和End-Device(终端设备)。ZigBee网络由一个Coordinator以及多个Router和多个End_Device组成.协调器负责启动整个网络。它也是网络的第一个设备。协调器选择一个信道和一个网络ID(也称之为PANID,即PersonalAreaNetworkID(个域网ID)),随后启动整个网络。协调器也可以用来协助建立网络中安全层和应用层的绑定(bindings)。协调器的角色主要涉及网络的启动和配臵。一旦这些都完成后,协调器的工作就像一个路由器(或者消失goaway)。由于ZigBee网络本身的分布特性,因此接下来整个网络的操作就不在依赖协调器是否存在,因为ZigBee是蜂窝式分布式网络。
路由器的功能主要是:允许其他设备加入网络,多跳路由和协助它自己的由电池供电的终端设备的通讯。 通常,路由器希望是一直处于活动状态,因此它必须使用主电源供电。但是当使用树状网 络拓扑结构时,允许路由间隔一定的周期操作一次,这样就可以使用电池给其供电。
终端设备没有特定的维持网络结构的责任,它可以睡眠或者唤醒,因此它可以可以是一个电池供电设备。通常,终端设备对存储空间(特别是 RAM 的需要)比较小。一般终端设备采集相关的传感器节点信息,控制相应的控制部分。
注意:一个ZigBee网络由一个协调器节点、多个路由器和多个终端设备节点组成。设备类型不以任何方式限制应用程序的类型,可以运行在特定的装置,这就说明了一个zigbee的开发模块,你给它烧写什么设备类型的代码它就是什么的设备类型了。
拓扑结构
ZigBee网络支持星状、树状和网状三种网络拓扑结构,它们的介绍(优缺点)分别为
星状网络(star)由一个PAN协调器和多个终端设备组成,只存在PAN协调器与终端的通讯,终端设备间的通讯都需通过PAN协调器的转发。
树状网络(tree)由一个协调器和一个或多个星状结构连接而成,设备除了能与自己的父节点或子节点进行点对点直接通讯外,其他只能通过树状路由完成消息传输。
网状网络(mesh)是树状网络基础上实现的,与树状网络不同的是,它允许网络中所有具有路由功能的节点直接互连,由路由器中的路由表实现消息的网状路由。该拓扑的优点是减少了消息延时,增强了可靠性,缺点是需要更多的存储空间开销
关于网络结构的定义同样在nwk_globals.h中定义。默认的是是选择网状结构
信标与非信标模式
ZigBee网络的工作模式可以分为信标(Beacon)和非信标(Non-beacon)两种模式。信标模式实现了网络中所有设备的同步工作和同步休眠,以达到最大限度的功耗节省,而非信标模式则只允许终端设备进行周期性休眠,协调器和所有路由器设备必须长期处于工作状态
信标模式下,协调器负责以一定的间隔时间(一般在15ms-4mins之间)向网络广播信标帧,两个信标帧发送间隔之间有16个相同的时槽,这些时槽分为网络休眠区和网络活动区两个部分,消息只能在网络活动区的各时槽内发送。
非信标模式下,ZigBee标准采用父节点为终端设备子节点缓存数据,终端设备主动向其父节点提取数据的机制,实现终端设备的周期性(周期可设臵)休眠。网络中所有父节点需为自己的终端设备子节点缓存数据帧,所有终端设备子节点的大多数时间都处于休眠模式,周期性的醒来与父节点握手以确认自己仍处于网络中,其从休眠模式转入数据传输模式一般只需要15ms。所有节点竞争同一信道,使用CSMA/CA的信道接入技术,(就是节点在发送数据之前先监听信道,如果信道空闲则可以发送数据,否则就要进行随机的退避,即延迟一段随机时间,然后再进行监听,)信标与非信标是信标接入模式。TI的Zstack协议栈采用的就是非信标模式。(网上是这么说的我也不知道在哪里定义的,稍后我去翻一翻源代码看一下)信道接入方式采用免冲突载波检测多址接入(CSMA-CA)机制;(这个不多说自己去查一下)
地址(16与64位地址)网络地址
ZigBee设备有两种类型的地址。一种是64位IEEE地址,即MAC地址,另一种是16位网络地址。
64位地址使全球唯一的地址,设备将在它的生命周期中一直拥有它。它通常由制造商或者被安装时设臵。这些地址由IEEE来维护和分配。
16位网络地址是当设备加入网络后分配的。它在加入的网络中是唯一的,用来在网络中鉴别设备和发送数据。其中,协调器的网络地址为0x0000,是固定的,所有的zigbee协调器中的网络地址是唯一的.(所以16位地址是可变的),这也决定了一个zigbee网络中最多只能有65535个节点组成。
在ZigBee无线网络里,每一个zigbee模块(模块可以由终端、路由器、协调器组成)都有一个在该网络中唯一的2个字节的地址,这个地址叫做网络地址(网络短地址)
PANID(个域网ID):这个是2个字节的编码,用来区别不同的ZigBee无线局域网,我们称为个域网ID。
信道
ZigBee无线通信需要高频的载波来提供发射效率,ZigBee模块之间要正常的通信,接收模块和发射模块的频率要一致。ZigBee有27个载波可以进行通信,载波又叫信道。这些载波的频率落在某个频率区段,我们把这些区段叫做频段
2.4G频段 16个信道
915频段 896频段 11个信道
但是TI的所有支持ZigBee底层协议的芯片只有在2.4G频段的16个信道里进行通信,即11-26号信道(0-10信道在896、915频段),11号信道是2405M,以后没增加一个信道,频率增加5M,26号信道2485M。
/* Default channel is Channel 11 - 0x0B */
// Channels are defined in the following:
// 0 : 868 MHz 0x00000001
// 1 - 10 : 915 MHz 0x000007FE
// 11 - 26 : 2.4 GHz 0x07FFF800
//
//-DMAX_CHANNELS_868MHZ 0x00000001
//-DMAX_CHANNELS_915MHZ 0x000007FE
//-DMAX_CHANNELS_24GHZ 0x07FFF800
在ZigBee无线局域网里面,节点(无线模块)按照网络里面的功能来划分,可以分为协调器,路由器,终端,他们的硬件可以一模一样。之所以在网络里面表现不同的功能,是因为他们下载了不同功能版本的程序,在同一个网络里担当不同的角色功能。
下载了路由器和终端代码的节点,上电后第一件事情是去寻找网络(是根据属于同一个个域网ID和信道来进行划分),请求加入,;而网络不会平白无故去产生,是由下载了协调器代码的节点创建的,下载协调器代码的节点上电后第一件事情是去创建网络。
任何一个网络,第一个节点一定是该网络的协调器,有且只有一个。协调器网络组建后,网络里面的协调器的网络地址已经固定为0x0000,当设备加入成功后,会产生一个ZDO_STATE_CHANGE_EVT事件,这个事件就是设备加入网络成功后,并在网络中的身份确定后产生的一个事件,我们可以在这里处理,一些初始化,比如可以发送终端的短地址,IEEE地址等,这里协调器接收到以后,可以提取出终端的短地址,其实在终端给协调器发送的每个数据包中,都含有其自身的短地址,如结构体当中的afAddrType_t srcAddr;协调器在接收到短地址后,就可以知道自己下面管辖的终端节点,或者路由节点有哪些了。
注意:模块如果想要通信,必须位于同一个信道,同一个个域网,在Tools文件夹中的f8wConfig.cfg文件可以修改信道和个域网
应用层是一个任务,它有一个系统分配给它的数值唯一的编号叫做任务ID,查看ZMain.c 中的osal_init_syste()中的osalInitTasks();发现应用层的任务ID是最大的那个(8);
任务可以处理事件,处理事件的这些代码都在一个函数里,这个函数就叫做任务处理函数,我们查看osal_start_system()中使用的tasksArr[] 知道应用层对应的任务处理函数是GenericApp_ProcessEvent();
应用层任务还有一个2个字节的变量 任务事件变量tasksEvents
当发现有一个任务事件变量的值不为0就意味着这个任务层有事件要去处理,如果这个任务事件变量得到值为0就是没有事件要去处理,如果有事件要去处理,那么会调用相对应的任务事件处理函数就行处理。
tasksEvents[]= {任务事件变量a,任务事件变量b,……}
tasksArr[] = {任务事件处理函数A,任务事件处理函数B,……};
osal_init_system(); 在这个函数里发现系统给所有任务分配任务ID,表示当前系统又taskCnt个任务;
osal_start_system();所有协议栈在稳定工作时,他的行为是不断的去读所有的任务的任务事件变量,如果发现所有的任务事件变量的值都为0,说明没有事件要去处理,直到发现某个任务事件变量的值不为0,就会通知这个不为0的任务ID去处理这个事件。
在GenericAPP.c 中的319行跳转到GENERICAPP_SEND_MSG_EVT的定义处,定义我自己的事件,比如#define GENERICAPP_MY_EVT 0x0002(后面的数字不能随便定义,每一个bit位上只有有一个为1),然后在网络状态改变的那里利用osal_start_timerEx将自定义的事件和应用层任务ID挂钩,然后在任务事件处理代码那里用调用osal_start_timerEx,那么可以实现灯的闪烁
寻址发数据
ZigBee向网络中的设备发送数据,应用程序通常使用AF_DataRequest()函数。
AF_DataRequest(afAddrType_t*dstAddr,//全ZB目的地址,包含NWK地址与ENDPoint
endPointDesc_t*srcEP,//端点描述符
uint16cID,//簇ID
uint16len,//要发送数据的长度
uint8*buf,//要发送的数据(地址)
uint8*transID,//传输序列号
uint8options,//发送选项的有效标志位
uint8radius);//一般设置为AF_DEFAULT_RADIUS
我们主要看一下第一个参数。NWK地址(这里指的是16位的地址)ENDPoint(这是那个20)
typedefstruct
{
union
{
uint16shortAddr;
ZLongAddr_textAddr;
}addr;
afAddrMode_taddrMode;
byteendPoint;
uint16panId;//usedfortheINTER_PANfeature
}afAddrType_t;
其中包括16或64为地址,网络地址。
地址模式参数
typedefenum
{
afAddrNotPresent=AddrNotPresent,
afAddr16Bit=Addr16Bit,
afAddr64Bit=Addr64Bit,
afAddrGroup=AddrGroup,
afAddrBroadcast=AddrBroadcast
}afAddrMode_t;
它主要用来表征是单点传送(unicast),多点传送(multicast)还是广播传送
单点传送(Unicast)Uicast是标准寻址模式,它将数据包发送给一个已经知道网络地址的网络设备。将afAddrMode设臵为Addr16Bit并且在数据包中携带目标设备地址。
间接传送(Indirect)当应用程序不知道数据包的目标设备在哪里的时候使用的模式。将模式设臵为AddrNotPresent并且目标地址没有指定。取代它的是从发送设备的栈的绑定表中查找目标设备。这种特点称之为源绑定。(这种方式一般用于绑定的情况下)当数据向下发送到达栈中,从绑定表中查找并且使用该目标地址。这样,数据包将被处理成为一个标准的单点传送数据包。如果在绑定表中找到多个设备,则向每个设备都发送一个数据包的拷贝。上一个版本的ZigBee(ZigBee2004),有一个选项可以讲绑定表保存在协调器(Coordinator)当中。发送设备将数据包发送给协调器,协调器查找它栈中的绑定表,然后将数据发送给最终的目标设备。这个附加的特性叫做协调器绑定(CoordinatorBinding)。
广播传送(broadcast)当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式。地址模式设臵为AddrBroadcast。目标地址可以设臵为下面广播地址的一种:
NWK_BROADCAST_SHORTADDR_DEVALL(0xFFFF)——数据包将被传送到网络上的所有设备,包括睡眠中的设备。对于睡眠中的设备,数据包将被保留在其父亲节点直到查询到它,或者消息超时(NWK_INDIRECT_MSG_TIMEOUT在f8wConifg.cfg中)。
NWK_BROADCAST_SHORTADDR_DEVRXON(0xFFFD)——数据包将被传送到网络上的所有在空闲时打开接收的设备(RXONWHENIDLE),也就是说,除了睡眠中的所有设备。
NWK_BROADCAST_SHORTADDR_DEVZCZR(0xFFFC)——数据包发送给所有的路由器,包括协调器。
组寻址(GroupAddressing)当应用程序需要将数据包发送给网络上的一组设备时,使用该模式。地址模式设臵为afAddrGroup并且addr.shortAddr设臵为组ID。
aps_Group_tgroup;
group.ID=0x0001;
group.name[0]=0;//Thiscouldbeahumanreadablestring,比如说“Group1”
aps_AddGroup(SAMPLEAPP_ENDPOINT,&group);
其中的aps_Group_t中只包含ID与name两个参数
获取重要地址
NLME_GetShortAddr()——返回本设备的16位网络地址
NLME_GetExtAddr()——返回本设备的64位扩展地址
NLME_GetCoordShortAddr()——返回本设备的父亲设备的16位网络地址NLME_GetCoordExtAddr()——返回本设备的父亲设备的64位扩展地址
NLME_IsAddressBroadcast()此函数根据设备能力来评估提供的地址是否是一个有效的广播地址。
NLME_SetBroadCastFilter()此函数根据设备能力设置掩码,用于处理有效的广播地址
单播
单播的特点是:在ZigBee网络中,模块之间要进行通信,发射模块要明确知道接收模块的网络地址,以这个发送数据给指定接收模块。
单播要确定的信息:
1)目标地址:也就是网络短地址,协调器的网络地址是0x0000.
2)目标端点与簇
端点:通常要发送到的数据要明确该数据是给接收模块的哪个任务层的,而这个是通过端点来指定的;(引用端口的主要原因是TI实现的zigbee协议栈中加入了一个小的操作系统,这样每个节点的所有端口共用一个发射/接收天线,不同的节点上的不同端口之间进行通信仅仅通过网络地址是无法区分,因此在发送数据的时候不但要指定网络地址还有指定端口号)
簇:在接收模块中,每个任务层可以对应不同的数据类型采取不同的处理方式,这里数据类型就是簇
3)要发送的数据与大小
发送函数和接收函数
发送
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius )
{
AF_DataRequest
( &GenericApp_DstAddr, 接收方发送方的地址,发送数据的格式(广播、单播和多播)
&GenericApp_epDesc, 发送方的地址,端点描述符,指定发送数据的端口
GENERICAPP_CLUSTERID, 发送数据使用的命令,主要用于接收数据的时候区分不同的命令
(byte)osal_strlen( theMessageData ) + 1, 数据大小
(byte *)&theMessageData, 数据的地址
&GenericApp_TransID,//指向发送数据的序号
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) 发送的路径,一般采取默认
AF_DataRequest( &GenericApp_DstAddr,
&GenericApp_epDesc,
GENERICAPP_MY_CLUSTERID, //指定事件
(byte)osal_strlen( str ) + 1,
str,//(byte *)&theMessageData,
&GenericApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
接收
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
P1_1 = ~P1_1;
case GENERICAPP_CLUSTERID:
// "the" message
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" );
#elif defined( WIN32 )
WPRINTSTR( pkt->cmd.Data );
#endif
break;
case GENERICAPP_MY_CLUSTERID:
/*
把自己收到的存放在(char*)pkt->cmd.Data;的数据取出来然后
通过串口发送给PC机
*/
char *s=(char*)pkt->cmd.Data;
UartTX_Send_String(s ,strlen(s));
break;
}
}
扩展阅读:zigbee协议栈数据包格式