您好,欢迎来到暴趣科技网。
搜索
您的当前位置:首页ZigBee协议栈学习总结

ZigBee协议栈学习总结

来源:暴趣科技网
ZigBee协议栈学习笔记

1、 CC2530的Flash为非易失性存储器,能保存必要数据,以便在设备重启后直接使用,使

用此功能可以保存具体的网络参数。

2、 CC2530协议栈例程模板中,SampleAPP.c文件相当于CC2430模板中sapi.c文件。 3、 CC2530引脚的寄存器介绍(即PxDIR、PxSEL、PxINP等语句的意思)

P0和P1 是完全的8 位端口,而P2 仅有5 位可用,作为缺省的情况,每当复位之后,所有的数字输入/输出引脚都设置为通用输入引脚。在任何时候,要改变一个端口引脚的方向,就使用寄存器PxDIR 来设置每个端口引脚为输入或输出。因此只要设置PxDIR 中的指定位为1,其对应的引脚口就被设置为输出了。用作输入时,通用I/O 端口引脚可以设置为上拉、下拉或三态操作模式,作为缺省的情况,复位之后,所有的端口均设置为带上拉的输入,。要取消输入的上拉或下拉功能,就要将PxINP 中的对应位设置为1。普通I/O就是作为输入输出接口,外设功能时就是将io作为外设与外部链接得接口,具体用什么,要根据实际需要,比如你要控制继电器,io作为普通功能输出就可以。要是使用串口,io就要作为外设接口与外通讯。GPIO-P0,P1,P2,P0-P1是8个,P2为5位,逻辑电平高低1,0,外设IO,就是定时器1,3,4和USART0 ,USART1,和ADC(P0口)。外设IO位置是固定的管脚。

例子:设置P1.0、P1.1、P1.4引脚输出代码。(写成P1DIR = 0x13也行)

4、 LED灯、按键等一些基础硬件引脚定义在HAL-Target-CC2530EB -Config-hal_board_cfg.h

头文件中,如下图所示要修改预定义的三个地方,BV(0)为该引脚所在的位,在第几位BV后面数字为几(可查看该变量定义,为0x01向左移动n位,该变量用于后面运算),后面两个变量一个控制引脚的端口号,一个为引脚端口号的方向选择。

5、 按下按键启动协调器设备并且开启网络,他的代码初始化的时候代码是跟自动启动的设

备并开启网络的代码初始化是一样的,但是为什么他没有在初始化的时候启动:因为按

键启动的有预定义了宏HOLD_AUTO_START,自动启动的没有,才导致这样的结果。这个预定义导致在SAPI层对devState的初始化的值不同(代码在ZDapp层的220行左右),群主给的例程设备都是自动启动的(原因,在SampleApp_Init初始化中加入了,#if defined ( HOLD_AUTO_START ),执行ZDOInitDevice(0);语句,导致都是自动启动)。 #if defined( HOLD_AUTO_START ) devStates_t devState = DEV_HOLD; #else

devStates_t devState = DEV_INIT;

6、 用户自己添加的应用任务程序在 Zstack 中的调用过程:main()---> osal_init_system()--->

osalInitTasks()---> SampleApp_Init()

7、 只需要掌握有关ZigBee协议栈提供串口操作相关的三个函数为:

(1) uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config);进行串口的初始化

(2) uint16 HalUARTRead(uint8 port, uint8 *buf, uint16 len);从串口读取数据并将其存

放在buf数组中(即在串口助手上发送数据),该函数返回的值为字符串的长度。

(3) uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len);将接收的字符(即buf数组

中的数据,设备通过串口发送数据到PC端)输出到串口,该函数返回的值为字符串的长度。

8、 在自己设计的协调器板子上,调用ZDO_StartDevice()函数,该函数进入网络层,形

