06 ZStack的task工作流程分析

来源:互联网 发布:ps淘宝详情页参数 编辑:程序博客网 时间:2024/06/05 07:54

安装ZStack-CC2530-2.5.1a.exe

默认安装到”C:\Texas Instruments\ZStack-CC2530-2.5.1a”目录下.
这里写图片描述

Components目录里实现 hal层、stack网络协议栈、mac层的封装, 而且也实现了osal操作系统的功能等. 通常情况下我们就是调用里面实现好的功能函数就可以了.

Documents目录里存放有各个分层的的功能函数的说明文档.

Projects目录存放TI提供的应用案例,我们可以基于这些案例上根据项目需要求进行修改.

Tools目录里存放TI提供的,用于在pc端使用的工具.

/////////////////////////////////////////////
这里写图片描述
打开此目录下的SampleApp.eww工程.

///////////////
在此位置选择要编译的设备类型, 不管是要作协调器,路由器,终端设备都是共用一个工程源码的。只要选择不同的设备类型,在编译时会自动编译加入相应的设备角色功能.
这里写图片描述

工程里目录说明(摘自开发板文档):
这里写图片描述

ZStack虽然叫zigbee协议栈,但除了网络数据的协议栈外还有一个多任务的操作系统。
////////////////////////////////////////////////////////////////////////////////////////////////////
协议栈里的操作系统工作原理分析:
里面的操作系统是支持多任务(多进程)同时执行的, 每个任务都是基于事件触发调度的。简单来说, 此系统就是在main函数里用个死循环,循环检查每个任务是否有事件发生, 如有事件发生则调用此任务的事件处理函数。

每个任务都有一个唯一的任务号,根据此任务号可以区别相应的任务。每个任务的事件都由一个uint16类型变量来存放,每种事件占用一位,相应的位为1即表示有相应的事件发生。而且每个任务都有一个事件处理函数,只要有事件发生了,系统就会调用任务的事件处理函数来处理已发生的事件. 除此外,每个任务还有一个初始化函数,用于记录分配的任务号并作任务工作前的初始化工作

在OSAL_SampleApp.c源文件里:

每个任务的事件处理函数地址由全局函数指针数组tasksArr数组来存放。此数组的元素个数即就是系统里的任务个数。同时此数组里是按任务的任务号从小到大顺序来存放的,即可以tasksArr[任务号]得到相应任务的事件处理函数地址.
//协议栈里的每个分层都由一个任务来实现相应的功能。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  SampleApp_ProcessEvent  // SampleApp任务的事件处理函数};const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); //此全局变量记录任务的个数uint16 *tasksEvents; //注意全局指针变量tasksEvents其实相当于一个数组(uint16 tasksEvents[tasksCnt]), 每个任务的事件都由一个uint16元素来记录。 即tasksEvents[任务号]可得到相应任务的事件信息.void osalInitTasks( void ) //系统初始化所有任务的函数,此函数在系统初始化后被调用{  uint8 taskID = 0; //用于分配的任务号  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); //这里动态分配空间,用于记录每个任务的事件信息.  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); //就是先把每个任务的事件信息清零  //下面是调用每个任务的初始化函数,并传递分配的任务号。注意此顺序需与在tasksArr函数指针数组里的初始化顺序一致.  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 ); //调用SampleApp任务的初始化函数}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
系统的具体工作流程:

系统最先是从工程的ZMain目录里的ZMain.c的79行的main函数开始执行的.

//这里只关注任务的调度,其它的就不深入了int main(void){    ...    osal_init_system();  //系统初始化函数        ...        //里面有调用上面的系统任务函数        osalInitTasks();            ...            //再进一步也就会调用各个任务的初始化函数,也包括SampleApp任务的初始化函数            SampleApp_Init( taskID );     ...    //main函数最后,就是开始系统工作了    osal_start_system();            //osal_start_system函数里面就是个死循环,循环调用osal_run_system        osal_run_system(); //此函数就是任务调度的实现}void osal_run_system( void ){  uint8 idx = 0; //此变量用于记录当前检查的任务的任务号  osalTimeUpdate();  Hal_ProcessPoll();  do {    if (tasksEvents[idx])  //不要忘了tasksEvents就是一个记录所有任务事件的数组,以任务号为下标.    {      break;  //如果tasksEvents[idx]不为零,则表示任务号为idx的任务有事件发生需要处理事件, 结束当前循环.    }  } while (++idx < tasksCnt); //tasksCnt为任务的个数  if (idx < tasksCnt)  // 有事件发生的任务需处理  {    uint16 events;    halIntState_t intState;    HAL_ENTER_CRITICAL_SECTION(intState); //进入防止并发处理的状态(上锁)    events = tasksEvents[idx]; //先用events变量记录任务的事件    tasksEvents[idx] = 0;  // Clear the Events for this task.    HAL_EXIT_CRITICAL_SECTION(intState); //退出防止并发的状态(解锁)    activeTaskID = idx;    events = (tasksArr[idx])( idx, events ); //不要忘了tasksArr是一个全局函数指针变量数组,记录所有任务的事件处理函数的地址. 这里调用有事件发生的任务的事件处理函数,并传递它的任务号及事件信息。最后用events变量存放返回值,返回值为尚未处理的事件信息,如返回0则表示当前已发生事件都已经处理了.    ...    HAL_ENTER_CRITICAL_SECTION(intState);    tasksEvents[idx] |= events; //把没有处理的事件信息存放到tasksEvent数组里,下次检查时,将会处理还没有处理的事件.    HAL_EXIT_CRITICAL_SECTION(intState);  }    ...}

总结,每个任务有一个初始化函数及事件处理函数, 而且所有任务的事件都在tasksEvent里存放起来。任务间的通信可以通过改变相应任务的事件信息来实现.