第1章 ZigBee协议栈初始化网络启动流程
作者:宋老师,华清远见嵌入式学院讲师。
ZigBee的基本流程:由协调器的组网(创建PAN ID),终端设备和路由设备发现网络以及加入网络。
基本流程:main()->osal_init_system()->osalInitTasks()->ZDApp_Init(),进协议栈初始化函数ZDApp_Init()。
1.1 进入程序入口main()。
ZMain.c中
C++ Code
int main( void )
{
// Turn off interrupts
osal_int_disable( INTS_ALL );
// Initialization for board related stuff such as LEDs
HAL_BOARD_INIT();
// Make sure supply voltage is high enough to run
zmain_vdd_check();
// Initialize board I/O
InitBoard( OB_COLD );
// Initialze HAL drivers
HalDriverInit();
// Initialize NV System
osal_nv_init( NULL );
// Initialize the MAC
ZMacInit();
// Determine the extended address
zmain_ext_addr();
// Initialize basic NV items
zgInit();
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit();
#endif
// Initialize the operating system
osal_init_system();
// Allow interrupts
osal_int_enable( INTS_ALL );
// Final board initialization
InitBoard( OB_READY );
// Display information about this device
zmain_dev_info();
/* Display the device info on the LCD */
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
#ifdef WDT_IN_PM1
/* If WDT is used, this is a good place to enable it. */
WatchDogEnable( WDTIMX );
#endif
osal_start_system(); // No Return from here
return 0; // Shouldn't get here.
} // main()
1.2 给任务添加ID
sapi.c中
C++ Code
void 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++ );
#endif
APS_Init( taskID++ );
ZDApp_Init( taskID++ );//判断如果协调器节点建立网络、如果终端节点加入网络
SAPI_Init( taskID );
}
1.3 初始化ZigBee协议栈网络
ZDApp.c
C++ Code
void ZDApp_Init( uint8 task_id )
{
// Save the task ID
ZDAppTaskID = task_id;
// Initialize the ZDO global device short address storage
ZDAppNwkAddr.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_START
HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
ZDApp_RegisterCBs();
} /* ZDApp_Init() */
如果设置devState为DEV_HOLD,则不会执行ZDOInitDevice;反之,系统会调用此
函数是设备组网或者入网。看下这个函数完成的功能是什么样子的。ZDOInitDevice是设备在网络中启动。它会读取NV中的
ZCD_NV_STARTUP_OPTION选项决定是否恢复网络状态。如果应用层强制进行新的join操作,它应该在调用这个函数之前设置
ZCD_NV_STARTUP_OPTION中的ZCD_STARTOPT_DEFAULT_NETWORK_STATE位。可以调用
zgWrieStartupOptions()函数完成这些设置。
1.4 初始化设备(启动网络和设置网络类型)
ZDApp.c
C++ Code
uint8 ZDOInitDevice( uint16 startDelay )
{
uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
uint16 extendedDelay = 0;
if ( devState == DEV_HOLD )
{
// Initialize the RAM items table, in case an NV item has been updated.
zgInitItems( FALSE );
}
ZDConfig_InitDescriptors();
//devtag.071807.todo - fix this temporary solution
_NIB.CapabilityInfo = ZDO_Config_Node_Descriptor.CapabilityFlags;
devState = DEV_INIT; // Remove the Hold state
// 函数读取NV项目ZCD_NV_LEAVE_CTRL的值,ZDApp_LeaveCtrl指向这个值
// Initialize leave control logic
ZDApp_LeaveCtrlInit();
// Check leave control reset settings//设备的断开会造成DEV_HOLD状态
ZDApp_LeaveCtrlStartup( &devState, &startDelay );
// Leave may make the hold state come back
if ( devState == DEV_HOLD )
{
//设置启动选项
// Set the NV startup option to force a "new" join.
zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );
//通知应用层触发事件
// Notify the applications
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
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 )
networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
else
{
// 通过读取ZCD_NV_STARTUP_OPTION选项决定是否进行网络恢复
// Determine if NV should be restored
networkStateNV = ZDApp_ReadNetworkRestoreState();
}
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
{
networkStateNV = ZDApp_RestoreNetworkState();
}
else
{
// Wipe out the network state in NV
NLME_InitNV();
NLME_SetDefaultNV();
}
#endif
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )//如果是要启动新的网络
{
ZDAppDetermineDeviceType();//根据选项设置设备的网络类型,默认路由类型
// Only delay if joining network - not restoring network state
extendedDelay = (uint16)((NWK_START_DELAY + startDelay)
+ (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));
}
// Initialize the security for type of device
ZDApp_SecInit( networkStateNV );
// 触发启动网络
// Trigger the network start
ZDApp_NetworkInit( extendedDelay );
// set broadcast address mask to support broadcast filtering 用于处理合法的广播地址
NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );
return ( networkStateNV );
}
这个函数注意功能:初始化设备配置,ZDAppDetermineDeviceType()设置网络类型(协调、路由、终端),ZDApp_NetworkInit( extendedDelay )初始化网络并开启或加入网络。
1.5 定时开启网络
ZDApp.c
进入ZDApp_NetworkInit()函数,等待一段时间在执行ZDO_NETWORK_INIT,跳入事件处理ZDApp层ZDAPP_EVENT_LOOP()函数开启网络。
C++ Code
void ZDApp_NetworkInit( uint16 delay )
{
if ( delay )
{
// Wait awhile before starting the device
osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );
//发送ZDO_NETWORK_INIT(网络初始化)消息到 ZDApp层,转到ZDApp层,ZDApp_event_loop()函数。
}
else
{
osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );
}
}
1.6 启动设备
ZDApp.c
ZDApp_event_loop()函数是回调函数,循环处理主事件。设备逻辑类型,启动模式,信标时间,超帧长度
C++ Code
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
uint8 *msg_ptr;
if ( events & SYS_EVENT_MSG )
{
while ( (msg_ptr = osal_msg_receive( ZDAppTaskID )) )
{
ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );
// Release the memory
osal_msg_deallocate( msg_ptr );
}
// Return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if ( events & ZDO_NETWORK_INIT )
{
// Initialize apps and start the network
devState = DEV_INIT;
//设备逻辑类型,启动模式,信标时间,超帧长度,接着转到去启动设备,转到ZDO_StartDevice()
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
// Return unprocessed events
return (events ^ ZDO_NETWORK_INIT);
}
if ( ZSTACK_ROUTER_BUILD )
{
if ( events & ZDO_NETWORK_START )
{
ZDApp_NetworkStartEvt();
// Return unprocessed events
return (events ^ ZDO_NETWORK_START);
}
if ( events & ZDO_ROUTER_START )
{
if ( nwkStatus == ZSuccess )
{
if ( devState == DEV_END_DEVICE )
devState = DEV_ROUTER;
osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
}
else
{
// remain as end device!!
}
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
// Return unprocessed events
return (events ^ ZDO_ROUTER_START);
}
}
if ( events & ZDO_STATE_CHANGE_EVT )
{
ZDO_UpdateNwkStatus( devState );
// At start up, do one MTO route discovery if the device is a concentrator
if ( zgConcentratorEnable == TRUE )
{
// Start next event
osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 );
}
// Return unprocessed events
return (events ^ ZDO_STATE_CHANGE_EVT);
}
if ( events & ZDO_COMMAND_CNF )
{
// User defined logic
// Return unprocessed events
return (events ^ ZDO_COMMAND_CNF);
}
if ( events & ZDO_NWK_UPDATE_NV )
{
ZDApp_SaveNetworkStateEvt();
// Return unprocessed events
return (events ^ ZDO_NWK_UPDATE_NV);
}
if ( events & ZDO_DEVICE_RESET )
{
// The device has been in the UNAUTH state, so reset
// Note: there will be no return from this call
SystemReset();
}
if ( ZG_SECURE_ENABLED )
{
return ( ZDApp_ProcessSecEvent( task_id, events ) );
}
else
{
// Discard or make more handlers
return 0;
}
}
1.7 开启网络
ZDObject.c
C++ Code
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
ZStatus_t ret;
#if defined ( ZIGBEE_FREQ_AGILITY )
static uint8 discRetries = 0;
#endif
#if defined ( ZIGBEE_COMMISSIONING )
static uint8 scanCnt = 0;
#endif
ret = ZUnsupportedMode;
if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR ) //当设备作为协调器时,执行这个条件语句。
{
if ( startMode == MODE_HARD )
{
devState = DEV_COORD_STARTING;
//向网络层发送网络形成请求。当网络层执行 NLME_NetworkFormationRequest()建立网络后,将给予ZDO层反馈信息。
// 接着转去执行ZDApp层的 ZDO_NetworkFormationConfirmCB()函数
ret =
NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID,
zgDefaultChannelList,
zgDefaultStartingScanDuration, beaconOrder,
superframeOrder, false );
}
else if ( startMode == MODE_RESUME )
{
// Just start the coordinator
devState = DEV_COORD_STARTING;
ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
}
else
{
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
#endif
}
}
if ( ZG_BUILD_JOINING_TYPE && (logicalType
== NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE)
)//当设备作为节点时,执行这个条件语句。
{
if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
{
devState = DEV_NWK_DISC;
#if defined( MANAGED_SCAN )
ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
#else
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
#if defined ( ZIGBEE_FREQ_AGILITY )
if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
( ret == ZSuccess ) && ( ++discRetries == 4 ) )
{
// For devices with RxOnWhenIdle equals to FALSE, any network channel
// change will not be recieved. On these devices or routers that have
// lost the network, an active scan shall be conducted on the Default
// Channel list using the extended PANID to find the network. If the
// extended PANID isn't found using the Default Channel list, an scan
// should be completed using all channels.
zgDefaultChannelList = MAX_CHANNELS_24GHZ;
}
#endif // ZIGBEE_FREQ_AGILITY
#if defined ( ZIGBEE_COMMISSIONING )
if (startMode == MODE_REJOIN && scanCnt++ >= 5 )
{
// When ApsUseExtendedPanID is commissioned to a non zero value via
// application specific means, the device shall conduct an active scan
// on the Default Channel list and join the PAN with the same
// ExtendedPanID. If the PAN is not found, an scan should be completed
// on all channels.
// When devices rejoin the network and the PAN is not found from
zgDefaultChannelList = MAX_CHANNELS_24GHZ;
}
#endif // ZIGBEE_COMMISSIONING
#endif
}
else if ( startMode == 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" );
#endif
}
}
if ( ret != ZSuccess )
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
1.8 开启网络事件
ZDApp.c
C++ Code
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{
nwkStatus = (byte)Status;
if ( Status == ZSUCCESS )
{
// LED on shows Coordinator started
HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
// LED off forgets HOLD_AUTO_START
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
#if defined ( ZBIT )
SIM_SetColor(0xd0ffd0);
#endif
if ( devState == DEV_HOLD )
{
// Began with HOLD_AUTO_START
devState = DEV_COORD_STARTING;
}
}
#if defined(BLINK_LEDS)
else
HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH ); // Flash LED to show failure
#endif
osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );
}
文章来源:华清远见嵌入式学院,原文地址:http://www.embedu.org/Column/Column877.htm 更多相关嵌入式免费资料查看华清远见讲师博文》》
第1章 ZigBee协议栈初始化网络启动流程的更多相关文章
- 深入刨析tomcat 之---第12篇 how tomcat works( 第17章 ) 解析catalina.bat 梳理启动流程
我们如何启动tomcat呢? 答案是双击startup.bat文件,这个文件在bin目录下 @echo off 不显示批处理命令 rem Licensed to the Apache Softw ...
- Hadoop源码学习笔记之NameNode启动场景流程四:rpc server初始化及启动
老规矩,还是分三步走,分别为源码调用分析.伪代码核心梳理.调用关系图解. 一.源码调用分析 根据上篇的梳理,直接从initialize()方法着手.源码如下,部分代码的功能以及说明,已经在注释阐述了. ...
- SpringBoot启动流程原理解析(二)
在上一章我们分析了SpingBoot启动流程中实例化SpingApplication的过程. return new SpringApplication(primarySources).run(args ...
- Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化
Netty源码分析第一章: Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...
- Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化
Netty源码分析第一章:Netty启动流程 第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...
- [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)
说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...
- 【RL-TCPnet网络教程】第30章 RL-TCPnet之SNTP网络时间获取
第30章 RL-TCPnet之SNTP网络时间获取 本章节为大家讲解RL-TCPnet的SNTP应用,学习本章节前,务必要优先学习第29章的NTP基础知识.有了这些基础知识之后,再搞本章节会 ...
- Zigbee协议栈--Z-Stack的使用
使用方法简介:一般情况下用户只需要额外添加三个文件就可以完成一个项目.一个是主文件,存放具体的任务事件处理函数:一个是这个主文件的头文件:另外一个是以Osal开头的操作系统接口文件,是专门存放任务处理 ...
- [ZigBee] 15、Zigbee协议栈应用(一)——Zigbee协议栈介绍及简单例子(长文,OSAL及Zigbee入门知识)
1.Zigbee协议栈简介 协议是一系列的通信标准,通信双方需要按照这一标准进行正常的数据发射和接收.协议栈是协议的具体实现形式,通俗讲协议栈就是协议和用户之间的一个接口,开发人员通过使用协议栈来使用 ...
随机推荐
- ASP.NET的新成员ASP.NET WebHooks
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:前几天微软除了发布了ASP.NET 5的Beta7之外,还有一个值得关注的东西,就是A ...
- C语言函数的读写
文件打开关闭函数:fopen()和fclose() <FILE *fopen(char *filename, char *mode)| int fclose(FILE *fp)> 字符读写 ...
- 【JDK】电脑上安装多个JDK ,修改JAVA_HOME后没有作用
电脑上装了 C:\Program Files\Java\jdk1.6.0_43 C:\Program Files\Java\jdk1.7.0_80 C:\Program Files\ ...
- 利用myeclipse配置数据库连接池
作为一个习惯使用myeclipse的人来说,即使是数据库连接池也肯定是用ide配置了. 下面说一下用数据库连接池的配置. 1 创建工程.不多说了. 2 添加数据库连接程序驱动包.直接放到lib目录下即 ...
- java 线程演示
package unit8; public class Mainthread { public static void main(String[] args) { Thread t = new Thr ...
- Liferay 6.2 改造系列之十六:关闭OpenID模式的单点登录
在/portal-master/portal-impl/src/portal.properties文件中,有如下配置: # # Set this to true to enable OpenId au ...
- hdu 3449 有依赖性的01背包
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=3449 Consumer Description FJ is going to do so ...
- PHP 下载简历
下载简历:先生成html模版,然后在下载转化为word格式: 获取数据方法: 先获取数据,然后开启缓存,写入数据,关闭缓存:然后下载成word: /** * 下载简历--简单 * @author La ...
- zepto下加载openbox弹出层
function fnOpenBox(objId,animateD,speed,tween,hide){ var oOpenBox = $(objId); oOpenBox.show(); oOpen ...
- Open CV 播放视频(2)
演示:读取一个视频,然后播放,ESC退出. #include "stdafx.h" #include <opencv2/core/core.hpp> # ...