z-stack协议栈sampleapp分析介绍(一)

时间:2021-03-06 10:11:48

看了一段时间的z-stack协议栈了,想自己写写对协议栈的理解,这几天花时间整理一下看的东西,顺带着自己梳理梳理。对SampleApp的例子出分析,从main函数开始,主要想的是按照代码运行的流程做出分析,中间穿插一些需要知道和了解的东西(啰啰嗦嗦),还望大家指正。

1、main函数分析简单介绍

int main( void )
{
  // Turn off interrupts
  osal_int_disable( INTS_ALL );//关闭所有中断

  // Initialization for board related stuff such as LEDs
  HAL_BOARD_INIT();//初始化时钟和LED

  // Make sure supply voltage is high enough to run
  zmain_vdd_check();//检测供电电压

  // Initialize board I/O
  InitBoard( OB_COLD );


 // Initialze HAL drivers(介绍见1.1)
 ```c
  HalDriverInit();/*在本例程中用到了按键,所以在该函数的初始化*/
  /*包括AD的初始化HalAdcInit()和按键的初始化HalKeyInit()*/
  ```


  // Initialize NV System
  osal_nv_init( NULL );

  // Initialize the MAC
  ZMacInit();

  // Determine the extended address
  zmain_ext_addr();

#if defined ZCL_KEY_ESTABLISH
  // Initialize the Certicom certificate information.
  zmain_cert_init();
#endif

  // 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(见介绍1.2)
  osal_init_system();/*在这个函数里包括对OSAL系统的初始化,*/
  /*可以先理解osalInitTasks()函数,给每个Task分配任务ID,以及初始化任务*/

  // Allow interrupts
  osal_int_enable( INTS_ALL );

  // Final board initialization(见介绍1.3)
  InitBoard( OB_READY );/*在这个函数里调用*/
  /*了HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)函数,*/
/* 是回调函数,下面会讲到*/

  // 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.1、HalDriverInit()中的函数HalAdcInit()和HalKeyInit()

HalAdcInit()函数

void HalAdcInit (void)
{
#if (HAL_ADC == TRUE)
  adcRef = HAL_ADC_REF_VOLT;//adcRef赋值为0x80
#endif
}

HalKeyInit()函数,在这个函数做的工作是对两个按键端口P0.1、P2.0进行初始化的工作

void HalKeyInit( void )
{
  /* Initialize previous key to 0 */
  halKeySavedKeys = 0;

  HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    /* Set pin function to GPIO *///P0.1
  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    /* Set pin direction to Input */

  HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO *///P2.0
  HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */


  /* Initialize callback function */
  pHalKeyProcessFunction  = NULL;//没有回调函数

  /* Start with key is not configured */
  HalKeyConfigured = FALSE;//按键配置为FALSE
}

1.2、osal_init_system()函数

在这里可以看到,osal_init_system()函数的作用就是初始化系统的各个任务,以及给每个任务分配任务ID

void osalInitTasks( void )
{
  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++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  SampleApp_Init( taskID );
}

可以看到每一句都是taskID++,也就是给每个任务的ID都每次加一。关于tasksEvents 我们后边再做介绍。同时需要了解SampleApp_Init()函数。

void SampleApp_Init( uint8 task_id )
{
  SampleApp_TaskID = task_id;  //osal分配的任务ID,这里为6,随着用户添加任务的增多而改变
  SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态(无连接)
  /* 初始化应用设备的网络类型,设备类型的改变都要产生一个事件—ZDO_STATE_CHANGE, 从字面理解为ZDO状态发生了改变。所以在设备初始化的时候一定要把它初始化为什么 状态都没有。那么它就要去检测整个环境,看是否能重新建立或者加入存在的网络。 但是有一种情况例外,就是当NV_RESTORE被设置的候(NV_RESTORE是把信息保存在非 易失存储器中),那么当设备断电或者某种意外重启时,由于网络状态存储 在非易失存储器中,那么此时就只需要恢复其网络状态,而不需要重新建立或者加入 网络了.这里需要设置NV_RESTORE宏定义。 */
  SampleApp_TransID = 0; //消息发送ID(多消息时有顺序之分)


  // Device hardware initialization can be added here or in main() (Zmain.c).
  // If the hardware is application specific - add it here.
  // If the hardware is other parts of the device add it in main().

 #if defined ( BUILD_ALL_DEVICES )
  // The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START
  // We are looking at a jumper (defined in SampleAppHw.c) to be jumpered
  // together - if they are - we will start up a coordinator. Otherwise,
  // the device will start as a router.
  if ( readCoordinatorJumper() )
    zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR;
  else
    zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER;
#endif // BUILD_ALL_DEVICES

#if defined ( HOLD_AUTO_START )
  // HOLD_AUTO_START is a compile option that will surpress ZDApp
  // from starting the device and wait for the application to
  // start the device.
  /* 该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候 会暂停启动流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触 发它的启动流程。 */
  ZDOInitDevice(0);
#endif

/* 设置发送数据的方式和目的地址寻址模式*/
  // Setup for the periodic message's destination address
  // Broadcast to everyone
  /*广播到所有设备*/ 
  SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
  SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
  SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址

    //闪烁消息:发送到组 
  // Setup for the flash command's destination address - Group 1
  SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup;    //(组寻址)
  SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
  SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;

   /*定义本设备用来通信的APS层端点描述符*/ 
  // Fill out the endpoint description.
  SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
  SampleApp_epDesc.task_id = &SampleApp_TaskID;
  SampleApp_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
  SampleApp_epDesc.latencyReq = noLatencyReqs;

  // Register the endpoint description with the AF
    /*向AF层登记EP描述符*/
 /*登记endpoint description 到AF,要对该应用进行初始化并在AF进行登记, 告诉应用层有这么一个EP已经开通可以使用,那么下层要是有关于该应用的 信息或者应用要对下层做哪些操作,就自动得到下层的配合。*/ 
  afRegister( &SampleApp_epDesc );

  // Register for all key events - This app will handle all key events
  RegisterForKeys( SampleApp_TaskID );

  // By default, all devices start out in Group 1
     /*设定一个新的组*/ 
  SampleApp_Group.ID = 0x0001;
  osal_memcpy( SampleApp_Group.name, "Group 1", 7  );
  aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );

#if defined ( LCD_SUPPORTED )
  HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 );