成网络形成请求(NLME_NetworkFormationRequest),有请求就有反馈函数,协调器启动网络后,LED3灯被点亮(LED3为红灯),LED4被熄灭(LED4为绿灯),若LED3灯闪烁表示建立失败,该语句在函数ZDO_NetworkFormationConfirmCB( ZStatus_t Status )中(ZDApp.c)。通过ZDO_UpdateNwkStatus ( devState )函数中,设置ZDO_STATE_CHANGE,ZDO状态改变事件,使AF层中的SampleApp.c处理(SAPI_ProcessEvent),最后生成( osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );)周期性发送事件进行处理。在自己设计的终端设备板子上,最后通过void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )函数,设置LED3灯点亮(LED3为绿灯),LED4被熄灭(LED4为黄灯),若LED3灯闪烁表示建立失败并设置了设备对象加入消息。在void ZDApp_ProcessNetworkJoin( void )函数中可以定义节点节能模式、路由功能。 9、 中断相关寄存器:

10、 定时器相关寄存器:

11、 路由对于应用层来说是完全透明的。路由还能够自愈ZigBee网络,如果某个无线连接

断开了,路由功能又能自动寻找一条新的路径避开那个断开的网络连接。极大的提高了网络的可靠性,同时也是ZigBee网络的一个关键特性(自组织、自愈能力强,通信可靠)

路由器执行如下功能:(1)允许其他设备加入网络;(2)多跳路由,即一个设备传递信息可借助路由器节点进行中继他的信息;(3)协助他的电池供电终端设备通信。 1. 路由协议(Routing Protocol)

ZigBee使用一种基于AODV(“按需距离矢量”)的路由协议。简化后用于传感器网络。 当一个路由器从它的应用或另一个设备收到一个单播数据包时,网络层按照下述过程进行转发。如果目的地址是相邻的路由器(包括它的子设备),数据包将被直接传送给目的设备。否则,路由器将检查其路由表中与数据包路由目的地相对应的一个条目,如果有一个包含目的地址的路由存在,数据包将被转发给存储该路由表条目中的下一跳地址。如果没有一个包含目的地址的路由表条目存在,一个路由发现将被启动并且数据包将被缓存直到该过程完成。

ZigBee终端节点不执行任何路由功能。终端节点要向任何一个设备传送数据包,他只需简单的将数据向上发送给它的父设备,由它的父设备以他自己的名义执行路由。

ZigBee路由器,包括协调器执行下面的路由函数: (i)路径发现和选择; (ii)路径保持维护; (iii)路径期满;

2. 路径的发现和选择(Route Discovery and Selection) 路径选择就是选择出可能最小成本的路径。每个结点通常持有跟它所有邻接点的“连接

成本(link costs)”。连接成本的典型函数是接收到的信号的强度。沿着路径,求出所有连接的连接成本总和,便可以得到整个路径的“路径成本”。路由算法试图寻找到拥有最小路径成本的路径。

路径通过一系列的请求和回复数据包被发现。

3. 路径保持维护(Route maintenance) 网状网提供路径维护和网络自愈功能。

4. 路径期满(Route expiry) 如果在一定的时间周期内,没有数据通过沿着这条路径发送,这条路径将被表示为期满。期满的路径一直保留到它所占用的空间要被使用为止。可以在配置文件中设置ROUTE_EXPIRY_TIME期满时间,单位为秒。如果设置为0,则表示关闭自动期满功能。

路由设备致力于路径发现,保持维护路径发现表。 路径设置快速参考:

(a) 设置路由表大小 MAX_RTG_ENTRIES,这个值不能小于4;

(b) 设置路径期满时间 ROUTE_EXPIRY_TIME,单位秒。设置为零则关闭路径期满。 (c) 设置路径发现表大小 MAX_RREQ_ENTRIES,网络中可以同时执行的路径发现操作的个数。

5.路由表

每一个ZigBee路由器(包括ZigBee协调器),包含一个路由表,设备在路由表中存储参与数据包路由所需的信息。每一个路由表条目包含了目的地址,下一跳节点和链接状态。发送到目的地址的所有数据包通过下一跳节点被路由。此外,为了回收那些不再被使用的条目所占的空间,路由表中的条目可被标记为过期。 12、 重要点:ZigBee协议栈中路由器自带转发功能,当终端设备由于距离协调器太远时,无

