Zigbee协议栈的实现方式采用的是分层的思想,分别有物理层、数据链路层(介质访问控制层)、网络层和应用层。每一层都实现了不同的功能,但是每一层实现的功能对于其它层来说又是封闭的,如果要进行数据互通,需要调用一些API函数。这是一些浅显的基本概念,百度一下都可以知道的啦!那么整个协议栈是如何执行的呢?我们直接来看代码吧!打开Zmain.c文件,之前是一些宏定义,暂时先不用管,看到主函数:
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()
上述代码蓝色部分都是一些宏定义和注释,只要看黑色字体部分就好了,不难发现都是一些初始化操作,直到osal_start_system();这个函数才是真正系统运行的开始,那么我来分析一下这个函数的具体运行机理吧,自己也是学来的,说的不好请见谅哦!
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_timer().
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);
events = (tasksArr[idx])( idx, events );
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
}
}
首先,我们可以发现上述代码是个无限循环FOR函数,紧接着的两个函数osaltimeupdate()应该也是初始化定时器吧,HAL_ProcessPoll()是对一些硬件轮询,检查是否发生参数变化之类的,接下来就是一个do...while的循环函数,意思是如果tasksEvents[idx]不为0那么就跳出循环,否则idx自增1,与tasksCnt比较,如果没有超过tasksCnt就继续循环do...while,如果超过了就跳出循环,如果再摸一个idx的循环过程中tasksEvent[idx]非空,则执行下面的代码,将tasksEvents[idx]赋值给events,然后清零,对(tasksArr[idx])( idx, events )求解并将值赋值给events,最后再和tasksEvents[idx]做或运算。那么这整个过程是什么意思呢?我们要来看一下tasksArr[idx]和tasksEvents是如何定义的。首先看tasksArr[idx]:
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
PuppyApp_ProcessEvent
};
不难发现,这是一个数组,数组的每个元素都是一个函数指针,指向了不同的事件处理函数,函数名即函数首地址。我们再找tasksEvents在哪里定义的,发现osalInitTasks( void )这个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
PuppyApp_Init( taskID );
}
在这个函数中,先定义了一个任务ID号,tasksEvents所指向的地址长度是两个字节,然后使tasksEvents指向一个为任务总数*2个字节大小的空间的首地址,并将空间内容初始化为0,这里就可以知道tasksEvents其实就是指向每个任务事件的指针了。而且不难发现,这个函数中的任务排序和tasksArr[]数组定义的排序是一样的。事实上,当摸个tasksEvents[idx]非空时,就表明有对应该任务的事件要处理,可能是一件,也可能是很多件。然后通过idx在taskArr[idx]中找到相应的事件处理函数进行处理,处理完了之后有这样一句指令return(events^SYS_EVENT_MSG),当然后面的宏定义可能不一样,这是一个异或处理,1^1=0,1^0=1,也就是说SYS_EVENT_MSG这个事件处理完了清零了,剩下的events继续反馈上去,进行下一轮的循环然后处理。
整个协议栈轮询的过程就是这样,是不是很清楚了呢!没清楚也没关系,我会再写一篇按键的小例子帮助理解~!