#endif
}

除了上面注释中提到的东西,需要另外注意一段代码

RegisterForKeys( SampleApp_TaskID );

这段函数是在这么写的:

uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}

把SampleApp_TaskID的值赋值给registeredKeysTaskID,以便后边按键触发回调函数调用。

1.3 函数InitBoard( OB_READY )

这这个函数里会调用函数HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback),这个地方需要的注意的就是OnBoard_KeyCallback函数,按键不使用中断,同时定义了按键的回调函数是OnBoard_KeyCallback

void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
  /* Enable/Disable Interrupt or */
  Hal_KeyIntEnable = interruptEnable;

  /* Register the callback fucntion */
  pHalKeyProcessFunction = cback;

  /* Determine if interrupt is enable or not */
  if (Hal_KeyIntEnable)//如果使用中断的话
  {
    /* Rising/Falling edge configuratinn */

    PICTL &= ~(HAL_KEY_SW_6_EDGEBIT);    /* Clear the edge bit */
    /* For falling edge, the bit must be set. */
  #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
    PICTL |= HAL_KEY_SW_6_EDGEBIT;
  #endif


    /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */
    HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
    HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);



    /* Rising/Falling edge configuratinn */

    HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT);    /* Clear the edge bit */
    /* For falling edge, the bit must be set. */
  #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE)
    HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
  #endif


    /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */
    HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
    HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);


    /* Do this only after the hal_key is configured - to work with sleep stuff */
    if (HalKeyConfigured == TRUE)
    {
      osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    }
  }
  else    /* Interrupts NOT enabled 不使用中断方式*/
  {
    HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
    HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */

    osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
  }

  /* Key now is configured */
  HalKeyConfigured = TRUE;
}

在上面的函数中注意到osal_set_event函数,它会调用

tasksEvents[task_id] |= event_flag;

这句话个人感觉特别关键,就是给taskEvents[Hal_TaskID]置1,表示有按键事件,后边程序需要对按键做轮询操作。

先到这里吧,后边接着做介绍。