zoukankan      html  css  js  c++  java
  • ZigBee开发(12)--协议栈工作原理

    由于我的学习平台是基于 TI 公司的,所以讲述的当然也是 TI Z-STACK

    协议栈下载链接  https://pan.baidu.com/s/1QCO1-E_UXsad__e3R8fQkw
    TI 公司搭建了一个小型的操作系统(本质也是大型的程序),名叫 Z-stack。他们帮你考虑底层和网络层的内容,将复杂部分屏蔽掉。

    让用户通过 API 函数就可以轻易用 ZigBee。这样大家使用他们的产品也理所当然了,确实高明。

    也就是说,协议栈是一个小操作系统。不要听到是操作系统就感觉到很复杂。回想当初学习 51 单片机时候是不是会用到定时器的功能?

    我们会利用定时器计时,令 LED 一秒改变一次状态。好,现在进一步,我们利用同一个定时器计时,令 LED1 一秒闪烁一次, LED2 二秒闪烁一次。这样就有 2个任务了。

    再进一步n LED,就有 n 个任务执行了。协议栈的最终工作原理也一样。从它工作开始,定时器周而复始地计时,有发送、接收等任务要执行时就执行。这个方式称为任务轮询:

    打开协议栈文件夹 Texas InstrumentsProjectszstack 。里面包含了 TI 公司的例程和工具。其中的功能我们会在用的的实验里讲解。再打开 Samples 文件夹:

    Samples 文件夹里面有三个例子: GenericAppSampleAppSimpleApp在这里们选择 SampleApp 对协议栈的工作流程进行讲解。

    打开SampleAppCC2530DB 下工程文件 SampleApp.eww。留意左边的工程目录, 我们暂时只需要关注 Zmain 文件夹和 App 文件夹。

    任何程序都在 main 函数开始运行, Z-STACK 也不例外。打开 Zmain.C,  找到 int main( void ) 函数。我们大概浏览一下 main 函数代码:

     1 /*********************************************************************
     2  * @fn      main
     3  * @brief   First function called after startup.
     4  * @return  don't care
     5  */
     6 int main( void )
     7 {
     8   // Turn off interrupts
     9   osal_int_disable( INTS_ALL ); ////关闭所有中断
    10 
    11   // Initialization for board related stuff such as LEDs
    12   HAL_BOARD_INIT();//初始化系统时钟
    13 
    14   // Make sure supply voltage is high enough to run
    15   zmain_vdd_check();          //检查芯片电压是否正常
    16 
    17   // Initialize board I/O
    18   InitBoard( OB_COLD );       //初始化 I/O , LED 、 Timer 等
    19 
    20   // Initialze HAL drivers
    21   HalDriverInit();            //初始化芯片各硬件模块
    22 
    23   // Initialize NV System
    24   osal_nv_init( NULL );       // 初始化 Flash 存储器
    25 
    26   // Initialize the MAC
    27   ZMacInit();                 //初始化 MAC 层
    28 
    29   // Determine the extended address
    30   zmain_ext_addr();           //确定 IEEE 64 位地址
    31 
    32 #if defined ZCL_KEY_ESTABLISH
    33   // Initialize the Certicom certificate information.
    34   zmain_cert_init();
    35 #endif
    36 
    37   // Initialize basic NV items
    38   zgInit();                   // 初始化非易失变量
    39 
    40 #ifndef NONWK
    41   // Since the AF isn't a task, call it's initialization routine
    42   afInit();
    43 #endif
    44 
    45   // Initialize the operating system
    46   osal_init_system();         // 初始化操作系统
    47 
    48   // Allow interrupts
    49   osal_int_enable( INTS_ALL );// 使能全部中断
    50 
    51   // Final board initialization
    52   InitBoard( OB_READY );      // 初始化按键
    53 
    54   // Display information about this device
    55   zmain_dev_info();           //显示设备信息
    56 
    57   /* Display the device info on the LCD */
    58 #ifdef LCD_SUPPORTED
    59   zmain_lcd_init();     
    60 #endif
    61 
    62 #ifdef WDT_IN_PM1
    63   /* If WDT is used, this is a good place to enable it. */
    64   WatchDogEnable( WDTIMX );
    65 #endif
    66 
    67   osal_start_system(); // No Return from here  执行操作系统,进去后不会返回
    68 
    69   return 0;  // Shouldn't get here.
    70 } // main()

    看了上面的代码后,感觉很多函数不认识。不过,代码很有条理性,开始先执行初始化工作。包括硬件、网络层、任务等的初始化。

    然后执行 osal_start_system();操作系统。进去后可不会回来了。在这里, 重点了解 2 个函数:
    c) 初始化操作系统
      osal_init_system();
    d) 运行操作系统
      osal_start_system();

    /************怎么看?在函数名上单击右键——go to definition of…,(或把光标放到函数上,按F12)便可以进入函数。 ********************/

    osal_init_system();  系统初始化函数,进入函数。发现里面有 6个初始化函数,没事,这里只关心osalInitTasks();任务初始化函数。继续由该函数进入。

    看下面代码的注释应该能有一些规律

    /*********************************************************************
     * @fn      osalInitTasks
     *
     * @brief   This function invokes the initialization function for each task.
     *
     * @param   void
     *
     * @return  none
     */
    void osalInitTasks( void )
    {
      uint8 taskID = 0;
    
      // 分配内存,返回指向缓冲区的指针
      tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
      // 设置所分配的内存空间单元值为0
      osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
    
      // 任务优先级由高向低依次排列,高优先级对应taskID 的值反而小
      macTaskInit( taskID++ );  //macTaskInit(0) ,用户不需考虑
      nwk_init( taskID++ );     //nwk_init(1),用户不需考虑
      Hal_Init( taskID++ );     //Hal_Init(2) ,用户需考虑
    #if defined( MT_TASK )
      MT_TaskInit( taskID++ );
    #endif
      APS_Init( taskID++ );      //APS_Init(3) ,用户不需考虑
    #if defined ( ZIGBEE_FRAGMENTATION )
      APSF_Init( taskID++ );
    #endif
      ZDApp_Init( taskID++ );    //ZDApp_Init(4) ,用户需考虑
    #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
      ZDNwkMgr_Init( taskID++ );
    #endif
      //用户创建的任务
      SampleApp_Init( taskID );  // SampleApp_Init _Init(5) ,用户需考虑
    }

    可以这样理解,函数对 taskID 个东西进行初始化,每初始化一个,taskID++

    注释后面有些写着用户需要考虑,有些则写着用户不需考虑。没错,需要考虑的用户可以根据自己的硬件平台或者其他设置,而写着不需考虑的也是不能修改的。

    TI 公司出品协议栈已完成的东西。 SampleApp_Init(taskID );很重要,是应用协议栈例程的必需要函数,用户通常在这里初始化自己的东西。
    至此, osal_init_system();大概了解完毕。

    再来看第二个函数 osal_start_system();运行操作系统。同样用 go todefinition 的方法进入该函数。 /********************************************************************* * @fn osal_start_system *

     * @brief
     *
     *   This function is the main loop function of the task system (if
     *   ZBIT and UBIT are not defined). This Function doesn't return.
      这个是任务系统轮询的主要函数。他会查找发生的事件然后调用相应的事件执行函数。如果没有事件登记要发生,那么就进入睡眠模式。这个函数是永远不会返回的。 * * @param void * * @return none
    */ void osal_start_system( void ) { #if !defined ( ZBIT ) && !defined ( UBIT ) for(;;) // Forever Loop #endif { osal_run_system(); } } /********************************************************************* * @fn osal_run_system * * @brief * * This function will make one pass through the OSAL taskEvents table * and call the task_event_processor() function for the first task that * is found with at least one event pending. If there are no pending * events (all tasks), this function puts the processor into Sleep. * * @param void * * @return none */ void osal_run_system( void ) { uint8 idx = 0; osalTimeUpdate(); Hal_ProcessPoll(); do { if (tasksEvents[idx]) // Task is highest priority that is ready. { break; } } while (++idx < tasksCnt); if (idx < tasksCnt) { uint16 events; halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); events = tasksEvents[idx]; tasksEvents[idx] = 0; // Clear the Events for this task. HAL_EXIT_CRITICAL_SECTION(intState); activeTaskID = idx; events = (tasksArr[idx])( idx, events ); activeTaskID = TASK_NO_TASK; HAL_ENTER_CRITICAL_SECTION(intState); tasksEvents[idx] |= events; // Add back unprocessed events to the current task. HAL_EXIT_CRITICAL_SECTION(intState); } #if defined( POWER_SAVING ) else // Complete pass through all task events with no activity? { osal_pwrmgr_powerconserve(); // Put the processor/system into sleep } #endif /* Yield in case cooperative scheduling is being used. */ #if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0) { osal_task_yield(); } #endif }

    进入 tasksEvents[idx]数组定义,如图 3.4H,发现恰好在刚刚 osalInitTasks( void )函数上面。而且 taskID 一一对应。这就是初始化与调用的关系。 taskID 把任务联系起来了。

  • 相关阅读:
    java之this关键字
    单位转换类UnitUtil2
    重量WeightFormatUtil辅助类
    语音提示辅助类MySoundAlertUtil
    Android 编程下 Touch 事件的分发和消费机制
    switch case语句的用法
    Struts2之环境配置
    CSS属性绘制图形(一)
    jquery $(document).ready() 与window.onload的区别
    Android开发之ActionBar
  • 原文地址:https://www.cnblogs.com/tianxxl/p/9885384.html
Copyright © 2011-2022 走看看