启动过程:
1、int main( void )中的 osal_init_system();
2、 osal_init_system();中的 osalInitTasks();
3、osalInitTasks();中的 ZDApp_Init( taskID++ );和SerialApp_Init( taskID );
4、void ZDApp_Init( uint8 task_id )--- ZDOInitDevice( uint16 startDelay )----ZDApp_NetworkInit( uint16 delay )
5、UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )中的
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
而对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
#define SW_BYPASS_START HAL_KEY_SW_1
因此避开网络层NV存储也可以通过手工方式来完成.
//把设备状态设为DEV_HOLD
程序的运行基本流程在上一个学习例子中已经讲过,这节主要讨论一下网络启动的几种方式,
先介绍一些基本概念,然后再来看程序:
successive changes until it achieves a unique Pan Id. A Router or End Device newly joining will have no way of knowing the value of the “de-conflicted” Pan Id established, and will therefore join only the Pan Id specified. A similarly challenging scenario arises when the permitted channel mask allows more than one channel and the Coordinator cannot use the first channel because of a Pan Id conflict – a Router or End Device will join the specified Pan Id on the first channel scanned, if allowed.
协调器在启动后将会在DEFAULT_CHANLIST制定的信道上形成一个网络。协调器将会根据它自己的物理地址生成一个随机的PAN ID或者在ZDAPP_CONFIG_PAN_ID不为oxffff的时候生成ZDAPP_CONFIG_PAN_ID指定的PAN ID。路由器和中断将会在启动后尝试加入信道为DEFAULT_CHANLIST的网络。如果ZDAPP_CONFIG_PAN_ID
Auto Start
A device will automatically start trying to form or join a network as part of the BSP power-up sequence. If the device should wait on a timer or other external event before joining, then HOLD_AUTO_START must be defined. In order to manually start the join process at a later time, invoke ZDApp_StartUpFromApp().
作为板级支持包上电顺序的一部分,设备将会自动开始形成或加入网络,如果设备在加入之前必须等待一段时间或是等待外部事件,HOLD_AUTO_START这个必须被定义。为了之后人工开始加入过程,应该调用ZDApp_StartUpFromApp()函数。
*********************************
注:ZDApp_StartUpFromApp()原型:
* @fn
ZStatus_t ZDApp_StartUpFromApp( uint8 mode )
{
#if defined( SOFT_START )
#endif
}
*************************************************************
1.3.2 Soft Start
To save on device types required to form the network, all router devices can be compiled as a Coordinator with SOFT_START defined. If the auto start behavior is required, then AUTO_SOFT_START must also be defined.
软启动
为了保存形成网络需要的设备类型,所有的路由设备可以定义SOFT_START 被编译为一个协调器。如果需要自启动,那么必须定义AUTO_SOFT_START。
1.3.3 Network Restore
Devices that have successfully joined a network can “restore the network” (instead of reforming by OTA messages) even after losing power or battery. This automatic restoration can be enabled by defining NV_RESTORE and/or NV_INIT.
网络恢复
成功加入网络的设备即使在没有掉电之后还可以恢复网络(不需要通过OTA信息来重新形成网络),自动恢复的功能需要定义
1.3.4 Join Notification
The device is notified of the status of the network formation or join (or any change in network state) with the ZDO_STATE_CHANGE message mentioned above.
加入告知
网络形成或加入的状态信息通过ZDO_STATE_CHANGE消息来通知设备。
下面来看具体的程序:
#if defined ( HOLD_AUTO_START )
#endif
HOLD_AUTO_START如果定义了将会使应用来启动设备;
ZDOInitDevice(0)的具体函数如下:(不要觉得头晕,想搞懂,没办法,是吧,恩恩,加油)
uint8 ZDOInitDevice( uint16 startDelay )
{
********************
注:为什么要清楚之前的状态:
#if defined( HOLD_AUTO_START )
#else
#endif
********************
#if defined ( NV_RESTORE )
#endif
}
设置周期信息的目标地址
设置flash命令的目标地址
****************
注:
typedef struct
{
} aps_Group_t;
aps_Group_t SampleApp_Group;
aps_AddGroup();这个函数好像不开源。
*********************************
把这个程序的整体流程说一下,好理清思路:
现在假设整个网络初始化成功。
当网络状态改变后,每个设备将调用osal_start_timerEx( SampleApp_TaskID,
这个函数说明将在SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT后发送SAMPLEAPP_SEND_PERIODIC_MSG_EVT消息,该消息发送到系统后,系统处理SAMPLEAPP_SEND_PERIODIC_MSG_EVT事件:
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )
SampleApp_SendPeriodicMessage()的原型如下:
void SampleApp_SendPeriodicMessage( void )
{
}
当设备接收到其他设备发送的消息的时候,将会触发case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB()原型如下:
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
}
同时程序中还注册了按键事件:
按下按键sw1时有:
SampleApp_SendFlashMessage()的原型:
void SampleApp_SendFlashMessage( uint16 flashTime )
{
}
按下sw2时,决定这个设备是加入还是退出组1。
}
在ZDApp.c文件中,可以看到下面的定义: ...看源代码。。
#if defined( HOLD_AUTO_START )
devStates_t devState = DEV_HOLD; // 初始化-不会自动启动
#else 。。。。。。。。。
在调用用户自己定义的任务初始化函数之前,调用下面的初始函数,看看这里怎么处理,devState状态的。
void ZDApp_Init( byte task_id ) {。)
// Initialize ZDO items and setup the device - type of device to create.
ZDO_Init(); //初始化ZDO条目,并设置设备的启动方式是协调器,还是别的
// 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 filtering NLME_GetRequest(nwkCapabilityInfo, 0, &capabilities); NLME_SetBroadcastFilter( capabilities );
// Start the device? 是否启动设备?如果devState不是DEV_HOLD时,则启动设备,在上面的代码分析中,也可以看到,如果定义了HOLD_AUTO_START宏,则devState等于DEV_HOLD,不会启动设备。如果按下了SW_1键devState也等于DEV_HOLD,也不会启动网络。也就是说有两种方式可以设置非自动启动模式,一种是通过按键,一种通过宏定义 if ( devState != DEV_HOLD ) {
ZDOInitDevice( 0 ); } else {
//如果定义了HOLD_AUTO_START,则等待延时或外部事件启动网络,并且LED4灯,也就是蓝色的灯闪烁
// Blink LED to indicate HOLD_START HalLedBlink ( HAL_LED_4, 0, 50, 500 ); }
ZDApp_RegisterCBs(); } /* ZDO_Init() */
void ZDAppCheckForHoldKey( void ) {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
//通过判断按键来决定是否采用HOLD_AUTO_START方式。当按下
SW_BYPASS_START按键,也就是SW1键,将避开自动启动设备,也就是设置 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) {
// Change the device state to HOLD on start up devState = DEV_HOLD; }
#endif // HAL_KEY }
说明:(1)这里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存储也可以通过手工方式来完成.
//根据编译选项来设置;比如SimpleApp中的灯节点,预编译了ZDO_COORDINATOR和REFLECTOR和SOFT_START,因此会根据这些来选择开启一些函数功能. void ZDO_Init( void ) {
// Initialize ZD items REFLECTOR如果定义了这个编译选项则使用“源绑定”, #if defined ( REFLECTOR ) ZDO_EDBind = NULL; #endif
// Setup the device - type of device to create. ZDODeviceSetup(); }
static void ZDODeviceSetup( void ) {
#if defined( ZDO_COORDINATOR ) //如果定义了协调器,协调器初始化 NLME_CoordinatorInit(); #endif
#if defined ( REFLECTOR ) //如果定义了COORDINATOR_BINDING 绑定时使用
#if defined ( ZDO_COORDINATOR )//定义了REFLECTOR,且定义了协调器 APS_ReflectorInit( APS_REFLECTOR_PUBLIC ); #else //编译了REFLECTOR且编译了路由器或终端 APS_ReflectorInit( APS_REFLECTOR_PRIVATE ); #endif #endif
#if !defined( ZDO_COORDINATOR ) || defined( SOFT_START )//如果没有定义协调器ZDO_COORDINATOR ),则还定义了SOFT_START则进行连接初始化 NLME_DeviceJoiningInit(); #endif }
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 settings
//设备的断开会造成DEV_HOLD状态,这里面设置的. ZDApp_LeaveCtrlStartup( &devState, &startDelay ); // Leave may make the hold state come back
//以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则 //把设备状态设为DEV_HOLD
//ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用. if ( devState == DEV_HOLD )
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 ) {
networkStateNV = ZDApp_RestoreNetworkState(); } else {
// Wipe out the network state in NV //恢复设备先前的网络状态参数
//设置devStartMode = MODE_RESUME 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 device security
ZDApp_SecInit( networkStateNV ); // Trigger the network start
ZDApp_NetworkInit( extendedDelay ); return ( networkStateNV ); }
ZigBee设备的启动,最终是要调用ZDO_StartDevice()函数来实现的。下面看一下是怎么启动这个函数的。在ZDOInitDevice()函数的最后,调用了下面的ZDApp_NetworkInit()函数,在这个函数中,启动了ZDO_NETWORK_INIT事件,这个事件是在ZDApp_event_loop()事件处理函数中进行处理的。在这个事件中调用了启动设备的函数ZDO_StartDevice(),这函数在前面的文章中也已经分析过了。
void ZDApp_NetworkInit( uint16 delay ) {
if ( delay ) {
// Wait awhile before starting the device
osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay ); } else {
osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); } }
UINT16 ZDApp_event_loop( byte task_id, UINT16 events ) {
................ if ( events & ZDO_NETWORK_INIT ) {
// Initialize apps and start the network devState = DEV_INIT;
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER ); // Return unprocessed events
return (events ^ ZDO_NETWORK_INIT); }
..................... }
下面以SimpleSwitchEB为例子看看当定义了HOLD_AUTO_START选项后,程序的流程是怎么样的。在void SAPI_Init( byte task_id )函数的最后,有下面一句话,
osal_set_event(task_id, ZB_ENTRY_EVENT);下图是编译选项的设置:
这将触发ZB_ENTRY_EVENT事件,这个事件的处理在, UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events ) {
................................. if ( events & ZB_ENTRY_EVENT ) {
uint8 startOptions;
// Give indication to application of device startup //这个函数不处理ZB_ENTRY_EVENT事件 zb_HandleOsalEvent( ZB_ENTRY_EVENT );
// LED off cancels HOLD_AUTO_START blink set in the stack
//关闭协议栈中LED4的闪烁,LED4灯闪烁表明没有正常启动设备或者没有加入网络关闭栈中的HOLD指示
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START )
{
zb_StartRequest(); } else {
//首次使用时,闪烁LED2,指示外部输入,等待启动设备 // blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500); }
return (events ^ ZB_ENTRY_EVENT); }
.............................. }
在按键处理函数中,可以看到
void zb_HandleKeys( uint8 shift, uint8 keys ) {
uint8 startOptions; uint8 logicalType;
// Shift is used to make each button/switch dual purpose. if ( shift ) {
if ( keys & HAL_KEY_SW_1 ) { }
if ( keys & HAL_KEY_SW_2 ) { }
if ( keys & HAL_KEY_SW_3 ) { }
if ( keys & HAL_KEY_SW_4 ) { } } else {
if ( keys & HAL_KEY_SW_1 ) {
if ( myAppState == APP_INIT ) {
// In the init state, keys are used to indicate the logical mode. // The Switch device is always an end-device
logicalType = ZG_DEVICETYPE_ENDDEVICE;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
// Do more configuration if necessary and then restart device with auto-start bit set 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();//这里导致设备重启,重启后,产生ZB_ENTRY_EVENT事件,启动网络设备 } else {
// Initiate a binding with null destination
zb_BindDevice(TRUE, TOGGLE_LIGHT_CMD_ID, NULL); } }
if ( keys & HAL_KEY_SW_2 ) {
if ( myAppState == APP_INIT ) {
// In the init state, keys are used to indicate the logical mode. // The Switch device is always an end-device
logicalType = ZG_DEVICETYPE_ENDDEVICE;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType); 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(); } else {
// Send the command to toggle light
zb_SendDataRequest( 0xFFFE, TOGGLE_LIGHT_CMD_ID, 0, (uint8 *)NULL, myAppSeqNumber, 0, 0 ); } }
if ( keys & HAL_KEY_SW_3 ) {
// Remove all existing bindings
zb_BindDevice(FALSE, TOGGLE_LIGHT_CMD_ID, NULL); }
if ( keys & HAL_KEY_SW_4 ) {
} } }
这样SimpleSwitchEB()就作为了非自动启动设备进行了启动了,也就是说必须在定义了HOLD_AUTO_START宏以后,当按键按下后,就会重新启动网络设备。
使用的协议栈版本信息: ZigBee2006\ZStack-1.4.3-1.2.1
Zigbee网络设备启动流程—协调器(非自启动模式HOLD_AUTO_START)
—以SimpleApp的灯节点SimpleControllerEB为例.
灯节点按K1则作为协调器启动,按K2则作为路由器启动.这里以按K1作为协调器启动为例.
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; HOLD_AUTO_START; SOFT_START; REFLECTOR; NV_INIT; LCD_SUPPORTED
xNV_RESTORE; xZTOOL_P1; xMT_TASK; xMT_SAPI_FUNC; xMT_SAPI_CB_FUNC
可以看到编译了HOLD_AUTO_START和SOFT_START.
因而初始化:
devState = DEV_HOLD(见基本问题说明3.编译了HOLD_AUTO_START)
devStartMode = MODE_JOIN(见基本问题说明4.编译了SOFT_START)
zgDeviceLogicalType = DEVICE_LOGICAL_TYPE=ZG_DEVICETYPE_SOFT(见基本问题说明5.编译了SOFT_START)
ZDO_Config_Node_Descriptor.LogicalType=NODETYPE_ROUTER(见基本问题说明6.编译了SOFT_START和RTR_NWK)
2、具体流程
main()->osal_init_system()->osalInitTasks()->ZDApp_Init()
进入ZDApp_Init()函数:
**************************************
void ZDApp_Init( byte task_id )
{
uint8 capabilities;
// Save the task ID
ZDAppTaskID = task_id;
// Initialize the ZDO global device short address storage
ZDAppNwkAddr.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 filtering
NLME_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_START
HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
ZDApp_RegisterCBs();
}
**************************************
ZDApp_Init()中一些函数可以见主要函数说明1.记录下灯节点里ZDO_Init()这个函数.
*********************
void ZDO_Init( void )
{
// Initialize ZD items
#if defined ( REFLECTOR )
ZDO_EDBind = NULL;
#endif
// Setup the device - type of device to create.
ZDODeviceSetup();
}
*********************
灯节点预编译了REFLECTOR,使用源绑定机制.进入ZDODeviceSetup():
*********************
//根据编译选项来设置功能函数;
static void ZDODeviceSetup( void )
{
#if defined( ZDO_COORDINATOR ) //编译了ZDO_COORDINATOR
NLME_CoordinatorInit();
#endif
#if defined ( REFLECTOR ) //编译了REFLECTOR
#if defined ( ZDO_COORDINATOR ) //编译了REFLECTOR且编译了ZDO_COORDINATOR
APS_ReflectorInit( APS_REFLECTOR_PUBLIC );
#else //编译了REFLECTOR且编译了ZR或ED
APS_ReflectorInit( APS_REFLECTOR_PRIVATE );
#endif
#endif
//没有编译ZDO_COORDINATOR或者编译了SOFT_START
#if !defined( ZDO_COORDINATOR ) || defined( SOFT_START )
NLME_DeviceJoiningInit();
#endif
}
*********************
灯节点编译了ZDO_COORDINATOR、RTR_NWK、REFLECTOR和SOFT_START.因而启动的功能函数有:
NLME_CoordinatorInit(); APS_ReflectorInit( APS_REFLECTOR_PUBLIC );NLME_DeviceJoiningIni()回到ZDApp_Init(),因为灯节点编译了HOLD_AUTO_START,则devState = DEV_HOLD,也就可以不用管有没有手工置位SW1了.因此不会进入ZDOInitDevice()来初始化设备(建网或入网).而是通过LED4的闪烁来指示这是一个非自动启动模式,等待应用程序来初始化设备.ZDApp_Init()初始化结束后再进入SAPI_Init():
**************************************
void osalInitTasks( void )
{
…………
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 );
}
**************************************
看下SAPI_Init():
**************************************
void SAPI_Init( byte task_id )
{
…………
if ( HalKeyRead () == HAL_KEY_SW_1)
{
//期间如果按下SW1键,则禁止自动启动和NV存储功能,更新配置重启
// If SW5 is pressed and held while powerup, force auto-start and nv-restore off and reset
startOptions = ZCD_STARTOPT_CLEAR_STATE | ZCD_STARTOPT_CLEAR_CONFIG; //默认启动状态和配置
//startOptions作为默认启动项配置,存到NV条目ZCD_NV_STARTUP_OPTION中
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset(); //复位系统
}
#endif // HAL_KEY
#ifdef LCD_SUPPORTED
Print8(HAL_LCD_LINE_2 ,20,"Simple",1);
#endif
// Set an event to start the application
osal_set_event(task_id, ZB_ENTRY_EVENT);
}
**************************************
按键SW1(个人记录中按键有点乱,具体查看映射函数)功能和SW_BYPASS_NV、SW_BYPASS_START类似,置位可以避开一些功能.
可以看到触发sapi_TaskID的ZB_ENTRY_EVENT事件,来看下对ZB_ENTRY_EVENT的处理:
**************************************
if ( events & ZB_ENTRY_EVENT ) /*sapi启动事件*/
{
uint8 startOptions;
// Give indication to application of device startup
zb_HandleOsalEvent( ZB_ENTRY_EVENT );
// LED off cancels HOLD_AUTO_START blink set in the stack
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF); //关闭指示HOLD_AUTO_START的LED4
//读配置
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START ) //自动启动
{
zb_StartRequest();
}
else
{
//LED2闪烁指示需要外部输入来配置信息再重启设备
// blink leds and wait for external input to config and restart
HalLedBlink(HAL_LED_2, 0, 50, 500);
#if defined SENSOR
…………
}
return (events ^ ZB_ENTRY_EVENT );
}
**************************************
从程序中可以看到首先读NV条目ZCD_NV_STARTUP_OPTION所存储的值,如果是ZCD_STARTOPT_AUTO_START则调用zb_StartRequest()来请求启动设备.NV条目ZCD_NV_STARTUP_OPTION的值在zgInit()中初始化为默认值(暂时估计为0,见基本问题说明9),因此这里不会调用zb_StartRequest()启动设备,而是LED2闪烁来指示需要外部输入来控制.
作为一个单一设备,main()函数中各项功能已经初始化完成并进入了系统主循环函数osal_start_system(),要不然也不会处理ZB_ENTRY_EVENT事件,只是这个时候还没有启动作为一个网络设备的功能.接下来就通过按键来启动,灯节点按下K1,按键处理函数处理如下:
**************************************
if ( keys & HAL_KEY_SW_1 )
{
/*初始化myAppState = APP_INIT,等灯设备逻辑类型确定并成功建网/入网,
zb_StartRequest()会产生回调函数zb_StartConfirm(),如果成功则设置
myAppState = APP_START,因而再按K1执行允许绑定功能.
*/
if ( myAppState == APP_INIT )
{
// In the init state, keys are used to indicate the logical mode.
// Key 1 starts device as a coordinator
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );
if ( logicalType != ZG_DEVICETYPE_ENDDEVICE )
{
logicalType = ZG_DEVICETYPE_COORDINATOR; //协调器
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
}
// Do more configuration if necessary and then restart device with auto-start bit set
// write endpoint to simple desc...dont pass it in start req..then reset
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();
}
else
{
// Initiate a binding
zb_AllowBind( myAllowBindTimeout ); //默认为10S,myAllowBindTimeout=10
}
}
**************************************
按K1选择逻辑类型logicalType = ZG_DEVICETYPE_COORDINATOR,写入NV条目ZCD_NV_LOGICAL_TYPE,这里改变了ZDO全局变量zgDeviceLogicalType的值.灯节点中zgDeviceLogicalType被初始化为zgDeviceLogicalType = DEVICE_LOGICAL_TYPE=ZG_DEVICETYPE_SOFT(可选类型),而通过按K1,
zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR.(见基本问题说明5).
然后把启动选项startOptions = ZCD_STARTOPT_AUTO_START的值写入NV条目ZCD_NV_STARTUP_OPTION中.下次读取NV条目ZCD_NV_STARTUP_OPTION的值就为ZCD_STARTOPT_AUTO_START(因此会在对ZB_ENTRY_EVENT的处理中触发进入zb_StartRequest()).
最后重启系统.重新经过流程main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件
此时
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START )//自动启动
{
zb_StartRequest();
}
再读取NV条目ZCD_NV_STARTUP_OPTION值时为ZCD_STARTOPT_AUTO_START,因而进入zb_StartRequest():
**************************************
* @fn zb_StartRequest
*
* @brief The zb_StartRequest function starts the ZigBee stack. When the
* ZigBee stack starts, the device reads configuration parameters
* from Nonvolatile memory and the device joins its network. The
* ZigBee stack calls the zb_StartConrifm callback function when
* the startup process completes.
*
* @param none
*
* @return none
*/
void zb_StartRequest()
{
uint8 logicalType;
// Start the device
// start delay = min(NWK_START_DELAY, zgStartDelay) + rand() - on
if ( zgStartDelay < NWK_START_DELAY )
zgStartDelay = 0;
else
zgStartDelay -= NWK_START_DELAY;
// check that bad combinations of compile flag definitions and device type
/*
// 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(可选择类型)
*/
//读取存储在NV里的设备ZCD_NV_LOGICAL_TYPE
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );
//检查错误的参数.
//如:logicalType>0x02(可选择类型选择后不是协调器就是路由器);定义
//了协调器,但logicalType为终端设备;
//只可能是路由器时logicalType不为路由器;未编译RTR_NWK功能却定义了
//logicalType为协调器;只可能为终端时logicalType却未定义为终端;
if ( ( logicalType > ZG_DEVICETYPE_ENDDEVICE ) ||
#if defined( RTR_NWK )
#if defined( ZDO_COORDINATOR )
// On
( logicalType == ZG_DEVICETYPE_ENDDEVICE ) ||
#else
// On
( logicalType != ZG_DEVICETYPE_ROUTER ) ||
#endif
#else
#if defined( ZDO_COORDINATOR )
// Error
( 1 ) ||
#else
// on
( logicalType != ZG_DEVICETYPE_ENDDEVICE ) ||
#endif
#endif
( 0 ) )
{
// error configuration
SAPI_SendCback( SAPICB_START_CNF, ZInvalidParameter, 0 ); //发送错误报告
}
else //设备逻辑类型参数都正确则调用ZDOInitDevice()
{
ZDOInitDevice(zgStartDelay);
}
return;
}
**************************************
读取设备存储在NV条目ZCD_NV_LOGICAL_TYPE中的逻辑类型参数(这里灯节点按K1逻辑类型参数为ZG_DEVICETYPE_COORDINATOR),并检验是否有错误,正确则调用ZDOInitDevice(zgStartDelay).看下ZDOInitDevice():
**************************************
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 settings
ZDApp_LeaveCtrlStartup( &devState, &startDelay );
// Leave may make the hold state come back
//以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则
//把设备状态设为DEV_HOLD
if ( devState == DEV_HOLD )
//ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用.
return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); // Don't join - (on
#if defined ( NV_RESTORE )
// Get Keypad directly to see if a reset nv is needed.
// Hold down the SW_BYPASS_NV key (defined in On
// 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 NV
NLME_InitNV();
NLME_SetDefaultNV();//设置默认NV条目
}
#endif
//如果设备的网络状态为新的网络状态
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
{
//根据预编译来设置设备新的网络状态参数
ZDAppDetermineDeviceType();/*!!!!*/
// On
extendedDelay = (uint16)((NWK_START_DELAY + startDelay)
+ (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));
}
// Initialize device security
ZDApp_SecInit( networkStateNV );
// Trigger the network start
ZDApp_NetworkInit( extendedDelay );
return ( networkStateNV );
}
**************************************
两种情况,编译了NV_RESTORE和未编译.见自启动模式记录的一些说明.灯节点没有编译NV_RESTORE因此networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE,调用ZDAppDetermineDeviceType():
**************************************
void ZDAppDetermineDeviceType( void )
{
if ( zgDeviceLogicalType == ZG_DEVICETYPE_ENDDEVICE )
return;
#if defined ( SOFT_START )
if ( zgDeviceLogicalType == ZG_DEVICETYPE_COORDINATOR )
{
devStartMode = MODE_HARD; // Start as a coordinator
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
}
else
{
if ( zgDeviceLogicalType == ZG_DEVICETYPE_ROUTER )
{
softStartAllowCoord = FALSE; // Don't allow coord to start
continueJoining = TRUE;
}
devStartMode = MODE_JOIN; // Assume joining
}
#endif // SOFT_START
}
**************************************
灯节点编译过SOFT_START,且通过按K1设置了zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR,因而这里执行:
devStartMode = MODE_HARD; // Start as a coordinator
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
然后跳回ZDOInitDevice()进入ZDApp_NetworkInit( extendedDelay )触发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 network
devState = DEV_INIT;
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
// Return unprocessed events
return (events ^ ZDO_NETWORK_INIT);
}
…………
}
**************************************
这里两个参数上面已经设置为:
devStartMode = MODE_HARD; // Start as a coordinator
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
进入ZDO_StartDevice()
**************************************
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 );
}
…………
**************************************
调用了NLME_NetworkFormationRequest()来建立网络.对NLME_NetworkFormationRequest()的调用会产生一个回调函ZDO_NetworkFormationConfirmCB()(参见主要函数说明3),接下来的回调函数流程与[Zigbee网络设备启动流程—协调器(自启动模式)]记录中的一样.
记录下最终应用层任务sapi_TaskID对ZDO_STATE_CHANGE事件的处理:
**************************************
UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )
{
…………
case ZDO_STATE_CHANGE: /*网络状态变化*/
//作为网络设备成功开启,通知应用层
// If the device has started up, notify the application
if (pMsg->status == DEV_END_DEVICE ||
pMsg->status == DEV_ROUTER ||
pMsg->status == DEV_ZB_COORD )
{
SAPI_StartConfirm( ZB_SUCCESS );
}
else if (pMsg->status == DEV_HOLD ||
pMsg->status == DEV_INIT)
{
SAPI_StartConfirm( ZB_INIT );
}
break;
…………
}
**************************************
调用了SAPI_StartConfirm():
**************************************
void SAPI_StartConfirm( uint8 status )
{
#if defined ( MT_SAPI_CB_FUNC )
/* First check if MT has subscribed for this callback. If so , pass it as
a event to MonitorTest and return control to calling function after that */
if ( SAPICB_CHECK( SPI_CB_SAPI_START_CNF ) )
{
zb_MTCallbackStartConfirm( status );
}
else
#endif //MT_SAPI_CB_FUNC
{
zb_StartConfirm( status );
}
}
**************************************
调用了zb_MTCallbackStartConfirm(),如果编译过MT_SAPI_CB_FUNC则也会调用zb_MTCallbackStartConfirm()通知MT.看下zb_StartConfirm()
**************************************
void zb_StartConfirm( uint8 status )
{
// If the device sucessfully started, change state to running
if ( status == ZB_SUCCESS )
{
myAppState = APP_START;
}
else
{
// Try again later with a delay
osal_start_timerEx(sapi_TaskID, MY_START_EVT, myStartRetryDelay);
}
}
**************************************
最终改变myAppState值来通知应用层网络设备启动成功.
3、协调器(非自启动模式HOLD_AUTO_START)—以SimpleApp的灯节点SimpleControllerEB按K1作为协调器启动为例.假设初始化成功,网络建立成功.
程序大致流程:
main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件->等待按键->按K1写设备逻辑类型和启动选项到NV->zb_SystemReset()->main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件->zb_StartRequest()->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()->触发sapi_TaskID的ZDO_STATE_CHANGE事件->SAPI_StartConfirm()->zb_StartConfirm()->设置myAppState = APP_START通知应用层网络设备开启成功,使再按K1时执行允许绑定功能.
***************************************
***************************************
说明:
1、本文为个人学习笔记,纯属个人理解,错误不可避免,仅供参考.随时更新
2、细节基本不管,遇到问题再作分析,程序注释为个人原始注释内容,记录有些仓促.
3、欢迎交流,转载请注明出处,谢谢!
2010.7.09 ~XF