法进行通讯,可在中间加入路由节点进行转发终端设备的数据(对传播方式没有规定,点播、广播、组播都行),这时候终端设备就会加入路由器的网络(该路由器也在协调器建立的网络中),之后路由器会建立一个链接表与下一个的地址,他收到数据后,发现不是给自己的数据,会在链接表找下一个的地址,找到后就会转发出去,转发的间隔时间默认为100毫秒(这个可以自己修改)。只要路由器在协调器建立的网络中,就也能采集数据与进行数据的转发,路由器也是设备,只是比终端设备多了一个数据转发的功能,但是他没有低功耗模式,即无法进行休眠,只有终端设备能进行休眠模式。终端设备加入路由器的状态,跟原先加入协调器状态都是一样的,可以用LED指示灯来判定。路由器是能够维持网络的,协调器只是进行网络的建立,当网络建立好之后(可以不要协调器),它就相当于是路由器,但是协调器有一个好处:协调器的网络地址是已知的为0x0000,这个在处理传感器组网的时候十分好用,正因为有这个功能,所以发送数据直接用协调器地址0x0000这样直接发送给协调器比较方便,路由器的话,它的地址是未知的,是加入网络以后才配置的。 13、 网络地址的分配:ZigBee使用一个分布式的编制方案来分配网络地址,以确保设备被分

配的16位网络地址在网络中是唯一的。一个设备仅和它的父设备进行通信。编制方案需要一些参数被提前知道并被配置在每一个加入网络的路由器中。主要有以下3个参数: (1) MAX_DEPTH(该值在协议栈中定义为5)参数决定了网络的最大深度。协调器在深

度为0,它的子节点在深度为1,这些子节点的子节点在深度为2,以此类推。 (2) MAX_CHILDREN(该值在协议栈中定义为20)参数决定了以路由器(或协调器)可

拥有的子节点最大数量。

(3) MAX_ROUTERS(该值在协议栈中定义为6)参数决定了以路由器(或协调器)可拥

有具有路由能力的子节点的最大数量。

若需修改这些值,必须保证这些新值是合法的,因为总的地址空间被在216,故参数的大小是有的;值合法后,开发者需要确保不适用标准栈规范(即ZigBee联盟定义的规范ZigBee2007,其标识符为1)而是设置为“专用网络”的栈规范(标识符为0)。 14、 ZigBee协议栈休眠机制:CC2530有3种睡眠模式,pm2模式比较省功耗而且可以被定

时唤醒;pm3模式最省电但是只能被外部中断唤醒。

在定义了“POWER_SAVIGN”宏以后,在Zmain函数中的osal_start_system()函数里面就会调用 osal_pwrmgr_powerconserve()函数。osal_pwrmgr_powerconserve()函数把获取os层timer的下一次的到时时间作为参数,调用hal_sleep()进入pm2睡眠模式,如果当前没有任务那么将进入pm3。所以说一旦启用省电模式,系统将根据当前的任务自动进入睡眠,睡眠前设置sleeptimer,醒来的时间刚好等于下次任务到来的时间,当完成任务后再次进入睡眠。

定义终端设备的睡眠由宏POWER_SAVING控制,休眠周期取决于OSAL的事件查询,如果出现OSAL的事件,则立即唤醒。设置终端设备休眠实质是设置MCU低功耗,但是如果传感器本身电流就很大,这样休眠也是杯水车薪。浅度休眠定时器会工作,关闭其他外设(其中,传感器、LED灯等不算外设,外设指的是MCU内部的功能外设,如ADC,定时器,运算器等);深度休眠是关闭所有外设。中间唤醒由于没有任务处理会马上进入休眠,这个过程不会有很大的能耗消耗。

可通过以下方式判断是否进入休眠:1) 进入休眠函数断点调试;2)用示波器测试32M晶振是否间歇性工作,用示波器测;3)通过串入供电电路,一个10电阻,然后看具体的工作过程!

