关于Z-STACK的任务处理机制已经有很多前辈写过了一些教程,我这里写一点自己的理解。
首先认识几个名词:
协议:标准,约定。
协议栈:各层协议的总和,也就是实现这些协议的代码。
Z-STACK:zigbee协议栈的名字。
OSAL类似于操作系统,是以实现多任务为核心的系统资源管理机制。
任务:又可称为线程(个人理解,这里说的可能不对),是一个简单的任务执行过程,在任务执行的过程中CPU完全属于该任务。在Z-STACK中,不同的层拥有不同的任务ID,并且这个任务ID的号码与该层任务的执行次序一致。
Z-STACK中重要的三个变量:
TasksCnt,TasksEvents,tasksArr[idx];
TasksCnt:任务的总个数,写死了,不会变化。
TasksEvents:是个指针,当然同时也是一个数组。其索引idx代表不同的层,由于Z-stack的每一个层都有一个唯一的任务ID,所以idx的值与同层的任务ID值一样。同时,数组的某个元素的值表示了该层有几个事件。下面以应用层为例说明:
看tasksEvents的定义与动态内存分配:
uint16 *tasksEvents;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
等价为uint16 tasksEvents[TasksCnt],一个数组,数组中的每个元素都是个两字节的数字,共有TasksCnt个元素。
再看OSAL的任务初始化函数
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 );
}
发现每个层的初始化函数传入的参数taskID都是递增的,这个taskID也就是本层的任务号,同时也是本层tasksEvents[idx]中idx的值。例如GenericApp_Init( taskID ),这个是应用层的初始化(用户自定义的函数,可能名字会不一样),到这里的taskID值为8的话,那么代表用户自定义任务的tasksEvents[idx]中idx的值也是8。
再看tasksEvents[idx]这个元素的值,代表的是某一层的事件,初始值0x0000,按位展开就是:0000 0000 0000 0000,每一个二进制位都可以用来设置一个事件,例如系统事件宏定义:
#define SYS_EVENT_MSG 0x8000
按位展开就是1000 0000 0000 0000,
假如我们定义一个发送事件为
#define SERIALAPP_SEND_EVT 0x0001,
按位展开就是0000 0000 0000 0001,
那么假如tasksEvents[8]= 0x8001,就代表着同时有SYS_EVENT_MSG和SERIALAPP_SEND_EVT两个事件。
任务调度中有个死循环,不断检测这个元素的值是否非0,检测到
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;//有事件发生则跳出循环
}
} while (++idx < tasksCnt);
具体怎么处理每个任务的不同事件,见tasksArr[idx];
tasksArr[idx]的定义:
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
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
};
可以看出tasksArr[idx]是一个指针(也就是数组),指向一个函数(数组元素是函数),函数的参数就是task_id与events。task_id与本层的任务ID是对应的,当然也对应着tasksEvents[idx]中idx的值,而events正好也对应这元素tasksEvents[idx]的值。如
uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )
这些函数也就是各个层的处理函数。
再来看一下每个事件具体是怎样处理的:
在处理用户自定义的任务函数——GenericApp_ProcessEvent函数中,有如下语句:
如果检测到SYS_EVENT_MSG(也就是tasksEvents[8]的二进制位最高位是1)
if ( events & SYS_EVENT_MSG )
{
处理语句
}
return (events ^ SYS_EVENT_MSG);
返回值执行了异或操作,也就是说把SYS_EVENT_MSG占据的这一个二进制位清零了,代表这个事件执行完毕,且不会影响其它位所代表的事件。
例如此时这一层的任务有两个事件SYS_EVENT_MSG与SERIALAPP_SEND_EVT(此事件是自定义的),那么传入的参数events值就是0x8001,执行完return (events ^ SYS_EVENT_MSG)之后值为0x0001,不会影响if ( events & SERIALAPP_SEND_EVT )的判断结果。当然,由于函数已经执行了返回值的操作,无法再执行后边的语句,SERIALAPP_SEND_EVT事件只能等到下一次执行GenericApp_ProcessEvent函数的时候,才有可能处理。
再来看一下死循环osal_run_system中的任务处理相关语句:
events = tasksEvents[idx];//提取需要处理的任务事件
tasksEvents[idx] = 0; // Clear the Events for this task.
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );//通过指针调用处理函数,处理结束事件按位清零
activeTaskID = TASK_NO_TASK;
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
关注events,先从tasksEvents中提取idx层中的所有事件(可能不是一个),然后作为参数传入(tasksArr[idx])( idx, events )中进行处理,处理函数在处理了一个事件之后会执行一个异或操作,消除已经处理了的一个事件,然后作为tasksArr[idx]的返回值重新赋值给events,再有events传值给tasksEvents[idx],如果这一层的任务中还有别的事件,那么就由主函数的下一次循环来处理。
顺带提一下,占据二进制位的顺序越靠左,那么这个事件的优先级就越高。tasksEvents[idx]中idx的值越小,那么这一层任务的优先级也就越高。
以上