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 Instruments\Projects\zstack 。里面包含了 TI 公司的例程和工具。其中的功能我们会在用的的实验里讲解。再打开 Samples 文件夹:

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

打开\SampleApp\CC2530DB 下工程文件 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 把任务联系起来了。

猜你喜欢

转载自www.cnblogs.com/tianxxl/p/9885384.html