设置了POWER_SAVING就是开启了PM2模式,系统在没有事件需要执行的时候会自动进入休眠,不需要再增加睡眠事件,你只需要调整好需要执行的事件就行了,比如:周期性唤醒事件,广播事件等等。注意,此模式下如果 没有下一次周期事件,会自动进入PM3模式,然后就只能通过IO中断唤醒了,而且osal_clock会停止计时,所以最好写一个周期性唤醒任务,周期可以设置的比较长,事件不需要执行什么特殊指令,只要能唤醒芯片,维持时钟计时就行了。

通过协议栈休眠,只要在预编译里定义POWER_SAVING,协议栈会判断操作系统是否有Task要处理,如果有并且到时间,就唤醒处理,如果有Task但没到时间,就进入PM2休眠。如果没有Task就进入PM3休眠。一旦进入PM3,就需要外部IO中断或定时器中断来唤醒。需要添加的Task可以用下面这个函数定时来执行,参数是我的工程里的,你自己定义:

osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF) ));

在其他资料中,有的在休眠时做了如下更改:将Onboard.c中的OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;改为OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; 这个参数只是按键轮询检测还是终端检测的设置,对休眠不影响。

休眠设置中关于f8Config.cfg中关于poll_rate三个参数(这三个参数为循环选项)设置

的说明:(1)-DPOLL_RATE=1000(意思:终端设备1S向协调器轮询一次看有没有发送数据。);(2)-DQUEUED_POLL_RATE=100;(3)-DRESPONSE_POLL_RATE=100; 当电源管理功能开启后(添加POWER_SAVING),任一个轮询选项的设置都会影响到睡眠模式。时间延迟的设置不能用于DEEP skeep中的轮询,因此了降低功耗。这三个轮询(巡检)选项分别如下:

(1) Data Request Polling(数据请求巡检)—周期性向父节点发送数据请求来轮

询消息队列。轮询的时间间隔值可以通过改变zgPollRate来设定和存储,或者由NLME_SetPollRate()函数设定,该函数的调用会立马开始巡检,即便之前被禁止。

(2) Queued Data Polling(数据接收巡检)—在收到数据指示后,就会周期性向

父节点发送数据请求消息。这个时间间隔可由NLME_SetQueuedPollRate()或zgQueuedPollRate来设定和存储。这种特点允许快速地接受(卸载)数据而不用考虑Request Poll rate。

(3) Response Data Polling(数据响应巡检)—在收到数据确认指示后,就会周

期性向父节点请求响应消息,这个时间间隔可由NLME_SetResponsePollRate()或改变zgResponsePollRate来设定和存储,这种特点允许快速地接受(卸载)响应信息(such as APS Acknowledgements),而不用考虑zgResponsePollRate。

如果只是使用默认的轮询频率进入睡眠态,则只能进入LITE sleep。为了进入DEEP sleep则必须将gNWK_POLL_RATE设为0,不让它反复轮询。设置这个三个选项可以实现多种轮询方式,例如,对于一个不需要接收消息的设备(就是当这几个参数设置为0时,终端设备接收不到协调器发送的数据,没有接收功能),在它加入网络后,就将这三个选项都设为0。如果APS层使用了ACK,则必需确保在消息发送后到收到ACK这一段时间内,轮询是使能的。 15、 当终端设备失去父节点以后,节点会先发出orphan然后会以rejoin的方式去加网络,

因为对于节点来说加入网是首要的任务。如果不想让节点不停的去搜索网络的话,可以extern uint8 ZDApp_StopJoiningCycle( void );,把搜索网络关掉,或者开一个定期把搜网的周期拉大,当节点一直寻找网络时,此时功耗较大 16、 设备之间通信分为(1)设备向主协调器发送数据(2)主协调器向设备发送信息(3)

设备之间通信。对于星型拓扑结构只有前两种通信方式,而对于对等(树型、网状)拓扑结构则存在3种通信方。

不同网络拓扑结构有不同的特点:

