zigbee学习笔记4----初次接触zstack

1.写在前面

之前一直在写裸机下的cc2530的那部分,现在觉得这样子写没啥意思,所以不再写那部分了,需要到http://www.cnblogs.com/hustlzp/archive/2011/02/13/1952881.html,这里面有你们需要的,而且里面写的很不错,所以请需要的人请移步。在这篇学习笔记中将初次接触Ti公司的zstack。


2.揭开她的面纱

          在开始的第一个笔记中我写到了如何安装zstack,其实很多人可能和我一样,开始装了zstack不知道拿来干嘛的,特别是一些刚接触这个行业的人,所以我们先来搞清楚zstack是装来干嘛的!其实很简单,zstack安装了之后得到了不是一大堆文件夹吗?里面包含了很多TI开源的代码和不开源的一些库,我们在编程的时候需要用到这些代码和库,就像我们写c语言程序的时候很多#include这些,很多都是库文件一样,而且Ti给了很多例子程序给我们,我们开始学习就从这些例子程序开始学起,特别重要的就是学习不是只看懂TI的例程就可以了,还要学会去修改TI的例程,把一个实现的简单的功能的程序,改编成一个实现复杂功能的程序。

       工欲善其事,必先利其器,在学习zstack之前我们先要安装两个软件辅助我们学习,一个就是大名鼎鼎的source insight,一个同样是大名鼎鼎的IAR(8.0),source insight辅助我们查看代码,IAR作为开发编译工具使用,当然,IAR也能作为看代码的工具,只是没有source insight那么直观。这两个软件的安装使用请询问百度或者谷歌。


       我们先来看看我们装完zstack之后得到的目录结构


      在图中我们可以看到好几个目录,我在第一篇中都解析过这些目录里面有啥干啥用的,如果你不想在你安装的目录去弄这些东西,你完全可以把这些文件拷贝到别的地方去弄,我就把他铐到了G盘去,这样子做的好处就是,就算你够衰把文件搞不见了,你还有原来的那个,不至于又要重装一次。现在我们先来看看Projects这个文件夹,里面是Ti给我们的一些例子程序之类的东西。,Projects->zstack之后我们可以看到这样子的一堆目录


      
     同样来说说这些目录都是干啥的,第一个就够霸气了,HomeAutomation,会英文的都知道,这是智能家居的例子,里面包含了一个开关程序例子,灯光例子,还有一个空中升级的例子。Libraies我就不说了,这是库的文件夹,OTA是啥来的呢?这是Ti提供的一个空中升级的插件程序,你的程序不一定要用到。Samples这个文件夹放得是一下简单的例子程序,好了,其他的不说了,ZMain放的是main函数的文件,因为协议栈用到了很多种的板子,所以提供了不同类型板子的程序模块。我们一般用到的是TI2530DB里面的那部分程序。

     先来看看这个main函数,也许这个是c程序猿的通病,一下子就要找入口。

[cpp] view plaincopy
/********************************************************************* 
 * @fn      main 
 * @brief   First function called after startup. 
 * @return  don't care 
 */  
int main( void )  
{  
  // Turn off interrupts  
  osal_int_disable( INTS_ALL );  
  
  // Initialization for board related stuff such as LEDs  
  HAL_BOARD_INIT();  
  
  // Make sure supply voltage is high enough to run  
  zmain_vdd_check();  
  
  // Initialize board I/O  
  InitBoard( OB_COLD );  
  
  // Initialze HAL drivers  
  HalDriverInit();  
  
  // 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  
  osal_init_system();  
  
  // Allow interrupts  
  osal_int_enable( INTS_ALL );  
  
  // Final board initialization  
  InitBoard( OB_READY );  
  
  // 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()  

       也许有人会问,大循环去哪啦?别着急,其实main做了一大堆东西之后在osal_start_system这里就进入大循环了,

我们先来看看osal_start_system做了什么事情

[cpp] view plaincopy
void osal_start_system( void )  
{  
#if !defined ( ZBIT ) && !defined ( UBIT )  
  for(;;)  // Forever Loop  
#endif  
  {  
    osal_run_system();  
  }  
}  
这会儿你看到你可爱的大循环了吧,继续看看osal_run_system做了什么东西

[cpp] view plaincopy
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  
}  
也许现在你看不明白他写了什么,放心总有一天你看得明白的,可是有个疑问会不会冒出来了呢?那我自己的代码写在哪啊!难过,难道我还得改这个大循环不成?!别急,现在我们来说说这协议栈是怎么工作的。

       首先,我们要来说的就是zstack的操作系统---OSAL,我们的协议栈其实是运行在TI的OSAL操作系统之上的,我们的程序(在程序中叫做task)和协议栈的各层都是作为osal的一个个任务在运行,由osal来调度的。我们刚才看到的是如何去启动了osal系统而已,既然我们的程序和协议栈都是osal的一个任务,那么我们如何把自己的任务注册进来呢?这是接下来我们要说的问题,至于osal的基本机制,我们以后再说,现在要做的是先让大家能够下载些程序下去玩玩,满足一下虚荣之心先。

