Zigbee网络设备启动流程—协调器(自启动模式)
- 格式:doc
- 大小:60.50 KB
- 文档页数:10
ZigBeeLED设备启动流程ZigBee LED设备启动流程及按键机制一.先对LED引脚进行相关配置,主要在两个文件中修改(hal_board_cfg.h ,Hal_led.h )hal_board_cfg.h定义我们三个LED的引脚配置分别是 P1_0 P1_1 P0_4 , 低电平有效。
/* 1 - Green */#define LED1_BV BV(0)#define LED1_SBI T P1_0#define LED1_DDR P1DIR#define LED1_POLARITY ACTIVE_LOW/* 2 - Red */#define LED2_BV BV(1)#define LED2_SBIT P1_1#define LED2_DDR P1DIR#define LED2_POLARITY ACTI VE_LOW/* 3 - Yellow */#define LED3_BV BV(4)#define LED3_SBIT P0_4#define LED3_DDR P0DIR#define LED3_POLARITY ACTI VE_LOW在HAL_BOARD_INI T将三个LED引脚配置为输出#define HAL_BOARD_INIT(){……………..设置LED1 LED2 LED3 引脚为输出LED1_DDR |= LED1_BV;LED2_DDR |= LED2_BV;LED3_DDR |= LED3_BV;}/* ----------- LED's ---------- */#define HAL_TURN_OFF_LED1() st( LED1_SBI T = LED1_POLARITY (0); ) //关灯#define HAL_TURN_ON_LED1() st( LED1_SBI T = LED1_POLARITY (1); ) //开灯#define HAL_TOGGLE_LED1() st( if (LED1_SBIT) { LED1_SBIT = 0; } else { LED1_SBI T = 1;} ) //取反#define HAL_STATE_LED1() (LED1_POLARITY (LED1_SBI T)) //获取状态Hal_led.h定义了3个LED/* LEDS - The LED number is the same as the bit position */ #define HAL_LED_1 0x01#define HAL_LED_2 0x02#define HAL_LED_3 0x04#define HAL_LED_ALL (HAL_LED_1 | HAL_LED_2 | HAL_LED_3) /* Modes 定义了LED的一些模式*/#define HAL_LED_MODE_OFF 0x00#define HAL_LED_MODE_ON 0x01#define HAL_LED_MODE_BLINK 0x02#define HAL_LED_MODE_FLASH 0x04#define HAL_LED_MODE_TOGGLE 0x08/* Defaults 定义了一些LED的控制模式*/#define HAL_LED_DEFAULT_MAX_LEDS 4#define HAL_LED_DEFAULT_DUTY_CYCLE 5#define HAL_LED_DEFAULT_FLASH_COUNT 50#define HAL_LED_DEFAULT_FLASH_TIME 1000二、初始化LED灯可以看到main()函数进来,后HAL_BOARD_INIT()进行了系统时钟的初始化,还将LED灯配置为输出状态。
星形网络和树型网络可以看成是网状网络的一个特殊子集,所以接下来分析如何组建一个Zigbee网状网络。
组建一个完整的Zigbee网络分为两步:第一步是协调器初始化一个网络;第二步是路由器或终端加入网络。
加入网络又有两种方法,一种是子设备通过使用MAC层的连接进程加入网络,另一种是子设备通过与一个先前指定的父设备直接加入网络。
一、协调器初始化网络协调器建立一个新网络的流程如图1所示。
图1 协调器建立一个新网络1、检测协调器建立一个新的网络是通过原语NLME_NETWORK_FORMATION.request发起的,但发起NLME_NETWORK_FORMATION.request原语的节点必须具备两个条件,一是这个节点具有ZigBee协调器功能,二是这个节点没有加入到其它网络中。
任何不满足这两个条件的节点发起建立一个新网络的进程都会被网络层管理实体终止,网络层管理实体将通过参数值为INVALID_REQUEST的NLME_NETWORK_FORMATION.confirm的原语来通知上层这是一个非法请求。
2、信道扫描协调器发起建立一个新网络的进程后,网络层管理实体将请求MAC子层对信道进行扫描。
信道扫描包括能量扫描和主动扫描两个过程。
首先对用户指定的信道或物理层所有默认的信道进行一个能量扫描,以排除干扰。
网络层管理实体将根据信道能量测量值对信道进行一个递增排序,并且抛弃能量值超过了可允许能量值的信道,保留可允许能量值内的信道等待进一步处理。
接着在可允许能量值内的信道执行主动扫描,网络层管理实体通过审查返回的PAN描述符列表,确定一个用于建立新网络的信道,该信道中现有的网络数目是最少的,网络层管理实体将优先选择没有网络的信道。
如果没有扫描到一个合适的信道,进程将被终止,网络层管理实体通过参数仠为STARTUP_FAILURE的NLME_NETWORK_FORMATION.confirm的原语来通知上层初始化启动网络失败。
ZigBee协议栈初始化网络启动流程ZigBee的基本流程:由协调器的组网(创建PAN ID),终端设备和路由设备发现网络以及加入网络。
基本流程:main()->osal_init_system()->osalInitTasks()->ZDApp_Init(),进协议栈初始化函数ZDApp_Init()。
1.1 进入程序入口main()。
ZMain.c中C++ Codeint main( void ){// Turn off interruptsosal_int_disable( INTS_ALL );// Initialization for board related stuff such as LEDsHAL_BOARD_INIT();// Make sure supply voltage is high enough to runzmain_vdd_check();// Initialize board I/OInitBoard( OB_COLD );// Initialze HAL driversHalDriverInit();// Initialize NV Systemosal_nv_init( NULL );// Initialize the MACZMacInit();// Determine the extended addresszmain_ext_addr();// Initialize basic NV itemszgInit();#ifndef NONWK// Since the AF isn't a task, call it's initialization routine afInit();#endif// Initialize the operating systemosal_init_system();// Allow interruptsosal_int_enable( INTS_ALL );// Final board initializationInitBoard( OB_READY );// Display information about this devicezmain_dev_info();/* Display the device info on the LCD */#ifdef LCD_SUPPORTEDzmain_lcd_init();#endif#ifdef WDT_IN_PM1/* If WDT is used, this is a good place to enable it. */ WatchDogEnable( WDTIMX );#endifosal_start_system(); // No Return from herereturn 0; // Shouldn't get here.} // main()1.2 给任务添加IDsapi.c中C++ Codevoid osalInitTasks( void ) //为各自进程添加ID 用于任务的查找{uint8 taskID = 0;tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));macTaskInit( taskID++ );nwk_init( taskID++ );Hal_Init( taskID++ );//硬件抽象层初始化#if defined( MT_TASK )MT_TaskInit( taskID++ );#endifAPS_Init( taskID++ );ZDApp_Init( taskID++ );//判断如果协调器节点建立网络、如果终端节点加入网络SAPI_Init( taskID );}1.3 初始化ZigBee协议栈网络ZDApp.cC++ Codevoid ZDApp_Init( uint8 task_id ){// Save the task ID// Initialize the ZDO global device short address storageZDAppNwkAddr.addrMode = Addr16Bit;ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.// Check for manual "Hold Auto Start"//检测到有手工设置HAL_KEY_SW_1则会设置devState = DEV_HOLD,从而避开网络初始化ZDAppCheckForHoldKey();// Initialize ZDO items and setup the device - type of device to create.ZDO_Init(); //通过判断预编译来开启一些函数功能// Register the endpoint description with the AF// This task doesn't have a Simple description, but we still need// to register the endpoint.afRegister( (endPointDesc_t *)&ZDApp_epDesc );#if defined( ZDO_USERDESC_RESPONSE )ZDApp_InitUserDesc();#endif // ZDO_USERDESC_RESPONSE// Start the device?if ( devState != DEV_HOLD ){ZDOInitDevice( 0 );}else{// Blink LED to indicate HOLD_STARTHalLedBlink ( HAL_LED_4, 0, 50, 500 );}} /* ZDApp_Init() */如果设置devState为DEV_HOLD,则不会执行ZDOInitDevice;反之,系统会调用此函数是设备组网或者入网。
ZigBee智能开关的连接网络教程
步骤一:添加网关,若已经添加本步骤可忽略
手机在华为市场或应用宝中下载并安装对应的APP,打开并登陆后点击的右上角的“+”键添加设备,选择“网关中控”,根据网关类型在右侧区域找到“无线网关(ZigBee)”、“有线网关(ZigBee)”等类型,点击后进入WiFi配置界面。
手机要确保连接对应房间的2.4G 无线wifi网络。
步骤二:添加成功后可在对应的APP“我的家”页面查看该设备,点击新增的ZigBee网关名称,进入设备页面。
该页面的右上角可设置属性,修改设备名称。
点击页面正下方的“添加子设备”,或在“我的家”页面点击的右上角的“+”键添加设备,选择“电工”,点击“开关(ZigBee)”(根据开关类型不同选择。
)
步骤三:长按开关按键7-10秒左右,开关指示灯出现闪烁状态,然后勾选app界面下方的“确认指示灯在快闪”,点击“下一步” ,等待App添加设备完成。
步骤四:成功找到设备后会进入“添加成功”界面,在该界面可以点击设备名称进行重命名,之后点击完成。
配网完成后可在app中查看或控制设备状态。
Zigbee工程启动流程解析1、Zigbee工程启动流程解析初始化流程:main() -> osal_init_system() -> osalInitTasks() -> GenericApp_Init()事件流程: main() -> osal_start_system() -> (tasksArr[idx])( idx, events ) -> GenericApp_ProcessEvent()2、GenericApp_ProcessEvent()中if ( events & SYS_EVENT_MSG ):SYS_EVENT_MSG是协议栈已经定义好的系统事件if ( events & GENERICAPP_SEND_MSG_EVT ):GENERICAPP_SEND_MSG_EVT就是用户自定义的事件事件号是一个16bit的常量,使用独热码(one-hot code)编码,方便进行event的提取,这样一个task中最多可以有16个event,SYS_EVENT_MSG已经占用了0x8000,故自定义的事件只能有16个。
事件提取events & GENERICAPP_SEND_MSG_EVT,事件清除events ^ GENERICAPP_SEND_MSG_EVT。
用户可以自定义系统事件的消息范围为0xE0~0xFF3、AF_INCOMING_MSG_CMD:当模块接收到属于自己的无线数据信息时就会触发消息ZDO_STATE_CHANGE:当网络状态改变时就会触发此消息4、osal_start_timerEx( GenericApp_TaskID,GENERICAPP_SEND_ MSG_EVT,GENERICAPP_SEND_MSG_TIMEOUT )osal_start_timerEx()的作用是启动一系统定时器,当其溢出(GENERICAPP_SEND_MSG_TIMEOUT)时,会触发task (GenericApp_TaskID)的事件(GENERICAPP_SEND_MSG_EVT)。
构建 ZigBee 网络总结概述ZigBee 是一种基于 IEEE 802.15.4 标准的无线通信协议,旨在提供低功耗、低数据率的短距离无线通信解决方案。
ZigBee 网络由一个或多个 ZigBee 设备组成,这些设备通过 ZigBee 协调器进行协调和管理。
本文将探讨构建 ZigBee 网络的关键步骤和注意事项。
步骤一:选择合适的硬件设备构建 ZigBee 网络的第一步是选择合适的硬件设备。
ZigBee 网络的设备分为三类:协调器(Coordinator)、路由器(Router)和终端设备(End Device)。
协调器是网络的主节点,负责组织和管理整个网络。
路由器允许设备之间进行中继和转发数据。
终端设备是网络中的最终节点,负责与其他设备进行通信。
在选择硬件设备时,需要考虑以下因素: - 功耗:如果是低功耗应用,选择低功耗的设备非常重要。
- 通信范围:根据项目需求选择合适的通信范围。
- 可靠性:确保设备的稳定性和可靠性。
- 成本:根据项目预算选择合适的硬件设备。
步骤二:设计网络拓扑结构在ZigBee 网络中,网络拓扑结构的设计非常重要。
常见的拓扑结构包括星型、网状和链状。
不同的拓扑结构适用于不同的应用场景。
星型拓扑结构星型拓扑结构是最简单和最常见的ZigBee 网络拓扑结构。
在星型拓扑结构中,所有设备都通过协调器进行通信。
该拓扑结构适用于需要集中管理的应用,例如家庭自动化系统。
网状拓扑结构网状拓扑结构允许设备之间进行多跳通信,提供了更强大的网络覆盖能力。
在网状拓扑结构中,路由器负责转发数据,并确保数据能够可靠地从源设备传输到目标设备。
该拓扑结构适用于需要大范围通信的应用,例如智能城市和工业自动化系统。
链状拓扑结构链状拓扑结构是一种特殊的网状拓扑结构,它只允许设备之间进行单向通信。
链状拓扑结构适用于需要按序传输数据的应用,例如传感器网络。
在设计网络拓扑结构时,需要考虑以下因素: - 设备位置:根据设备的位置选择合适的拓扑结构。
ZigBee设备⼊⽹流程之关联⽅式ZigBee设备⼊⽹流程ZigBee设备⼊⽹有关联⽅式和直接⽅式两种,我所熟悉的是关联⽅式,这也是最常⽤的⽅式。
关联⽅式step1 设备发出Beacon Request设备会在预先设置的⼏个信道⾥⾯按照指定的顺序逐信道发出这个包,看到Dest PAN ID,Dest Address都是0xFFFF,说明这是个⼴播包,在这些信道⾥⾯的⽹络都会收到它。
step2 route节点发出Beacon回复这个回复⾥⾯有五个关键的值Source PAN ID :回复Beacon的这个设备所处⽹络的PAN IDSource Address:回复Beacon的这个设备所处⽹络的短地址Association Permit:关联许可是否开放Router Capacity:可否接⼊Route节点End Device Capacity:可否接⼊End Device能收到⼊⽹设备发出的Beacon Request的⽹络都会回复Beacon,并且同⼀个⽹络⾥⾯能收到⼊⽹设备Beacon Request的FFD设备都会回复Beacon。
这样⼀来,⼀般⼊⽹设备会受到多个Beacon回复。
那么它会按照下列的顺序,并且结合这帧Beacon的Link Quality来进⾏下⼀步动作:1. ⼊⽹设备⾸先判断Association Permit是否开放,这个是需要协调器发出全⽹⼴播,通知所有route节点这个许可开放了。
2. 如果关联许可是开放的,再根据⾃⼰所属的设备类型来判断Router Capacity、End Device Capacity。
3. 如果可以接⼊,再筛选最佳Link Quality的设备发出Association Request,这个时候就需要⽤Beacon⾥⾯的Source PAN ID和SourceAddress发出⼀个MAC层的单播包。
step3 设备发出Association Requeststep4 route发出Association Responsestep5 秘钥传输step5 Device AnnounceDevice Announce的⼴播数据主要是通知全⽹相关节点有⼀个新设备进来了,给⼤家做个⾃我介绍,⼤家刷新下路由表这类的信息。
ZigBee模块设置1.ZigBee模块F8913D插入配置基板F8913-EVB(注:基板排针接口与模块插针序号正确对应,不可反插。
否则,模块可能烧坏。
)2.用USB连接线,连接配置基板与电脑。
配置基板通过USB口供电。
电脑端打开ZigBeeConfig.exe3.查看端口号,,,(如何查看端口号,详见备注2)4.参数设置,配置步骤如图。
串口波特率:9600校验:无校验停止位:1个停止位是否启用硬件控制流:不勾选调试等级:0AT命令是否回显:不勾选网络号:自行设置(同一网络内模块和网关的网络号必须相同)节点类型:路由 分节点网络地址:自行设置(同一网络内的设备不能有相同网络地址) 透传地址:0 重新自52A8加入网络:勾选 物理信道:26 应用模式: 透传模块设置关键参数说明:网络号:网络号是ZigBee 判断是否在同一个网络的标志,只有网络号相同的设备才会互相组网,互相通信。
节点类型:同一网络,有且仅有一个协调器。
路由具备转发其他模块数据功能,终端不具有该功能。
1 92 34 5 67 8分节点网络地址:即ZigBee 设备本机在网络内的地址标志,协调器不能修改,默认固定为0,路由或终端可设置为非0 的其它数值,一个ZigBee 设备设置完网络地址后这个地址在本网络内就是唯一的,不可再重复加入这个地址的设备。
透传地址:即本ZigBee 设备串口收到的数据要发送的目标ZigBee 设备的分节点网络地址,在透传模式下,只要指定了透传地址,那么本设备发出的数据都会发送给那个分节点网络地址的zibgee 设备。
例如:ZigBee1(分节点网络地址为10)---ZigBee2(分节点网络地址为13)ZigBee1 要把串口收到的数据发给ZigBee2,ZigBee1 的透传地址就要指定为13,ZigBee2 把串口收到的数据发给ZigBee1,那ZigBee2 的透传地址就要设置为10。
物理信道:要互相通信的设备必须设置为一样的信道,推荐使用15,20,25,26 信道,可减少WIFI的干扰。
zigbee技术介绍ZigBee作为用于个人网络的短距离无线通信协议,已变得越来越知名。
Zigbee是一种适用于短距离无线通信的低成本,低功耗,低速的新技术,可以嵌入各种电子设备中。
该技术主要设计用于低速通信网络。
它的最大特点是低功耗和联网功能,尤其是具有路由功能的联网功能。
从理论上讲,ZigBee覆盖的通信领域可以无限扩展。
ZigBe包含3种节点类型,即:协调器,路由节点和终端节点。
●协调器——启动网络和维护网络●路由节点——转发数据包●终端节点——发送和接收数据。
在实际的Zigbee网络中,仅支持两种无线设备:全功能设备和简化功能设备。
FFD可以提供所有IEEE802.15.4协议服务,不仅可以发送和接收数据,还可以具有路由功能;最终节点负责收集数据,然后将其发送到协调点或路由节点进行处理。
这三种类型的节点使ZigBee支持三种网络拓扑:星形结构,树形结构和网状结构Zigbee协议无线通信技术的特点:●低速率:数据传输速率在10Kb/s〜250Kb/s之间●低功耗:在低功耗待机模式下,两节普通5号电池可以使用6到24个月●成本低:Zigbee数据传输速率低,协议简单,大大降低了成本●网络容量大:网络可容纳65,000个设备●短延迟:典型的搜索设备延迟为30ms,睡眠激活延迟为15ms,活动设备通道访问延迟为15ms。
数据安全性:Zigbee提供数据完整性检查和声音功能,采用AES-128加密算法,并且每个应用程序都可以灵活确定其安全属性基于ZigBee技术的应用数字家庭ZigBee模块可以安装在电视,门禁系统,空调系统和其他家用电器中。
通过ZigBee终端设备,可以收集各种家庭信息并将其传输到中央控制设备,或者可以使用远程控制来实现远程控制的目的,从而提供家庭生活自动化,联网和智能化。
自动抄表读数通过ZigBee网络直接发送到提供天然气,水和电的公司。
利用ZigBee技术,天然气或水电公司可以直接向用户发送用水、用电、用气等信息,十分方便。
Zigbee简介:Zigbee网络通常由三种节点构成:z协调器(Coordinator):用来创建一个Zigbee网络,并为最初加入网络的节点分配地址,每个Zigbee网络需要且只需要一个Coordinator.z路由器(Router):也称为Zigbee全功能节点,可以转发数据,起到路由的作用,也可以收发数据,当成一个数据节点,还能保持网络,为后加入的节点分配地址.z终端节点(End Device):通常定义为电池供电的低功耗设备,通常只周期性发送数据。
或者通过休眠按键控制节点的休眠或工作。
注意:三种Zigbee节点的P ANID在相同的情况下,可以组网并且互相通讯(上电即组网,不需要人为干预)。
这样可以通过P ANID区分zigbee网络,在同一个区域内,可以同时并存多个zigbee网络,互相不会干扰。
Panid设置见下。
管脚定义:z P1.5:休眠键,输入脚,p1.5拉高时,休眠有效。
模块如果是Cornidator、Router 时此脚无效,只有模块是Enddevice时,此脚才有效,如果不需要休眠功能,则此脚与GND连接。
z p1.7:Set键,输入脚,p1.7拉高时候,设置功能有效,平时模块处于数据收发状态时,此引脚应为低电平,具体设置功能见下节z p2.0 网络连接状态灯,输出脚,模块如果是Router或Enddevice时,此按键表明当前模块是否入网,高电平表明入网,低电平表明没有入网。
z p0.2:Rx,与外置MCU的Tx连接z P0.3:Tx,与外置MCU的Rx连接z GND:电源地z VCC:电源3.3V用户在使用时候,可以根据自己需要选择引脚。
最简单的情况是只使用Rx、Tx.、GND、VCC四个脚,但需要将P1.5(休眠键)、P1.7(设置键)接地。
P2.0(网络连接状态)悬空。
当P1.7为高,通过串口对模块进行设置,数据格式如下(以下数据均为16进制):说明:模块处于设置状态时,波特率固定为38400.即P1.7为高时,模块波特率为38400;P1.7为低时,波特率为设置的波特率,波特率设置见下面命令。
记录几个问题:***********************************1、有关设备的启动模式选项:(有待完善):非自动启动模式HOLD_AUTO_START:HOLD_AUTO_START is a compile option that will surpres s ZDApp from starting the device and wait for the application to start the device.不通过ZDApp(具体讲是ZDOInitDevice())而是等待应用程序来开启设备并初始化建立/加入网络.软启动模式SOFT_START:SOFT_START is a compile option that allows the device to start as a c oordinator if one isn't found. Otherwise, the device will start as a router.如果没有协调器则作为协调器启动,有则作为路由器启动.自动启动模式:个人认为就是自动通过ZDApp(具体讲是ZDOInitDevice())来开启设备并初始化建立/加入网络,设备的逻辑类型由所携带的配置文件来决定.**********************************************************************2、三种逻辑类型节点的配置文件协调器:f8wCoord.cfg配置文件中同时编译了路由功能RTR_NWK和协调器功能ZDO_COORDINATOR/* Common To All Applications */-DCPU32MHZ // CC2430s Run at 32MHz-DFORCE_MAC_NEAR // MAC code in NEAR-DROOT=__near_func // MAC/ZMAC code in NEAR-DMAC_CFG_APP_PENDING_QUEUE=TRUE/* Coordinator Settings */-DZDO_COORDINATOR // Coordinator Functions-DRTR_NWK // Router Functions/* Optional Settings */-DBLINK_LEDS // LED Blink Functions/* Compiler keywords */-DCONST="const __code"-DGENERIC=__generic // Ptr declaration路由器:f8wRouter.cfg配置文件中编译了路由功能RTR_NWK/* Common To All Applications */-DCPU32MHZ // CC2430s Run at 32MHz-DFORCE_MAC_NEAR // MAC code in NEAR-DROOT=__near_func // MAC/ZMAC code in NEAR-DMAC_CFG_APP_PENDING_QUEUE=TRUE/* Router Settings */-DRTR_NWK // Router Functions/* Optional Settings */-DBLINK_LEDS // LED Blink Functions/* Compiler keywords */-DCONST="const __code"-DGENERIC=__generic // Ptr declaration终端:f8wEdev.cfg配置文件中没有编译这两个功能./* Common To All Applications */-DCPU32MHZ // CC2430s Run at 32MHz-DFORCE_MAC_NEAR // MAC code in NEAR-DROOT=__near_func // MAC/ZMAC code in NEAR/* Optional Settings */-DMAC_OPT_FFD=0 // ZigBee RFD-DBLINK_LEDS // LED Blink Functions/* Compiler keywords */-DCONST="const __code"-DGENERIC=__generic // Ptr declaration协调器的配置文件只比路由器配置文件多编译了个-DZDO_COORDINATOR // Coordinator Functions 如果同时编译非自动启动模式HOLD_AUTO_START和软启动模式SOFT_START,设备可以通过应用程序来选择成为路由器或协调器.当然这个设备所带的配置文件应为f8wCoord.cfg.对于设备携带的是何种配置文件,可以打开project->options->c/c++compiler->extraOptions选项查看.比如SampleApp中的DemoEB,同时编译了SOFT_START和HOLD_AUTO_START,貌似下载进去后通过外部跳线来选择是当协调器还是路由器,不过这部分程序已经被注销了.当然的,DemoEB所带的配置文件应该是同时编译了路由功能和协调器功能的f8wCoord.cfg,即编译了RTR_NWK和ZDO_COORDINAT OR.再比如SimpleApp中两个实验中的灯与开关实验.开关设备SimpleSwitchEB无论按K1还是K2都是作为终端设备的,其预编译选项只有HOLD_AUTO_START而没有SOFT_START,配置文件为f8wEndev.cfg,没有协调器和路由功能;而灯设备SimpleControllerEB按键K1则作为协调器,按K2则作为路由器,其预编译选项同时编译了SOFT_START和HOLD_AUTO_START,配置文件为f8wCoord.cfg,即编译了RTR _NWK和ZDO_COORDINATOR. SimpleApp中传感器实验也一样,只能作为终端设备的SimpleSensorEB 预编译了HOLD_AUTO_START,配置文件为f8wEndev.cfg;而SimpleCollectorEB按K1作为协调器按K2作为路由器,预编译了SOFT_START和HOLD_AUTO_START,配置文件f8wCoord.cfg. 可以通过project ->options->c/c++compiler->extraOptions选项查看,也可由workspace看出,如下:作为终端的开关节点与作为协调器/路由器的灯节点:空白的文件表示没有包含在这个workspace中.作为终端的传感器节点与作为协调器、路由器的中心收集节点:空白的文件表示没有包含在这个workspace 中.**********************************************************************3、有两种方式来设置非自动启动模式:Hold Auto Start(1)、手工方式:在ZDApp_Init()函数中有个ZDAppCheckForHoldKey();(// Check for manual(手工的) "Hold Auto Star t").来看下这个函数:********************void ZDAppCheckForHoldKey( void ){#if (defined HAL_KEY) && (HAL_KEY == TRUE)//直接通过读取按键来判断是否需要采用HOLD_AUTO_START 模式,如果发现//SW_1处于按下(普通按键?)/向上(Joystick up)状态,则设置devState 为DEV_HOLD;// Get Keypad directly to see if a HOLD_START is needed.// Hold down the SW_BYPASS_START key (see OnBoard.h)// while booting to avoid starting up the device.if ( HalKeyRead () == SW_BYPASS_START) //HAL_KEY_SW_1{// Change the device state to HOLD on start updevState = DEV_HOLD;}#endif // HAL_KEY}*********************可以看到如果手工使HAL_KEY_SW_1置位状态,则会设置devState = DEV_HOLD.这里HAL_KEY的初始化见hal_board_cfg.h:#ifndef HAL_KEY#define HAL_KEY TRUE#endif而对SW_BYPASS_START的初始化见OnBoard.h:// These Key definitions are unique to this development system.// They are used to bypass functions when starting up the device.//这些键的定义仅适用于本应用例子,可以在设备启动时避开一些功能://避开网络层的NV存储和避开网络初始化#define SW_BYPASS_NV HAL_KEY_SW_5 // Bypass Network layer NV restore#define SW_BYPASS_START HAL_KEY_SW_1 // Bypass Network initialization因此避开网络层NV存储也可以通过手工方式来完成.(2)、预编译方式:project->options->c/c++compiler->preprocessor->defined symbols下编译选项:HOLD_AUTO_START SimpleApp例子里四种节点都预编译了HOLD_AUTO_START:非自动启动模式在ZDApp.c中:#if defined( HOLD_AUTO_START )devStates_t devState = DEV_HOLD;#elsedevStates_t devState = DEV_INIT;#endif把devState初始化为DEV_HOLD.以上两种方式最终都会设置devState = DEV_HOLD // Initialized - not started automatically********************************************************************************4、devStartMode和devState的初始化,ZDApp.c中:devStartMode:#if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )// Set the default to coodinatordevStartModes_t devStartMode = MODE_HARD;#elsedevStartModes_t devStartMode = MODE_JOIN; // Assume joining//devStartModes_t devStartMode = MODE_RESUME; // if already "directly joined"// to parent. Set to make the device do an Orphan scan.#endif编译了ZDO_COORDINATOR并且没有编译SOFT_START,则初始化devStartMode = MODE_HARD;其他情况初始化devStartMode = MODE_RESUME;比如SampleApp三种逻辑类型节点.devState:#if defined( HOLD_AUTO_START )devStates_t devState = DEV_HOLD;// Initialized - not started automatically#elsedevStates_t devState = DEV_INIT;// Initialized - not connected to anything#endif预编译了HOLD_AUTO_START,则devState = DEV_HOLD;否则devState = DEV_INIT;********************************************************************************5、存储设备逻辑类型的NV条目ZCD_NV_LOGICAL_TYPE:(1)在ZGlobals.c下的NV条目表ZGlobal Item Table有这么一个条目:static CONST zgItem_t zgItemTable[ ] ={#if defined ( NV_INIT ){ZCD_NV_LOGICAL_TYPE, sizeof(zgDeviceLogicalType), &zgDeviceLogicalType},…………}zgItem_t结构体如下:typedef struct zgItem{uint16 id;uint16 len;void *buf;} zgItem_t;ZDO全局变量zgDeviceLogicalType被初始化为uint8 zgDeviceLogicalType = DEVICE_LOGICAL_TYP E;因此这里全局变量zgDeviceLogicalType的值为NV条目ZCD_NV_LOGICAL_TYPE的值,buf指向zgD eviceLogicalType.如果在应用程序中改变了设备逻辑类型并写入NV条目ZCD_NV_LOGICAL_TYPE中,这时即全局变量zgDeviceLogicalType的值改变了(卡在这步上近两天,因为找不着改变的逻辑状态与设备开启时选择的逻辑状态之间的关系).(2)NV条目ZCD_NV_LOGICAL_TYPE的值有哪些?// Device Logical Type//NV条目ZCD_NV_LOGICAL_TYPE的值包括:(即zgDeviceLogicalType的值)// Values for ZCD_NV_LOGICAL_TYPE (zgDeviceLogicalType)#define ZG_DEVICETYPE_COORDINATOR 0x00#define ZG_DEVICETYPE_ROUTER 0x01#define ZG_DEVICETYPE_ENDDEVICE 0x02#define ZG_DEVICETYPE_SOFT 0x03(3)对于DEVICE_LOGICAL_TYPE的值各逻辑类型设备初始化如下:// Device Logical Type//zgDeviceLogicalType = DEVICE_LOGICAL_TYPE#if defined ( SOFT_START )#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_SOFT //可选择类型#elif defined( ZDO_COORDINATOR )#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_COORDINATOR //协调器#elif defined (RTR_NWK)#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ROUTER //路由器#else#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ENDDEVICE //终端#endif如果编译了SOFT_START,则初始化DEVICE_LOGICAL_TYPE=ZG_DEVICETYPE_SOFT,即zgDevice LogicalType = ZG_DEVICETYPE_SOFT;如果没有编译SOFT_START但编译了ZDO_COORDINATOR,则初始DEVICE_LOGICAL_TYPE = ZG_DEVICETYPE_COORDINATOR,即zgDeviceLogicalType=Z G_DEVICETYPE_COORDINATOR;路由器和终端类似.********************************************************************************6、启动设备都要通过ZDApp_event_loop()函数调用ZDO_StartDevice()对ZDO_NETWORK_INIT事件的处理:ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );这里对于设备逻辑类型的传递参数ZDO_Config_Node_Descriptor.LogicalType,ZDConfig.c中初始化是这样的:NodeDescriptorFormat_t ZDO_Config_Node_Descriptor ={#if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )NODETYPE_COORDINATOR,//协调器#elif defined (RTR_NWK)NODETYPE_ROUTER, //路由器#elseNODETYPE_DEVICE, //终端设备// Logical Type#endif…………}如果编译了ZDO_COORDINATOR且没有编译SOFT_START,则ZDO_Config_Node_Descriptor.Logical Type的值为NODETYPE_COORDINATOR; 如果没有编译ZDO_COORDINATOR或者编译了SOFT_S TART,且编译了了RTR_NWK,则ZDO_Config_Node_Descriptor.LogicalType的值为NODETYPE_ROU TER(比如simpleApp中灯节点和中心收集节点就是这种情况,因此它俩的ZDO_Config_Node_Descriptor. LogicalType被初始化为NODETYPE_ROUTER,当然当通过外部按键选择设备逻辑类型为协调器时,会通过内部程序把这个值改为NODETYPE_COORDINATOR,这点后面再作记录);剩下情况为NODETYPE_D EVICE.********************************************************************************7、simpleApp中的按键完成两个功能:设置设备的逻辑类型(ZC/ZR/ED) 和设置设备的启动方式(ZCD_STARTOPT_AUTO_START),然后写入相应的NV条目ZCD_NV_LOGICAL_TYPE和ZCD_NV_STARTUP_OPTION.ZDApp_Init()是先于应用任务如SampleApp_Init()初始化的.ZCD_NV_STARTUP_OPTION值包括:// These are bit weighted - you can OR these together.// Setting one of these bits will set their associated NV items// to code initialized values.#define ZCD_STARTOPT_DEFAULT_CONFIG_STATE 0x01 //默认配置#define ZCD_STARTOPT_DEFAULT_NETWORK_STATE 0x02 //默认网络状态#define ZCD_STARTOPT_AUTO_START 0x04 //自动启动#define ZCD_STARTOPT_CLEAR_CONFIG ZCD_STARTOPT_DEFAULT_CONFIG_STATE //清除配置=默认配置#define ZCD_STARTOPT_CLEAR_STATE ZCD_STARTOPT_DEFAULT_NETWORK_STATE //清除状态=默认网络状态ZCD_NV_LOGICAL_TYPE值参见4.******************************************************************************8、网络状态的类型://设备的网络状态为恢复的网络状态#define ZDO_INITDEV_RESTORED_NETWORK_STATE 0x00//网络状态初始化,即设备的网络状态为新的网络状态.可能意味着ZCD_NV_STARTUP_OPTION不能恢复,或没有任何网络状态恢复#define ZDO_INITDEV_NEW_NETWORK_STATE 0x01//复位前,网络重返选项为TRUE,因此该设备在网络中没有启动(仅一次),下次调用该函数将启动.#define ZDO_INITDEV_LEAVE_NOT_STARTED 0x02********************************************************************************9、NV条目ZCD_NV_STARTUP_OPTION初始化******************************************************************************** ******************************************************************************** ********************************************************************************分析一:(错误,见分析二;暂不删除)NV条目的初始化在zgInit()中.main()函数中:***************// Initialize basic NV items/*初始化NV条目*/zgInit();***************************************** @fn zgInit** @brief** Initialize the Z-Stack Globals. If an item doesn't exist in* NV memory, write the system default into NV memory. But if* it exists, set the item to the value stored in NV memory.** NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicate* that the Config state items (zgItemTable) need to be* set to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE). The*** @param none** @return ZSUCCESS if successful, NV_ITEM_UNINIT if item did not* exist in NV, NV_OPER_FAILED if failure.*/uint8 zgInit( void ){uint8 i = 0;uint8 setDefault = FALSE;// Do we want to default the Config state valuesif ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01 {setDefault = TRUE;}#if 0 //正常情况下被禁止来节省NV空间// Enable this section if you need to track the number of resets// This section is normally disabled to minimize "wear" on NV memory…………#endif// Initialize the Extended PAN ID as my own extended address//扩展地址ZMacGetReq( ZMacExtAddr, zgExtendedPANID );#ifndef NONWK// Initialize the Pre-Configured Key to the default keyosal_memcpy( zgPreConfigKey, defaultKey, SEC_KEY_LEN ); // Do NOT Change!!!#endif // NONWK//----------------------------------初始化各NV配置状态条目值//初始化NV条目表zgItemTable[ ].如果setDefault=TRUE,则设置为默认//值;否则读取原先存储在里面的值.while ( zgItemTable[i].id != 0x00 ){// Initialize the itemzgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault );// Move on to the next itemi++;}//----------------------------------// Clear the Config State default//如果NV条目ZCD_NV_STARTUP_OPTION的ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位为1if ( setDefault ){//把ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位清0zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE ); }return ( ZSUCCESS );}****************************************NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicatethat the Config state items (zgItemTable) need to beset to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE).开启选项表明了各NV配置状态条目(zgItemTable表中的即为NV配置状态条目)需要被设置为默认值. setDefault的值取决于zgReadStartupOptions(),而这个函数记取的正是ZCD_NV_STARTUP_OPTION的值.****************************************// Reads the ZCD_NV_STARTUP_OPTION NV Item.uint8 zgReadStartupOptions( void ){// Default to Use Config State and Use Network Stateuint8 startupOption = 0;// This should have been done in ZMain.c, but just in case.if ( osal_nv_item_init( ZCD_NV_STARTUP_OPTION,sizeof(startupOption),&startupOption ) == ZSUCCESS ){// Read saved startup control//NV条目ZCD_NV_STARTUP_OPTION值读到startupOptionosal_nv_read( ZCD_NV_STARTUP_OPTION,0,sizeof( startupOption ),&startupOption);}return ( startupOption );}********************************************************************************* @fn zgItemInit()** Initialize a global item. If the item doesn't exist in NV memory,* write the system default (value passed in) into NV memory. But if* it exists, set the item to the value stored in NV memory.** Also, if setDefault is TRUE and the item exists, we will write* the default value to NV space.** @param id - item id* @param len - item len* @param buf - pointer to the item* @param setDefault - TRUE to set default, not read* TRUE则设置为默认值而不读取原先的值* @return ZSUCCESS if successful, NV_ITEM_UNINIT if item did not * exist in NV, NV_OPER_FAILED if failure.*/static uint8 zgItemInit( uint16 id, uint16 len, void *buf, uint8 setDefault ) {uint8 status;// If the item doesn't exist in NV memory, create and initialize// it with the value passed in.status = osal_nv_item_init( id, len, buf ); //NV条目读写前要进行条目的初始化if ( status == ZSUCCESS ){if ( setDefault ) //条目存在,setDefault=TRUE,设置为默认值{// Write the default value back to NVstatus = osal_nv_write( id, 0, len, buf );}else//条目存在,setDefault=FALSE,读取原先存储的值{// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );}}return (status);}****************************************zgReadStartupOptions()中:Default to Use Config State and Use Network State说明采用默认的配置和网络状态,那设备初始启动时ZCD_NV_STARTUP_OPTION值应该是默认配置标志位和默认网络标志位都置1,两者相或0x03(见7),但还没能找到究竟哪里进行过初始化.回到zgInit():(1)setDefault = FALSE;(2)读取ZCD_NV_STARTUP_OPTION值如果ZCD_STARTOPT_D EFAULT_CONFIG_STATE标志位为1,设置setDefault = TRUE;(3)初始化NV配置状态条目表zgItemTa ble[ ]中各条目为默认值(4)清除ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位.至于清除ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位,我想如果用户不改变ZCD_NV_STAR TUP_OPTION条目的值,设备每次重启都会将NV条目表中各项初始化为默认值:比如SampleApp例子,每次设备重启配置状态都为默认值.而如果用户改变了ZCD_NV_STARTUP_OPTION的值,比如SimpleApp 中:zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );startOptions = ZCD_STARTOPT_AUTO_START;zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );zb_SystemReset();每次按键后都会先把设备逻辑状态写入NV中(准备下次启动设备时以保存在这个NV条目里的逻辑类型来启动),然后把ZCD_NV_STARTUP_OPTION设为ZCD_STARTOPT_AUTO_START 0x04,则下面两个标志位都为0.ZCD_STARTOPT_DEFAULT_CONFIG_STATE 0x01 //默认配置状态:存储配置信息ZCD_STARTOPT_DEFAULT_NETWORK_STATE 0x02 //默认网络状态:存储网络信息这样系统重启进入zgInit()后:if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01{setDefault = TRUE;}ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位不为1,setDefault保持FALSE,因而进入NV配置状态条目初始化zgItemInit()时,执行的是:else //条目存在,setDefault=FALSE,读取原先存储的值{// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );}这样把上次存储在NV条目ZCD_NV_LOGICAL_TYPE中的设备逻辑类型值读出作为此次NV条目ZCD _NV_LOGICAL_TYPE的值.问题是,还未确定开启选项ZCD_NV_STARTUP_OPTION的值初始化的地方.纠结中,待解决……************************************************************************************************************************************************************************************************************************************************分析二:@@@ZCD_NV_STARTUP_OPTION最开始在osal_nv_item_init()中初始化,初始化为0***************************************** @fn zgInit** @brief** Initialize the Z-Stack Globals. If an item doesn't exist in* NV memory, write the system default into NV memory. But if* it exists, set the item to the value stored in NV memory.** NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicate* that the Config state items (zgItemTable) need to be* set to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE). The*** @param none** @return ZSUCCESS if successful, NV_ITEM_UNINIT if item did not* exist in NV, NV_OPER_FAILED if failure.*/uint8 zgInit( void ){uint8 i = 0;uint8 setDefault = FALSE;// Do we want to default the Config state valuesif ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE ) // 0x01{setDefault = TRUE;}#if 0 //正常情况下被禁止来节省NV空间// Enable this section if you need to track the number of resets// This section is normally disabled to minimize "wear" on NV memory…………#endif// Initialize the Extended PAN ID as my own extended address//扩展地址ZMacGetReq( ZMacExtAddr, zgExtendedPANID );#ifndef NONWK// Initialize the Pre-Configured Key to the default keyosal_memcpy( zgPreConfigKey, defaultKey, SEC_KEY_LEN ); // Do NOT Change!!!#endif// NONWK//----------------------------------初始化各NV配置状态条目值//初始化NV条目表zgItemTable[ ].如果setDefault=TRUE,则设置为系统默认//值;否则设置为原先存储在里面的值.while ( zgItemTable[i].id != 0x00 ){// Initialize the itemzgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault );// Move on to the next itemi++;}//----------------------------------// Clear the Config State default//如果NV条目ZCD_NV_STARTUP_OPTION的ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位为1if ( setDefault ){ //把ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位清0zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE ); }return ( ZSUCCESS );}****************************************首先来看下zgReadStartupOptions():****************************************// Reads the ZCD_NV_STARTUP_OPTION NV Item.uint8 zgReadStartupOptions( void ){// Default to Use Config State and Use Network Stateuint8 startupOption = 0;// This should have been done in ZMain.c, but just in case.//能返回ZSUCCESS,说明条目已经存在;当然,如果不存在,还是在osal_nv_item_init //中创建条目并设置为startupOption的值.if ( osal_nv_item_init( ZCD_NV_STARTUP_OPTION,sizeof(startupOption),&startupOption ) == ZSUCCESS ){// Read saved startup control//NV条目ZCD_NV_STARTUP_OPTION值读到startupOptionosal_nv_read( ZCD_NV_STARTUP_OPTION,0,sizeof( startupOption ),&startupOption);}return ( startupOption );}****************************************这里读取ZCD_NV_STARTUP_OPTION NV值,再来看下osal_nv_item_init(): ***************************************** @fn osal_nv_item_init** @brief If the NV item does not already exist, it is created and* initialized with the data passed to the function, if any.* This function must be called before calling osal_nv_read() or* osal_nv_write().如果条目不存在,则创建并初始化为传递过来的值.如果存在,直接返回ZSUCCESS.这个必须在read和write之前调用.** @param id - Valid NV item Id.* @param len - Item length.* @param *buf - Pointer to item initalization data. Set to NULL if none.* 条目初始化时的值.* @return NV_ITEM_UNINIT - Id did not exist and was created successfully. * ZSUCCESS - Id already existed, no action taken.* NV_OPER_FAILED - Failure to find or create Id.*/uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf )/* Global fail flag for fail due to low bus voltage has less impact on code* size than passing back a return value all the way from the lowest level.*/failF = FALSE;// ZCD_NV_EXTADDR is maintained without an osalNvHdr_t, so it is always alreadyinitialized.//条目已经存在,则直接返回ZSUCCESSif ( (id == ZCD_NV_EXTADDR) || (findItem( id ) != OSAL_NV_ITEM_NULL) ){return ZSUCCESS; //只有条目已经存在了,才会返回ZSUCCESS}//条目不存在,则通过initItem创建条目并初始化为buf传递过来的值else if ( initItem( TRUE, id, len, buf ) ){if ( failF ){(void)initNV(); // See comment at the declaration of failF.return NV_OPER_FAILED;}else{return NV_ITEM_UNINIT;}}else{return NV_OPER_FAILED;}}****************************************在zgReadStartupOptions()中调用了osal_nv_item_init( ZCD_NV_STARTUP_OPTION,sizeof(startupOpti on),&startupOption );其中startupOption = 0;一开始并不存在ZCD_NV_STARTUP_OPTION这个NV 条目,因而这里会创建NV条目ZCD_NV_STARTUP_OPTION并初始化为startupOption传递过来的值0.成功则返回NV_ITEM_UNINIT,因而不会调用zgReadStartupOptions()的osal_nv_read( ZCD_NV_STAR TUP_OPTION,0,sizeof( startupOption ),&startupOption),最后返回startupOption=0.回到zgInit(),setDefa ult = FALSE保持不变,然后进入NV配置状态条目初始化zgItemInit( zgItemTable[i].id, zgItemTable[i]. len, zgItemTable[i].buf, setDefault ),注意此时传递进去的值zgItemTable[i].buf已经定义为系统默认值(可以查看ZGlobals.c的NWK GLOBAL VARIABLES栏).看下zgItemInit():***************************************** @fn zgItemInit()** @brief* Initialize a global item. If the item doesn't exist in NV memory,* write the system default (value passed in) into NV memory. But if* it exists, set the item to the value stored in NV memory.* 条目不存在,创建并设置为传递过来的值;存在则设置为原存储的值.* Also, if setDefault is TRUE and the item exists, we will write* the default value to NV space.** @param id - item id* @param len - item len* @param buf - pointer to the item* @param setDefault - TRUE to set default, not read** @return ZSUCCESS if successful, NV_ITEM_UNINIT if item did not* exist in NV, NV_OPER_FAILED if failure.*/static uint8 zgItemInit( uint16 id, uint16 len, void *buf, uint8 setDefault ){uint8 status;// If the item doesn't exist in NV memory, create and initialize// it with the value passed in.status = osal_nv_item_init( id, len, buf );//NV条目读写前都要进行条目的初始化//这里是要status == ZSUCCESS才会执行里面的内容,说明//此NV条目是已经存在的,这样才会返回ZSUCCESSif ( status == ZSUCCESS ){if ( setDefault ) //条目存在,设置为默认值{// Write the default value back to NVstatus = osal_nv_write( id, 0, len, buf );}else //条目存在,读取原先存储的值{// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );}}return (status);}****************************************可以看到同样调用了osal_nv_item_init( id, len, buf ),如果条目不存在则创建并设置为buf传递过来的值,如果存在则直接返回ZSUCCESS.一开始各NV配置状态条目不存在,因而这里创建各条目并设置为buf传递过来的值(系统定义的默认值).回到zgInit(),各NV配置状态条目初始化完成后,返回ZSUCCESS.因为此时setDefault保持FALSE不变,因而不会执行zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_ST ARTOPT_DEFAULT_CONFIG_STATE );以上为NV条目的初始化流程.初始化时ZCD_NV_STARTUP_OPTION=0,即把各NV条目初始化为系统默认的值.想到zgReadStartupOptions()开头那句话:// Default to Use Config State and Use Network Stateuint8 startupOption = 0;startupOption即为ZCD_NV_STARTUP_OPTION的初始化值.(###)如果,这样初始化完成后,又在应用程序中改变ZCD_NV_STARTUP_OPTION的值,改为ZCD_START OPT_DEFAULT_CONFIG_STATE.然后zb_SystemReset()重启系统.进入zgInit(),首先设置setDefault = FALSE,然后同样进入判断:if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01{setDefault = TRUE;}调用:zgReadStartupOptions()->osal_nv_item_init(),因为此时NV条目ZCD_NV_STARTUP_OPTION已经存在,所以osal_nv_item_init()不会再创建条目并设置为传递过来的值startupOption = 0,而是直接返回ZS UCCESS,因而这时在zgReadStartupOptions()里会调用osal_nv_read( ZCD_NV_STARTUP_OPTION,0,s izeof( startupOption ),&startupOption)读取先前已经存储在条目ZCD_NV_STARTUP_OPTION里的值: ZCD_STARTOPT_DEFAULT_CONFIG_STATE!所以这里设置setDefault = TRUE.然后进入各NV配置状态条目初始化:zgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault );进入zgItemInit(),同样先调用了osal_nv_item_init( id, len, buf ),因为各NV配置状态条目已经存在,所以不再创建而是直接返回ZSUCCESS,所以这次会调用:if ( status == ZSUCCESS ){if ( setDefault ) //条目存在,设置为默认值{// Write the default value back to NVstatus = osal_nv_write( id, 0, len, buf );}else//条目存在,设置为原先存储的值{// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );}}因为setDefault = TRUE,所以重新设置各NV配置状态条目为系统默认值.然后回到zgInit(),最终调用if ( setDefault ){ //把ZCD_STARTOPT_DEFAULT_CONFIG_STATE这位清0zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE ); }这里把ZCD_NV_STARTUP_OPTION条目值重新清0(我上面只假设ZCD_NV_STARTUP_OPTION的值为ZCD_STARTOPT_DEFAULT_CONFIG_STATE 0x01).(###)当然,如果像SampleApp例子没有在应用程序中修改过ZCD_NV_STARTUP_OPTION,最开始启动设备时会创建条目并初始化为0(startupOption = 0),然后创建各NV配置状态条目并初始化为系统默认值.以后的每次设备启动,ZCD_NV_STARTUP_OPTION值为0,setDefault = FALSE,各NV配置状态条目初始化执行的是:else//条目存在,设置为原先存储的值{// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );}读取此前的配置状态.(###)再比如SimpleApp例子,应用程序通过按键改变NV条目ZCD_NV_STARTUP_OPTION以及NV配置状态条目ZCD_NV_LOGICAL_TYPE的值,通过zb_WriteConfiguration()写入NV中,然后zb_SystemRe set()重启系统.进入zgInit(),通过zgReadStartupOptions()读出的ZCD_NV_STARTUP_OPTION值不为ZC D_STARTOPT_DEFAULT_CONFIG_STATE,因而setDefault = FALSE.再进入zgItemInit(),因为条目存在且setDefault = FALSE,所以执行// The item exists in NV memory, read it from NV memorystatus = osal_nv_read( id, 0, len, buf );把条目的值设置为先前存储的值.即在SampleApp例子中,把上次存储在NV条目ZCD_NV_LOGICAL_TY PE中的设备逻辑类型值读出作为此次NV条目ZCD_NV_LOGICAL_TYPE的值.(1)初始ZCD_NV_STARTUP_OPTION=0.如果设备是首次启动,则调用status = osal_nv_item_init( id, len, buf )创建各NV配置状态条目并初始化为系统默认值;如果设备不是首次启动,则调用osal_nv_read( id, 0, len, buf )读取各NV配置状态条目先前存储的值作为本次的值.(2)设置ZCD_NV_STARTUP_OPTION=ZCD_STARTOPT_DEFAULT_CONFIG_STATE.NV条目ZCD_ NV_STARTUP_OPTION已经存在,调用osal_nv_write( id, 0, len, buf )设置各NV配置状态条目为系统默认值.最后把这位清0.(3)设置ZCD_NV_STARTUP_OPTION=ZCD_STARTOPT_AUTO_START.NV条目ZCD_NV_STARTUP _OPTION已经存在,调用osal_nv_read( id, 0, len, buf )读取各NV配置状态条目先前存储的值作为本次的值.********************************************************************************************************************************************************************************************************************************************************************************************************************************。
Zigbee设备加入网络过程---关联加入在一个zigbee协调器设备建立网络后,路由器设备或者终端设备(end device),可以加入协调器建立的网络,具体加入网络有两种方式,一种是通过关联(association)方式,就是待加入的设备发起加入网络,具体实现方式后面讨论,另一中是直接(direct)方式,就是待加入的设备具体加入到那个设备下,作为该设备的子节点,由以前网络中的设备,想待加入的设备作为其子设备决定。
下面重点讨论第一种方式,也是实际中用的最多的方式通过关联方式加入一个网络:加入一个设备,是两个设备的事,即子设备和待定父设备对于子设备,首先子设备调用NLME-NETWORK-DISCOVERY.request 原语,设定待扫描的信道,以及每个信道扫描的时间,网络层收到这个原语,将要求MAC层执行被动或主动扫描。
具体发送到设备外的是一个becon request 帧,当在这个信道中的设备收到该帧,将会发送becon帧,这是子设备通过BEACON-NOTIFY.indication 原语,告知该设备的MAC 层,该becon帧包含了发送该帧的地址信息,以及是否允许其他设备以其子节点的方式加入。
待加入的设备,在网络层,将检查该becon帧协议 ID是否是zigbee ID 。
如果不是,将忽略;如果是,该设备将复制收到每个becon帧的相关信息到其关联表中(neighbor tabl e)。
一旦MAC层完成了扫描,将发送 MLME-SCAN.confirm 原语,告知网络层,网络层将发送NLME-NETWORK-DISCOVERY.confirm 原语,告知应用层。
应用层收到该原语,应用层将根据情况,要么重新要求扫描,或者从关联表中选择所发现的网络加入。
调用NLME-JOIN.request 原语,原语中各个参数的设置参看协议(可以在本站下载栏找到)非常容易。
如果在关联表中找不到合适的准父节点,将调用原语告知应用层,如果由多个设备可以满足要求,将选择到协调器节点深度最低的设备,如果有几个设备的深度相同,且都是最小深度,将从中随机选择一个。
【转自小峰博客】协调器的启动【自动模式】使用的协议栈版本信息: ZigBee2019\ZStack-1.4.3-1.2.1Zigbee网络设备启动流程—协调器(自启动模式)—以SampleApp的协调器为例.1、协调器预编译信息通过project->options->c/c++compiler->extraOptions可以看到协调器所带的配置文件为:-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wCoord.cfg-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wConfig.cfg即编译了ZDO_COORDINATOR和RTR_NWK.通过project->options->c/c++compiler->Preprocessor->Defined symbols可以看到协调器预编译包含了:CC2430EB; ZTOOL_P1; MT_TASK; LCD_SUPPORTED=DEBUG; MANAGED_SCAN没有编译HOLD_AUTO_START和SOFT_START.2、具体流程main()->osal_init_system()->osalInitTasks()->ZDApp_Init()进入ZDApp_Init()函数:**************************************void ZDApp_Init( byte task_id ){uint8 capabilities;// Save the task IDZDAppTaskID = task_id;// Initialize the ZDO global device short address storageZDAppNwkAddr.addrMode = Addr16Bit;ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR; //0xFFFE(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.// Check for manual"Hold Auto Start"//检测到有手工设置SW_1则会设置devState = DEV_HOLD,从而避开网络初始化 ZDAppCheckForHoldKey();// Initialize ZDO items and setup the device - type of device to create.ZDO_Init(); //通过判断预编译来开启一些函数功能// Register the endpoint description with the AF// This task doesn't have a Simple description, but we still need// to register the endpoint.afRegister( (endPointDesc_t *)&ZDApp_epDesc );#if defined( ZDO_USERDESC_RESPONSE )ZDApp_InitUserDesc();#endif // ZDO_USERDESC_RESPONSE// set broadcast address mask to support broadcast filteringNLME_GetRequest(nwkCapabilityInfo, 0, &capabilities);NLME_SetBroadcastFilter( capabilities );// Start the device?if ( devState != DEV_HOLD ){ZDOInitDevice( 0 );}/*如果devState=DEV_HOLD,则不会调用ZDOInitDevice()来初始化网络即不组网也不进网.LED4闪烁等待应用程序来开启设备并初始化网络*/else{// Blink LED to indicate HOLD_STARTHalLedBlink ( HAL_LED_4, 0, 50, 500 );}ZDApp_RegisterCBs();}**************************************协调器没有编译HOLD_AUTO_START,也没有手工设置SW_1,初始化devState = DEV_INIT(参见基本问题说明3).因此直接在ZDApp_Init()中进入ZDOInitDevice( 0 )开启设备.**************************************uint8 ZDOInitDevice( uint16 startDelay ){//初始化设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE:新的网络状态.//可能意味着ZCD_NV_STARTUP_OPTION不能恢复,或没有任何网络状态恢复uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;uint16 extendedDelay = 0;devState = DEV_INIT; // Remove the Hold state// Initialize leave control logic//函数读取NV项目ZCD_NV_LEAVE_CTRL的值,ZDApp_LeaveCtrl指向这个值ZDApp_LeaveCtrlInit();// Check leave control reset settingsZDApp_LeaveCtrlStartup( &devState, &startDelay );// Leave may make the hold state come back//以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则//把设备状态设为DEV_HOLDif ( devState == DEV_HOLD )//ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用.return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); // Don't join - (one time).#if defined ( NV_RESTORE )// Get Keypad directly to see if a reset nv is needed.// Hold down the SW_BYPASS_NV key (defined in OnBoard.h)// while booting(引导) to skip past NV Restore.if ( HalKeyRead() == SW_BYPASS_NV )//SW_BYPASS_NV按键处于按下状态时,则避开网络层的NV存储networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; //设备网络状态为新的网络状态else{// Determine if NV should be restored//函数返回的设备网络状态要么是新的网络状态;要么是恢复的网络状态;以此//来确定要不要读取NV里相应条目来恢复网络先前状态networkStateNV = ZDApp_ReadNetworkRestoreState();}//如果设备的网络状态为恢复的网络状态if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ){//恢复设备先前的网络状态参数并且//设置devStartMode = MODE_RESUME!!!!networkStateNV = ZDApp_RestoreNetworkState();}else //如果设备的网络状态为新的网络状态,在下面进行处理{// Wipe out(清除) the network state in NVNLME_InitNV();NLME_SetDefaultNV(); //设置默认NV条目}#endif//如果设备的网络状态为新的网络状态if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE ){//根据预编译来设置设备新的网络状态参数ZDAppDetermineDeviceType();/*!!!!*/// Only delay if joining network - not restoring network stateextendedDelay = (uint16)((NWK_START_DELAY + startDelay)+ (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));}// Initialize device securityZDApp_SecInit( networkStateNV );// Trigger the network startZDApp_NetworkInit( extendedDelay );return ( networkStateNV );}**************************************分两种情况:(1)如果协调器预编译了NV_RESTORE,且函数ZDApp_ReadNetworkRestoreState()返回值为ZDO_INITDEV_RESTORED_NETWORK_STATE,则进入ZDApp_RestoreNetworkState()里设置ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR和devStartMode = MODE_RESUME.****************uint8 ZDApp_RestoreNetworkState( void ){…………// Are we a coordinator//设备的网络状态为恢复的网络状态.则进入这个函数进行恢复//先判断如果短地址为0则设置设备逻辑类型为协调器且devStartMode = MODE_RESUME //否则设置devStartMode = MODE_RESUMEZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();if ( ZDAppNwkAddr.addr.shortAddr == 0 ) //如果短地址是0,即协调器{ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR; //!!!!!}devStartMode = MODE_RESUME; //MODE_RESUME!!!!!!!!…………}****************(2)如果协调器没有预编译NV_RESTORE,networkStateNV==ZDO_INITDEV_NEW_NETWORK_STATE,但由于协调器编译了ZDO_COORDINATOR而没有编译SOFT_START,因此ZDAppDetermineDeviceType()不起作用.因此ZDO_Config_Node_Descriptor.LogicalType和devStartMode这两个关键参数保持初始化时的值:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR(见基本问题说明6)devStartMode = MODE_HARD(见基本问题说明4)对于协调器,这两种情况最终都是确定两个关键设备网络状态参数的值.对本例的SampleApp的协调器,没有编译NV_RESTORE,因此属于情况(2).然后调用ZDApp_NetworkInit()启动网络:****************void ZDApp_NetworkInit( uint16 delay ){if ( delay ){// Wait awhile before starting the deviceosal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );}else{osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );}}****************通过触发ZDAppTaskID的ZDO_NETWORK_INIT事件.来看下对ZDO_NETWORK_INIT 事件的处理:****************UINT16 ZDApp_event_loop( byte task_id, UINT16 events ){…………if ( events & ZDO_NETWORK_INIT ){// Initialize apps and start the networkdevState = DEV_INIT;ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode, DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );// Return unprocessed eventsreturn (events ^ ZDO_NETWORK_INIT);}…………}****************tuzhuke2019-11-08 20:18:11可以看到调用了ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,DEF AULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );这里设备网络状态参数:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATORdevStartMode = MODE_HARD且协调器编译了ZDO_COORDINATOR****************void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ){ZStatus_t ret;ret = ZUnsupportedMode;#if defined(ZDO_COORDINATOR)if ( logicalType == NODETYPE_COORDINATOR ){if ( startMode == MODE_HARD ) //MODE_HARD{devState = DEV_COORD_STARTING; //Started as Zigbee Coordinator//建网ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,zgDefaultStartingScanDuration, beaconOrder,superframeOrder, false );}else if ( startMode == MODE_RESUME ) //MODE_RESUME{// Just start the coordinatordevState = DEV_COORD_STARTING;ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );}else //错误,未知启动模式{#if defined( LCD_SUPPORTED )//HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );ClearScreen();Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);#endif}}#endif // !ZDO_COORDINATOR//#if !defined ( ZDO_COORDINATOR ) || defined( SOFT_START )if ( logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE ){if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) ){devState = DEV_NWK_DISC; //Discovering PAN's to join#if defined( MANAGED_SCAN )ZDOManagedScan_Next();ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC ); #elseret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration ); #endif}else if ( startMode == MODE_RESUME ) //MODE_RESUME 恢复{if ( logicalType == NODETYPE_ROUTER ){ZMacScanCnf_t scanCnf;devState = DEV_NWK_ORPHAN;/* if router and nvram is available, fake successful orphan scan */scanCnf.hdr.Status = ZSUCCESS;scanCnf.ScanType = ZMAC_ORPHAN_SCAN;scanCnf.UnscannedChannels = 0;scanCnf.ResultListSize = 0;nwk_ScanJoiningOrphan(&scanCnf);ret = ZSuccess;}else{devState = DEV_NWK_ORPHAN; //孤儿ret = NLME_OrphanJoinRequest( zgDefaultChannelList,zgDefaultStartingScanDuration );}}else{#if defined( LCD_SUPPORTED )// HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);#endif}}//#endif //!ZDO COORDINATOR || SOFT_STARTif ( ret != ZSuccess )osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );}****************通过参数可知协调器调用NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,zgDefaultStartingScanDuration, beaconOrder,superframeOrder, false )进行网络的组建.而对NLME_NetworkFormationRequest()的调用会产生一个回调函数ZDO_NetworkFormationConfirmCB()(见主要函数说明3),来看下:****************void ZDO_NetworkFormationConfirmCB( ZStatus_t Status ){#if defined(ZDO_COORDINATOR)nwkStatus = (byte)Status;if ( Status == ZSUCCESS ){// LED on shows Coordinator startedHalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );// LED off forgets HOLD_AUTO_STARTHalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);#if defined ( ZBIT )SIM_SetColor(0xd0ffd0);#endifif ( devState == DEV_HOLD ){// Began with HOLD_AUTO_STARTdevState = DEV_COORD_STARTING;}}#if defined(BLINK_LEDS)elseHalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH ); // Flash LED to show failure#endifosal_set_event( ZDAppTaskID, ZDO_NETWORK_START );#endif //ZDO_COORDINATOR}****************如果Status返回ZSUCCESS,建立网络成功,通过一些灯亮来来指示;不成功则通过闪烁灯来指示.最后触发任务ZDAppTaskID的ZDO_NETWORK_START事件,看下对ZDO_NETWORK_START的处理:****************#if defined (RTR_NWK)if ( events & ZDO_NETWORK_START ){ZDApp_NetworkStartEvt();// Return unprocessed eventsreturn (events ^ ZDO_NETWORK_START);}****************调用了ZDApp_NetworkStartEvt()****************void ZDApp_NetworkStartEvt( void ){if ( nwkStatus == ZSuccess )//网络建立成功{// Successfully started a ZigBee networkif ( devState == DEV_COORD_STARTING ){devState = DEV_ZB_COORD;}osal_pwrmgr_device( PWRMGR_ALWAYS_ON );osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );}else //网络建立不成功,则增加能量阀值重新建网.{// Try again with a higher energy threshold !!if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff ){NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); //重新初始化建立网络}else{// Failed to start network. Enter a dormant state (until user intervenes)devState = DEV_INIT;osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );}}}****************如果协调器建立网络成功,则触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT事件.看下对ZDO_STATE_CHANGE_EVT的处理:****************if ( events & ZDO_STATE_CHANGE_EVT ){ZDO_UpdateNwkStatus( devState );// Return unprocessed eventsreturn (events ^ ZDO_STATE_CHANGE_EVT);}****************调用了ZDO_UpdateNwkStatus( devState ),网络状态改变,这个函数会更新和发送信息通知每个注册登记过的应用终端.****************void ZDO_UpdateNwkStatus( devStates_t state ){// Endpoint/Interface descriptor list.epList_t *epDesc = epList;byte bufLen = sizeof(osal_event_hdr_t);osal_event_hdr_t *msgPtr;ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.while ( epDesc ){if ( epDesc->epDesc->endPoint != ZDO_EP ){msgPtr = (osal_event_hdr_t *)osal_msg_allocate( bufLen );if ( msgPtr ){msgPtr->event = ZDO_STATE_CHANGE; // Command IDmsgPtr->status = (byte)state;osal_msg_send( *(epDesc->epDesc->task_id), (byte *)msgPtr ); //发往应用任务}}epDesc = epDesc->nextDesc;}}****************对SampleApp的协调器来说,这里触发应用任务SampleApp_TaskID的ZDO_STATE_CHANGE事件,看下对ZDO_STATE_CHANGE的处理:****************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) ){// Start sending the periodic message in a regular interval.osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );}else{// Device is no longer in the network}break;****************可以看到当协调器建立网络成功,通过回调函数触发应用任务的ZDO_STATE_CHANGE事件,最终开启定时器发送周期信息.3、协调器(自启动模式)—以SampleApp的协调器为例,并假设初始化成功,网络建立成功.程序大致流程:main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->ZDOInitDevice()->ZDApp_NetworkInit->触发ZDAppTaskID的ZDO_NETWORK_INIT->ZDO_StartDevice()->NLME_NetworkFormationRequest()->网络建立成功ZDO_NetworkFormationConfirmCB->触发ZDAppTaskID的ZDO_NETWORK_START->ZDApp_NetworkStartEvt()->触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT->ZDO_UpdateNwkStatus()->触发SampleApp_TaskID的ZDO_STATE_CHANGE->开户周期信息发送的定时器.4、注:(1)自启动模式下SampleApp的终端和路由器总体流程基本一致.(2)以SampleApp为例,ZDO_StartDevice()函数的两个重要参数比较:终端:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICEdevStartMode = MODE_JOIN路由器:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_ROUTER devStartMode = MODE_JOIN协调器:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR devStartMode = MODE_HARD******************************************************************************说明:1、本文为个人学习笔记,纯属个人理解,错误不可避免,仅供参考.随时更新2、细节基本不管,遇到问题再作分析,程序注释为个人原始注释内容,记录有些仓促.3、欢迎交流,转载请注明出处,谢谢!。
Zigbee设备加入网络过程---关联加入在一个zigbee协调器设备建立网络后,路由器设备或者终端设备(end device),可以加入协调器建立的网络,具体加入网络有两种方式,一种是通过关联(association)方式,就是待加入的设备发起加入网络,具体实现方式后面讨论,另一中是直接(direct)方式,就是待加入的设备具体加入到那个设备下,作为该设备的子节点,由以前网络中的设备,想待加入的设备作为其子设备决定。
下面重点讨论第一种方式,也是实际中用的最多的方式通过关联方式加入一个网络:加入一个设备,是两个设备的事,即子设备和待定父设备对于子设备,首先子设备调用NLME-NETWORK-DISCOVERY.request 原语,设定待扫描的信道,以及每个信道扫描的时间,网络层收到这个原语,将要求MAC层执行被动或主动扫描。
具体发送到设备外的是一个becon request 帧,当在这个信道中的设备收到该帧,将会发送becon帧,这是子设备通过BEACON-NOTIFY.indication 原语,告知该设备的MAC 层,该becon帧包含了发送该帧的地址信息,以及是否允许其他设备以其子节点的方式加入。
待加入的设备,在网络层,将检查该becon帧协议 ID是否是zigbee ID 。
如果不是,将忽略;如果是,该设备将复制收到每个becon帧的相关信息到其关联表中(neighbor tabl e)。
一旦MAC层完成了扫描,将发送 MLME-SCAN.confirm 原语,告知网络层,网络层将发送NLME-NETWORK-DISCOVERY.confirm 原语,告知应用层。
应用层收到该原语,应用层将根据情况,要么重新要求扫描,或者从关联表中选择所发现的网络加入。
调用NLME-JOIN.request 原语,原语中各个参数的设置参看协议(可以在本站下载栏找到)非常容易。
如果在关联表中找不到合适的准父节点,将调用原语告知应用层,如果由多个设备可以满足要求,将选择到协调器节点深度最低的设备,如果有几个设备的深度相同,且都是最小深度,将从中随机选择一个。
zi gbee 网络建立过程简介( G1)星形网络和树型网络可以看成是网状网络的一个特殊子集,所以接下来分析如何组建一个Zigbee网状网络。
组建一个完整的Zigbee网络分为两步:第一步是协调器初始化一个网络;第二步是路由器或终端加入网络。
加入网络又有两种方法,一种是子设备通过使用MAC层的连接进程加入网络,另一种是子设备通过与一个先前指定的父设备直接加入网络。
一、协调器初始化网络协调器建立一个新网络的流程如图1所示。
WliE-N ETWORK-FORW MION 啲TMJ^E-SCAN requestMLWE-SDAK confamMLME-SCAN.requestMLWE-SCAN contfni畝外站it PM ID.MUML-5C.1「eqestHL ME ShT cenfinnMLK1E-5TAr(T requestmiE-STARTanfirmNLM:'. NETWCftK-rORMMlON infirmAPL NWK MAC图1协调器建立一个新网络1、检测协调器建立一个新的网络是通过原语NLME_NETWORK_FORMATION.reqi发起的,但发起NLME_NETWORK_FORMATION.reqi原语的节点必须具备两个条件,一是这个节点具有ZigBee协调器功能,二是这个节点没有加入到其它网络中。
任何不满足这两个条件的节点发起建立一个新网络的进程都会被网络层管理实体终止,网络层管理实体将通过参数值为INVALID_REQUES的NLME_NETWORK_FORMATION.con的原语来通知上层这是一个非法请求。
2、信道扫描协调器发起建立一个新网络的进程后,网络层管理实体将请求MAC子层对信道进行扫描。
信道扫描包括能量扫描和主动扫描两个过程。
首先对用户指定的信道或物理层所有默认的信道进行一个能量扫描,以排除干扰。
网络层管理实体将根据信道能量测量值对信道进行一个递增排序,并且抛弃能量值超过了可允许能量值的信道,保留可允许能量值内的信道等待进一步处理。
设备启动准备一、设备类型选择:通过Workspace下拉框选择设备的类型:图1:协调器图2:路由器图3:终端节点协议栈设备类型:#define ZG_DEVICETYPE_COORDINATOR 0x00#define ZG_DEVICETYPE_ROUTER 0x01#define ZG_DEVICETYPE_ENDDEVICE 0x02#define ZG_DEVICETYPE_SOFT 0x03ZG_DEVICETYPE_SOFT说明:可选设备类型。
可以成为路由器、也可以成为协调器,由后面的程序决定。
初始化:对于DEVICE_LOGICAL_TYPE的值各逻辑类型设备初始化如下:// Device Logical Type//zgDeviceLogicalType = DEVICE_LOGICAL_TYPE在ZGlobals.h文件中设备逻辑类型进行了初始化:#if defined ( SOFT_START )#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_SOFT //可选择类型#elif defined( ZDO_COORDINATOR )#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_COORDINATOR //协调器#elif defined (RTR_NWK)#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ROUTER //路由器#else#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ENDDEVICE //终端#endif说明:如果编译了SOFT_START,则初始化设备逻辑类型(DEVICE_LOGICAL_TYPE)为可选择类型(ZG_DEVICETYPE_SOFT)即设备可以作为协调器启动创建一个网络或者作为路由器加入一个已经存在的网络。
原创作者:幻一、协调器网络的建立1、网络的格式化A:初始化的能量阀值为16。
通过设置ZDApp_event_loop任务中的事件ID为ZDO_NETWORK_INIT以便执行B。
B:调用NLME_NetworkFormationRequest函数进行申请,对申请的回应在程序中没对应的回调处理,猜想回调处理在lib库中处理。
在程序找到了两个对申请结果作处理的函数,voidZDO_NetworkFormationConfirmCB( ZStatus_t Status )和voidnwk_Status( uint16 statusCode,uint16 statusValue );在两个函数中都有申请的结果。
如果不成功则nwk_Status先执行如果成功则ZDO_NetworkFormationConfirmCB先执行。
C:系统(lib)调用ZDO_NetworkFormationConfirmCB函数,在函数中通过设置ZDApp_event_loop任务中的事件ID为ZDO_NETWORK_START以便执行D。
D:调用ZDApp_NetworkStartEvt函数,在函数中检测返回的申请结果,如果不成功则能量阀值增加16并将ZDApp_event_loop任务中的事件ID置为ZDO_NETWORK_INIT以便执行B;如果成功则ZDApp_event_loop 任务中的事件ID置为ZDO_STATE_CHANGE_EVT以便执行E。
E:调用ZDO_UpdateNwkStatus函数,函数通过端点表向SampleApp_ProcessEvent任务发送一个ZDO_STATE_CHANGE命令,以便执行F。
F:设置ZDApp_event_loop任务中的事件ID为SAMPLEAPP_SEND_PERIODIC_MSG_EVT以便执行G。
G:发送一个定期信息,并设置ZDApp_event_loop任务中的事件ID 为SAMPLEAPP_SEND_PERIODIC_MSG_EVT以便执行G。
ZigBee协议栈wireless UART模板网络自启动的实现杨打生【摘要】ZigBee控制是将下位机微处理器采集的现场环境和运行信息通过接口电路发送到ZigBee节点,从而控制工业过程的一种无线网络系统。
文中给出了在Freescale ZigBee协议栈wireless UART应用模板中简化网络启动的步骤,同时给出了在源代码中需要修改的内容。
%ZigBee control is a wireless network system which is used to send the spot environment and operating information collected by the lower computer microprocessor to the ZigBee node through the interface circuit, in order to control the industrial processes. There the steps is given to simplify the booting of network in the Freescale ZigBee protocol stack of UARTwireless application template. The details needs to be modified in the source code is pointed out.【期刊名称】《物联网技术》【年(卷),期】2013(000)004【总页数】2页(P45-46)【关键词】ZigBee协议栈;绑定;网络自启动;wireless UART【作者】杨打生【作者单位】内蒙古电子信息职业技术学院,内蒙古呼和浩特 010070【正文语种】中文【中图分类】TN915.650 引言在工业应用领域,ZigBee工业控制的主要原理是下位机利用微处理器采集现场环境和运行信息,并通过接口电路将现场信息发送到ZigBee节点,ZigBee节点通过ZigBee网络发送到ZigBee协调器,协调器将收到的信息转发到上位管理计算机,上位机对采集的信息进行处理,并将相关控制命令通过ZigBee网络发送到下位机。
使用的协议栈版本信息: ZigBee2006\ZStack-1.4.3-1.2.1Zigbee网络设备启动流程—协调器(自启动模式)—以SampleApp的协调器为例.1、协调器预编译信息通过project->options->c/c++compiler->extraOptions可以看到协调器所带的配置文件为:-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wCoord.cfg-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wConfig.cfg即编译了ZDO_COORDINATOR和RTR_NWK.通过project->options->c/c++compiler->reprocessor->Defined symbols可以看到协调器预编译包含了:CC2430EB; ZTOOL_P1; MT_TASK; LCD_SUPPORTED=DEBUG; MANAGED_SCAN没有编译HOLD_AUTO_START和SOFT_START.2、具体流程main()->osal_init_system()->osalInitTasks()->ZDApp_Init()进入ZDApp_Init()函数:**************************************void ZDApp_Init( byte task_id ){uint8 capabilities;// Save the task IDZDAppTaskID = task_id;// Initialize the ZDO global device short address storageZDAppNwkAddr.addrMode = Addr16Bit;ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR; //0xFFFE(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.// Check for manual"Hold Auto Start"//检测到有手工设置SW_1则会设置devState = DEV_HOLD,从而避开网络初始化ZDAppCheckForHoldKey();// Initialize ZDO items and setup the device - type of device to create.ZDO_Init(); //通过判断预编译来开启一些函数功能// Register the endpoint description with the AF// This task doesn't have a Simple description, but we still need// to register the endpoint.afRegister( (endPointDesc_t *)&ZDApp_epDesc );#if defined( ZDO_USERDESC_RESPONSE )ZDApp_InitUserDesc();#endif // ZDO_USERDESC_RESPONSE// set broadcast address mask to support broadcast filteringNLME_GetRequest(nwkCapabilityInfo, 0, &capabilities);NLME_SetBroadcastFilter( capabilities );// Start the device?if ( devState != DEV_HOLD ){ZDOInitDevice( 0 );}/*如果devState=DEV_HOLD,则不会调用ZDOInitDevice()来初始化网络即不组网也不进网.LED4闪烁等待应用程序来开启设备并初始化网络*/else{// Blink LED to indicate HOLD_STARTHalLedBlink ( HAL_LED_4, 0, 50, 500 );}ZDApp_RegisterCBs();}**************************************协调器没有编译HOLD_AUTO_START,也没有手工设置SW_1,初始化devState = DEV_INIT(参见基本问题说明3).因此直接在ZDApp_Init()中进入ZDOInitDevice( 0 )开启设备.**************************************uint8 ZDOInitDevice( uint16 startDelay ){//初始化设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE:新的网络状态.//可能意味着ZCD_NV_STARTUP_OPTION不能恢复,或没有任何网络状态恢复uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;uint16 extendedDelay = 0;devState = DEV_INIT; // Remove the Hold state// Initialize leave control logic//函数读取NV项目ZCD_NV_LEAVE_CTRL的值,ZDApp_LeaveCtrl指向这个值ZDApp_LeaveCtrlInit();// Check leave control reset settingsZDApp_LeaveCtrlStartup( &devState, &startDelay );// Leave may make the hold state come back//以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则//把设备状态设为DEV_HOLDif ( devState == DEV_HOLD )//ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用.return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); // Don't join - (one time).#if defined ( NV_RESTORE )// Get Keypad directly to see if a reset nv is needed.// Hold down the SW_BYPASS_NV key (defined in OnBoard.h)// while booting(引导) to skip past NV Restore.if ( HalKeyRead() == SW_BYPASS_NV )//SW_BYPASS_NV按键处于按下状态时,则避开网络层的NV存储networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; //设备网络状态为新的网络状态else{// Determine if NV should be restored//函数返回的设备网络状态要么是新的网络状态;要么是恢复的网络状态;以此//来确定要不要读取NV里相应条目来恢复网络先前状态networkStateNV = ZDApp_ReadNetworkRestoreState();}//如果设备的网络状态为恢复的网络状态if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ){//恢复设备先前的网络状态参数并且//设置devStartMode = MODE_RESUME!!!!networkStateNV = ZDApp_RestoreNetworkState();}else //如果设备的网络状态为新的网络状态,在下面进行处理{// Wipe out(清除) the network state in NVNLME_InitNV();NLME_SetDefaultNV(); //设置默认NV条目}#endif//如果设备的网络状态为新的网络状态if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE ){//根据预编译来设置设备新的网络状态参数ZDAppDetermineDeviceType();/*!!!!*/// Only delay if joining network - not restoring network stateextendedDelay = (uint16)((NWK_START_DELAY + startDelay)+ (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));}// Initialize device securityZDApp_SecInit( networkStateNV );// Trigger the network startZDApp_NetworkInit( extendedDelay );return ( networkStateNV );}**************************************分两种情况:(1)如果协调器预编译了NV_RESTORE,且函数ZDApp_ReadNetworkRestoreState()返回值为ZDO_INITDEV_RESTORED_NETWORK_STATE,则进入ZDApp_RestoreNetworkState()里设置ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR和devStartMode = MODE_RESUME.****************uint8 ZDApp_RestoreNetworkState( void ){…………// Are we a coordinator//设备的网络状态为恢复的网络状态.则进入这个函数进行恢复//先判断如果短地址为0则设置设备逻辑类型为协调器且devStartMode = MODE_RESUME//否则设置devStartMode = MODE_RESUMEZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();if ( ZDAppNwkAddr.addr.shortAddr == 0 ) //如果短地址是0,即协调器{ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR; //!!!!!}devStartMode = MODE_RESUME; //MODE_RESUME!!!!!!!!…………}****************(2)如果协调器没有预编译NV_RESTORE,networkStateNV ==ZDO_INITDEV_NEW_NETWORK_STATE,但由于协调器编译了ZDO_COORDINATOR而没有编译SOFT_START,因此ZDAppDetermineDeviceType()不起作用.因此ZDO_Config_Node_Descriptor.LogicalType和devStartMode这两个关键参数保持初始化时的值:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR(见基本问题说明6) devStartMode = MODE_HARD(见基本问题说明4)对于协调器,这两种情况最终都是确定两个关键设备网络状态参数的值.对本例的SampleApp的协调器,没有编译NV_RESTORE,因此属于情况(2).然后调用ZDApp_NetworkInit()启动网络:****************void ZDApp_NetworkInit( uint16 delay ){if ( delay ){// Wait awhile before starting the deviceosal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );}else{osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );}}****************通过触发ZDAppTaskID的ZDO_NETWORK_INIT事件.来看下对ZDO_NETWORK_INIT 事件的处理: ****************UINT16 ZDApp_event_loop( byte task_id, UINT16 events ){…………if ( events & ZDO_NETWORK_INIT ){// Initialize apps and start the networkdevState = DEV_INIT;ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );// Return unprocessed eventsreturn (events ^ ZDO_NETWORK_INIT);}…………}可以看到调用了ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,DEFA ULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );这里设备网络状态参数:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATORdevStartMode = MODE_HARD且协调器编译了ZDO_COORDINATOR****************void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ){ZStatus_t ret;ret = ZUnsupportedMode;#if defined(ZDO_COORDINATOR)if ( logicalType == NODETYPE_COORDINATOR ){if ( startMode == MODE_HARD ) //MODE_HARD{devState = DEV_COORD_STARTING; //Started as Zigbee Coordinator//建网ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,zgDefaultStartingScanDuration, beaconOrder,superframeOrder, false );}else if ( startMode == MODE_RESUME ) //MODE_RESUME{// Just start the coordinatordevState = DEV_COORD_STARTING;ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );}else //错误,未知启动模式{#if defined( LCD_SUPPORTED )//HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );ClearScreen();Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);#endif}#endif // !ZDO_COORDINATOR//#if !defined ( ZDO_COORDINATOR ) || defined( SOFT_START )if ( logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE ){if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) ){devState = DEV_NWK_DISC; //Discovering PAN's to join#if defined( MANAGED_SCAN )ZDOManagedScan_Next();ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );#elseret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );#endif}else if ( startMode == MODE_RESUME ) //MODE_RESUME 恢复{if ( logicalType == NODETYPE_ROUTER ){ZMacScanCnf_t scanCnf;devState = DEV_NWK_ORPHAN;/* if router and nvram is available, fake successful orphan scan */scanCnf.hdr.Status = ZSUCCESS;scanCnf.ScanType = ZMAC_ORPHAN_SCAN;scanCnf.UnscannedChannels = 0;scanCnf.ResultListSize = 0;nwk_ScanJoiningOrphan(&scanCnf);ret = ZSuccess;}else{devState = DEV_NWK_ORPHAN; //孤儿ret = NLME_OrphanJoinRequest( zgDefaultChannelList,zgDefaultStartingScanDuration );}}else{#if defined( LCD_SUPPORTED )// HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);#endif}//#endif //!ZDO COORDINATOR || SOFT_STARTif ( ret != ZSuccess )osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );}****************通过参数可知协调器调用NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,zgDefaultStartingScanDuration, beaconOrder,superframeOrder, false )进行网络的组建.而对NLME_NetworkFormationRequest()的调用会产生一个回调函数ZDO_NetworkFormationConfirmCB()(见主要函数说明3),来看下:****************void ZDO_NetworkFormationConfirmCB( ZStatus_t Status ){#if defined(ZDO_COORDINATOR)nwkStatus = (byte)Status;if ( Status == ZSUCCESS ){// LED on shows Coordinator startedHalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );// LED off forgets HOLD_AUTO_STARTHalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);#if defined ( ZBIT )SIM_SetColor(0xd0ffd0);#endifif ( devState == DEV_HOLD ){// Began with HOLD_AUTO_STARTdevState = DEV_COORD_STARTING;}}#if defined(BLINK_LEDS)elseHalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH ); // Flash LED to show failure#endifosal_set_event( ZDAppTaskID, ZDO_NETWORK_START );#endif //ZDO_COORDINATOR}****************如果Status返回ZSUCCESS,建立网络成功,通过一些灯亮来来指示;不成功则通过闪烁灯来指示.最后触发任务ZDAppTaskID的ZDO_NETWORK_START事件,看下对ZDO_NETWORK_START的处理:****************#if defined (RTR_NWK)if ( events & ZDO_NETWORK_START ){ZDApp_NetworkStartEvt();// Return unprocessed eventsreturn (events ^ ZDO_NETWORK_START);}****************调用了ZDApp_NetworkStartEvt()****************void ZDApp_NetworkStartEvt( void ){if ( nwkStatus == ZSuccess )//网络建立成功{// Successfully started a ZigBee networkif ( devState == DEV_COORD_STARTING ){devState = DEV_ZB_COORD;}osal_pwrmgr_device( PWRMGR_ALWAYS_ON );osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );}else //网络建立不成功,则增加能量阀值重新建网.{// Try again with a higher energy threshold !!if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff ){NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); //重新初始化建立网络}else{// Failed to start network. Enter a dormant state (until user intervenes)devState = DEV_INIT;osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );}}}****************如果协调器建立网络成功,则触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT事件.看下对ZDO_STATE_CHANGE_EVT的处理:****************if ( events & ZDO_STATE_CHANGE_EVT ){ZDO_UpdateNwkStatus( devState );// Return unprocessed eventsreturn (events ^ ZDO_STATE_CHANGE_EVT);}****************调用了ZDO_UpdateNwkStatus( devState ),网络状态改变,这个函数会更新和发送信息通知每个注册登记过的应用终端.****************void ZDO_UpdateNwkStatus( devStates_t state ){// Endpoint/Interface descriptor list.epList_t *epDesc = epList;byte bufLen = sizeof(osal_event_hdr_t);osal_event_hdr_t *msgPtr;ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.while ( epDesc ){if ( epDesc->epDesc->endPoint != ZDO_EP ){msgPtr = (osal_event_hdr_t *)osal_msg_allocate( bufLen );if ( msgPtr ){msgPtr->event = ZDO_STATE_CHANGE; // Command IDmsgPtr->status = (byte)state;osal_msg_send( *(epDesc->epDesc->task_id), (byte *)msgPtr ); //发往应用任务}}epDesc = epDesc->nextDesc;}}****************对SampleApp的协调器来说,这里触发应用任务SampleApp_TaskID的ZDO_STATE_CHANGE事件,看下对ZDO_STATE_CHANGE的处理:****************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) ){// Start sending the periodic message in a regular interval.osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );}else{// Device is no longer in the network}break;****************可以看到当协调器建立网络成功,通过回调函数触发应用任务的ZDO_STATE_CHANGE事件,最终开启定时器发送周期信息.3、协调器(自启动模式)—以SampleApp的协调器为例,并假设初始化成功,网络建立成功.程序大致流程:main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->ZDOInitDevice()->ZDApp_NetworkInit->触发ZDAppTaskID的ZDO_NETWORK_INIT->ZDO_StartDevice()->NLME_NetworkFormationRequest()->网络建立成功ZDO_NetworkFormationConfirmCB->触发ZDAppTaskID的ZDO_NETWORK_START->ZDApp_NetworkStartEvt()->触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT->ZDO_UpdateNwkStatus()->触发SampleApp_TaskID的ZDO_STATE_CHANGE->开户周期信息发送的定时器.4、注:(1)自启动模式下SampleApp的终端和路由器总体流程基本一致.(2)以SampleApp为例,ZDO_StartDevice()函数的两个重要参数比较:终端:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICEdevStartMode = MODE_JOIN路由器:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_ROUTERdevStartMode = MODE_JOIN协调器:ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATORdevStartMode = MODE_HARD。