(1) 星状网络:所有节点只能与协调器进行通信

(2) 树状网络:终端节点只能与父节点通信,路由节点可与子节点和父节点通信 (3) 网状网络:所有节点都是对等实体,任意两点之间都可以通信 对于不同的用户需求可使用不同的网络结构解决方案:1) 需要一个小范围内的无线网,用来传输传感器收集到的信息。 2) 需要一个能够能延伸的无线网,来实现远距离采集传感器信息。 2.解决方法 :第一个需求使用最简单的星型网络拓扑结构即能实现。 第二个需求使用网型拓扑结构和簇型拓扑结构均能实现,后续根据实际情况决定采用何种方案。

在ZigBee协议栈中,已经定义好了各种网络拓扑结构(在nwk_globals.h文件中),本项目使用的网络拓扑结构为网状网络,并且协议栈默认使用HOME_CONTROLS参数配置方案。若需要修改网络拓扑结构,只需将预定义中NWK_MODE改为相应的网络类型即可(可通过查找来修改)。 17、 485设备是半双工的通信方式,因此需要使用另外的IO口来控转换芯片的收发工作。

原理为:在使用UART发送时,从IO口输出高电平,使485芯片处于接收状态。当发送完成产生中断时,将IO口输出低电平,等待设备的反馈信息(具体看土壤传感器驱动代码)。如果设备收不到正确的信号,无法正常工作,原因为发送完成产生中断时,延时3毫秒在输出低电平,这是因为当检测到UART发送完成的中断发生时,UART仍然有1到2个数据没有完成发送,因此需要延时两个数据的长度。一个数据大约为10为位,包括起始位,数据位,停止位,因此需要的时间是(1/9600)*1000000us*10*2=2083us,所以设置时间长度为3ms。 18、 ZigBee协议栈下串口通讯:对于串口中使用回调函数对串口接收进行处理,可参考

http://www.docin.com/p-63991.htmlhttp://www.docin.com/p-63991.html?qq-pf-to=pcqq.c2c#userconsent# 进行修改(协议栈下串口回调机制:在串口初始化中设置回调函数,当2530芯片中串口发现有数据接收之后,会马上调用回调函数进行处理)。

对于ZigBee协议栈中双串口的设置以及串口采用中断模式的设置,可参看材料“ZigBee学习资料中的协议栈双串口实验”。

协议栈通过串口初始化分别配置了一块内存空间rxBuf和txBuf,具体在HalUARTOpen()里配置。而中断与DMA这两种模式具体就运用于数据在串口缓存U0_1DBUF与rxBuf/txBuf之间传送的过程。 (1)串口接收DMA模式:(data) —> U0DBUF —(DMA)—> rxBuf —> HalUARTRead()读取rxBuf数据进行处理

(2)串口接收中断模式:(data) —> U0DBUF —(中断)—> rxBuf —> HalUARTRead()读取rxBuf数据进行处理

(1)串口发送DMA模式:(data) <— U0DBUF <—(DMA)— txBuf (2)串口发送中断模式:(data) <— U0DBUF <—(中断)— txBuf

协议栈中HalUARTWrite与HalUARTRead并不是真正的串口发送与串口接收函数,它们只是将要发送的数据写入缓存,然后下一次通过主函数中系统开始函数中的串口轮询,当发现有要发送的数据时,才真正将缓存中的数据通过串口发送出去(static void HalUARTPollDMA(void)),该函数为DMA串口发送,并且在该函数中调用串口发送函数中断(void HalUARTIsrDMA(void),执行到该函数才是串口数据真正发送完毕)。并且static void HalUARTPollDMA(void)函数最后会对回调函数进行判断,只要回调函数不为空,就会启动回调函数。可参考http://www.tuicool.com/articles/U3ymA3 19、 (1)现阶段协调器上电组网,路由器和终端上电(不分先后顺序),此时服务器能收到

