This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

关于zigbee ha1.2协议中终端自动启动问题

我用的TI官网的例子,没有对代码进行任何改动,只是删除了HOLD_AUTO_START定义,配置成自启动模式。

但是经调试发现终端设备程序执行一会就进入软复位,一直反复这个过程。

求解

  • 如果使用HOLD_AUTO_START选项,那么本工程就会禁止自动启动ZDApp事件处理循环中的ZDO_NETWORK_INIT事件,也就是上电后不自动调用ZDOInitDevice(),需要通过外部事件,或者用户自己调用这个函数,下面我们看看定义了这个函数后,程序的流程是怎么样的。

    ZDApp.c文件中,可以看到下面的定义:
    #if defined( HOLD_AUTO_START )
    devStates_t devState = DEV_HOLD;  // 初始化-不会自动启动
    #else
      devStates_t devState = DEV_INIT;  //初始化-没有连接到任何东西
    #endif
     
    #if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )
      // Set the default to coodinator
      devStartModes_t devStartMode = MODE_HARD;
    #else
      devStartModes_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
     
    #if !defined( ZDO_COORDINATOR ) || defined( SOFT_START )
      static uint8 retryCnt;
    #endif
        

    启动过程:

    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 );

    在调用用户自己定义的任务初始化函数之前,调用下面的初始函数,看看这里怎么处理,devState状态的。
    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;
      (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer. 加载IEEE地址
     
      // 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(); //初始化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_1devState也等于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();
    }
     
    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_COORDINATORREFLECTORSOFT_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宏以后,当按键按下后,就会重新启动网络设备。
  • 那怎么设置成自启动呢?不定义HOLD_AUTO_START就可以了吗?还有什么其它地方需要修改吗?

  • 自启动的话不预编译HOLD_AUTO_START即可