3.建立自己的任务


      还是按照例子来说比较好说,那么我们挑选的例子就是TI的一个基本的例子,实现了一些最基本的入网和收发数据亮灯程序。我挑选了ZStack-CC2530-2.5.1a\Projects\zstack\Samples\GenericApp这个工程来说明如何建立自己的任务,打开ZStack-CC2530-2.5.1a\Projects\zstack\Samples\GenericApp\CC2530DB里面的IAR工程,我们可以看到这样子的架构



        当然你重头到尾建立这样子一个工程是挺费劲的,TI已经提供好了,那就用TI的例子修改就行了,没必要重头到尾建立一次,我建过,还是要看着TI的配置来做,很麻烦,很多配置东西你要清楚才能配置好。我们要建立自己任务,那么这些东西都在App这个Group里面,看看里面有啥东西



         可以看出只有三个文件,这三个文件却大有头,这样子的架构几乎成了TI的zstack的程序架构,几乎所有的都是这样子写的,GenericApp.h放的是一些用户的宏定义,GenericApp.c实现我们自己的任务代码,OSAL_GenericApp.c主要是实现osal协议的协议初始化接口的编写,我们的任务添加,就靠这三个文件了!好,我们先不看我们的任务代码是怎么实现的,我们先看OSAL_GenericApp.c是怎么添加我们的任务的和初始化osal的任务的,内容不多,就全放上来吧


[cpp] view plaincopy
#include "ZComDef.h"  
#include "hal_drivers.h"  
#include "OSAL.h"  
#include "OSAL_Tasks.h"  
  
#if defined ( MT_TASK )  
  #include "MT.h"  
  #include "MT_TASK.h"  
#endif  
  
#include "nwk.h"  
#include "APS.h"  
#include "ZDApp.h"  
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )  
  #include "ZDNwkMgr.h"  
#endif  
#if defined ( ZIGBEE_FRAGMENTATION )  
  #include "aps_frag.h"  
#endif  
  
#include "GenericApp.h"  
  
/********************************************************************* 
 * GLOBAL VARIABLES 
 */  
  
// The order in this table must be identical to the task initialization calls below in osalInitTask.  
const pTaskEventHandlerFn tasksArr[] = {  
  macEventLoop,  
  nwk_event_loop,  
  Hal_ProcessEvent,  
#if defined( MT_TASK )  
  MT_ProcessEvent,  
#endif  
  APS_event_loop,  
#if defined ( ZIGBEE_FRAGMENTATION )  
  APSF_ProcessEvent,  
#endif  
  ZDApp_event_loop,  
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )  
  ZDNwkMgr_event_loop,  
#endif  
  GenericApp_ProcessEvent  
};  
  
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );  
uint16 *tasksEvents;  
  
/********************************************************************* 
 * FUNCTIONS 
 *********************************************************************/  
  
/********************************************************************* 
 * @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);  
  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  
  GenericApp_Init( taskID );  
}  

        其实里面就两样东西是重要的,一个是tasksArr这个数组,一个是osalInitTasks这个函数
        tasksArr其实就是osal的一个任务列表,里面包含了osal系统中所有的任务,里面有我们任务处理事件的循环,当有消息是给我们的任务的时候osal就会调用我们的任务处理循环GenericApp_ProcessEvent,当然啦,osal没有win系统那么牛叉,更没有linux系统那么让人感到可爱,他是一个让人感到不恶心,可是不讨人喜欢的小小的实时操作系统。osalInitTasks这个其实就是任务初始化函数,在上面的osal_start_system之前会被调用,而且这个函数会调用很多任务的初始化函数,同样包括我们自己的任务的初始化函数GenericApp_Init,至于GenericApp_ProcessEvent和GenericApp_Init等下再说,我们先来看看到底是啥时候调用了osalInitTasks,以及tasksArr是如何被osal使用的。

     我们回到main函数中,可以看到这样子的一个函数被调用了osal_init_system,我们再看看看osal_init_system做了什么

[cpp] view plaincopy
uint8 osal_init_system( void )  
{  
  // Initialize the Memory Allocation System  
  osal_mem_init();  
  
  // Initialize the message queue  
  osal_qHead = NULL;  
  
  // Initialize the timers  
  osalTimerInit();  
  
  // Initialize the Power Management System  
  osal_pwrmgr_init();  
  
  // Initialize the system tasks.  
  osalInitTasks();  
  
  // Setup efficient search for the first free block of heap.  
  osal_mem_kick();  
  
  return ( SUCCESS );  
}  


        很明显,在这里他不但初始化了osal的内存管理,电源管理,并且调用了我们的osalInitTasks函数来初始化osal的所有task,所以在系统进入大循环之前,这些任务是被初始化了的,而osal是如何知道我们的这些任务的呢,就是通过tasksArr这个数组。我们来看看osal_run_system可以看到这样子一段events = (tasksArr[idx])( idx, events );由此可以看出,我们的任务是这样子被添加进来的,并且任务初始化的时候每个task都带了一个taskID,osal就是根据这个taskID来区分每个task,event是哪个task发起的,应该发给哪个task。


        至于GenericApp_ProcessEvent和GenericApp_Init是怎么实现的,下篇再说。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值