数据,给协调器断电后重启,此时协调器接收不到路由器与终端产生的数据。原因:网络组好后,将协调器关掉,路由器是不会再加入到重新上电的协调器了。因为PANID不同,路由器已经是一个的网络(协调器创建网络后也是一个普通的协路由器了)。如果你再重新给协调器上电,PANID和路由器相同不能建立网络,只能在另一个PANID上建立网络,那么这两个设备就不是一个网络了,所以你一直开着的那个设备没有反应,因为他们已经不是一个网络里的了。(此时关掉路由器,终端不会自动加入协调器断电重启的网络,并且重启终端也不会加入,此时重启路由器也不会加入协调器网络,因为他们现在是两个网络,此时只有在重启协调器才能让终端跟路由器加入协调器建立的网络)

(2)解决方法:在协调器配置工程中,在Project -> Options -> c/c++ Compiler 中选中对话框 Preproce ,在Define symbols 中添加 NV_RESTORE宏定义。NV_RESTORE选项保

存的内容主要是节点运行过程中的状态量,借助这个特性,节点在掉电或者复位之后不需要无线电通讯就可以恢复到之前的网络连接状态。NV_RESTORE保存的内容主要有网络层数据库NIB、设备关联表、绑定表、路由表等。经测试:能够解决现阶段出现的问题。(此时,给协调器断电重启,协调器入网指示灯马上点亮,终端给路由器发送数据,协调器能收到。) 20、 协议栈对按键有两种处理方式:(软定时器即软件定时器)

(1) 中断法:有按键按下,则进入中断,开启一软定时器25ms后,读取键值进行

相应处理。

(2) 查询法:开启一软定时器,系统每隔100ms进行轮询,如有按键按下读取键值

进行相应处理。

在协议栈默认代码中,HAL_KEY_SW_6 0x20 // Button S1 if available为按键1,通过P0.1脚控制。

查询法函数调用流程如下:HalKeyConfig()配置一定时器为轮询按键作准备——时间一到触发系统任务事件调用Hal_ProcessEvent()—— 调用HalKeyPoll()得到按键值——调用OnBoard_KeyCallback()进一步处理——调用OnBoard_SendKeys()构造消息包,准备触发应用按键事件(注意这个应用之前必须通过RegisterForKeys()注册接收按键事件的任务ID)——调用osal_msg_send()向系统发送消息——调用osal_set_event()设置事件发生标志——调用SampleApp_ProcessEvent()处理事件——最终调用SampleApp_HandleKeys()处理具体按键事件。

按键引起的中断属于外部中断。中断法函数调用流程如下:alKeyConfig()进行按键中断配置——按键引起中断进入中断函数HAL_ISR_FUNCTION()——调用halProcessKeyInterrupt()对按键中断进行下一步处理:清除中断标志,启动一定时器——时间一到触发系统任务事件调用Hal_ProcessEvent()—— 调用HalKeyPoll()得到按键值——调用OnBoard_KeyCallback()进一步处理——调用OnBoard_SendKeys()构造消息包,准备触发应用按键事件【注意这个应用之前必须通过RegisterForKeys()注册接收按键事件的任务ID】——调用osal_msg_send()向系统发送消息——调用osal_set_event()设置事件发生标志——调用SampleApp_ProcessEvent()处理事件——最终调用SampleApp_HandleKeys()处理具体按键事件。

从上面对按键查询法和中断法的总结,可以看到中断法就比查询法大致多出两步:进入中断函数HAL_ISR_FUNCTION()与调用调用halProcessKeyInterrupt(),后面都是开始开启一软定时器触发相同事件HAL_KEY_EVENT,然后……………………。

自己在协议栈中添加了一应用,应用中需要用到按键,如果硬件配置按协议栈默认来配置,那基本只要完成这两件事情:

(1)、在自己的应用中注册按键:RegisterForKeys(MyAPP_TaskID),这样用户应用就得接收到所有按键事件

(2)、在自己的应用任务事件处理函数中配置具体处理函数:比如SampleApp中按SW1,发送闪烁信息给组1,按SW2,进/退组1.

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- baoquwan.com 版权所有 湘ICP备2024080961号-7

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务