第二讲:Zstack协议栈初窥(二)
时间:10-02
整理:3721RD
点击:
上次我们粗略描述了ZStack协议栈main()函数调用的一些函数,在main()函数的最后,调用的是osal_start_system(),从字面意思我们不难理解,这个函数是要启动“系统”的意思。启动系统?也许你要问了,这个系统难不成是操作系统的意思么?好吧,其实,你也可以理解成就是一个“操作系统”了。这时候可能你可能就来兴致了:ZStack运行的是哪门子操作系统?它是如何工作的?
查看osal_start_system()如下:
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
好吧,一个永不停止的for循环,目的就是不断执行osal_run_system(),这个也很好理解,永不停歇地运行系统,那么怎么运行呢?让我们再继续深入osal_run_system():
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
}
是不是有点头晕了,不要着急,我们慢慢来。osalTimeUpdate()和Hal_ProcessPoll()一个是更新系统时钟,另一个是处理POLL事件,还记得上个帖子里说到的按键处理的两种方式吗?中断方式和POLL方式。对,这个函数就是来处理启用了POLL方式任务,比如:SPI POLL、UART POLL等等。这里我们不详细解释了,感兴趣的朋友可以自己研究。
这里我们主要说一下osal_init_system()这个函数,等等,这个函数不是在第一个帖子里已经说过了吗?怎么又回去了呢?接下来难道不是该说do while这个条件语句了么?不要着急,只有看懂了osal_init_system()我们才能更深刻理解下面的内容。zstack里对这个函数的解释是“Initialize the operating system”,即初始化操作系统。进一步来看一下osal_init_system(),它包含了下面几个函数:
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 );
}
从函数后面的英文注释中我们不难理解每个函数的意义,主要有内存分配、定时器、电源管理系统等等的一系列初始化过程。这里面最重要的是osalInitTasks(),即系统任务初始化,zstack采用了“任务管理机制”来确保整个系统的正常运行。当然,很多别的操作系统也都采用了任务管理机制,Zstack这一点并没有什么特别之处。那么,我们就来详细了解一下zstack的任务管理机制。
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
zcl_Init( taskID++ );
zclSampleSw_Init( taskID );
}
上面贴出的是osalInitTasks()函数的具体内容。函数首先为tasksEvents指定了存储空间,它是用来保存每个任务的具体内容,初始化时被置为0,表示没有任务;非0值则表示有任务需要处理。tasksEvents里的成员数值会伴随着事件的发生被相应地发生改变。另外可以看到,在这里面taskID被多次用到。taskID是zstack为自己的每个任务分配的id号码,id越小的任务的优先级就越高。一些对于协议栈来说比较重要的任务如mac层、网络层的任务,它们的id值就比较小。在初始化函数的最后是zclSampleSw_Init,这个其实是应用层的初始化,它被放到了zcl_samplesw.c里,也就是说应用层任务的优先级别是最低的。我们对于协议栈的修改基本上也都集中在这里,这个我们放到以后再说。
好了,目前我们已经完成了zstack任务的初始化过程,那么当一个任务被触发时,zstack又是如何处理的呢?好,这里就要涉及到另外一个重要的数组: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
#if defined( INTER_PAN )
StubAPS_ProcessEvent,
#endif
zcl_event_loop,
#if defined ( ZCL_KEY_ESTABLISH )
zclKeyEstablish_event_loop,
#endif
esp_event_loop
};
聪明的朋友也许已经看出来了,任务数组各成员的名称和刚才初始化过程中各个初始化函数的名称有某些契合。呵呵,不契合就怪了(好吧,这里有点废话了)。
(图1:某一时刻tasksEvents各成员的数值)
上面的图示列出的是某一时刻tasksEvents各成员的数值。可以看到tasksEvents成员值可以有多个非0值,即有多个任务需要进行处理。优先级较高的nwk_event_loop将首先被执行,然后是级别较低的Hal_ProcessEvent。
本文为与非网月光码头原创,未经允许谢绝转载。
更多内容请见:【深度分析Zigbee】Zigbee技术知多少?资深大牛对对碰
----------------------------
主讲嘉宾简 介:网名:月光码头。毕业于中国科学院电子学研究所,主要从事zigbee物联网方向的应用研究,尤其擅长TI RF芯片、和Silicon Lab MCU芯片的使用。现就职于上海理滋芯片设计公司,任研发部门经理,主要从事智能家居产品的设计开发,拥有5年多的zigbee软硬件开发经验。
------------------
推荐技术讲座:
听东北人讲天线:每周一个实例,个个经典!(CST仿真实践全包括)
【ADS学习小组】课程汇总(火烽主讲)
【HFSS学习小组】课程汇总(木木主讲)
查看osal_start_system()如下:
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
好吧,一个永不停止的for循环,目的就是不断执行osal_run_system(),这个也很好理解,永不停歇地运行系统,那么怎么运行呢?让我们再继续深入osal_run_system():
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
}
是不是有点头晕了,不要着急,我们慢慢来。osalTimeUpdate()和Hal_ProcessPoll()一个是更新系统时钟,另一个是处理POLL事件,还记得上个帖子里说到的按键处理的两种方式吗?中断方式和POLL方式。对,这个函数就是来处理启用了POLL方式任务,比如:SPI POLL、UART POLL等等。这里我们不详细解释了,感兴趣的朋友可以自己研究。
这里我们主要说一下osal_init_system()这个函数,等等,这个函数不是在第一个帖子里已经说过了吗?怎么又回去了呢?接下来难道不是该说do while这个条件语句了么?不要着急,只有看懂了osal_init_system()我们才能更深刻理解下面的内容。zstack里对这个函数的解释是“Initialize the operating system”,即初始化操作系统。进一步来看一下osal_init_system(),它包含了下面几个函数:
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 );
}
从函数后面的英文注释中我们不难理解每个函数的意义,主要有内存分配、定时器、电源管理系统等等的一系列初始化过程。这里面最重要的是osalInitTasks(),即系统任务初始化,zstack采用了“任务管理机制”来确保整个系统的正常运行。当然,很多别的操作系统也都采用了任务管理机制,Zstack这一点并没有什么特别之处。那么,我们就来详细了解一下zstack的任务管理机制。
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
zcl_Init( taskID++ );
zclSampleSw_Init( taskID );
}
上面贴出的是osalInitTasks()函数的具体内容。函数首先为tasksEvents指定了存储空间,它是用来保存每个任务的具体内容,初始化时被置为0,表示没有任务;非0值则表示有任务需要处理。tasksEvents里的成员数值会伴随着事件的发生被相应地发生改变。另外可以看到,在这里面taskID被多次用到。taskID是zstack为自己的每个任务分配的id号码,id越小的任务的优先级就越高。一些对于协议栈来说比较重要的任务如mac层、网络层的任务,它们的id值就比较小。在初始化函数的最后是zclSampleSw_Init,这个其实是应用层的初始化,它被放到了zcl_samplesw.c里,也就是说应用层任务的优先级别是最低的。我们对于协议栈的修改基本上也都集中在这里,这个我们放到以后再说。
好了,目前我们已经完成了zstack任务的初始化过程,那么当一个任务被触发时,zstack又是如何处理的呢?好,这里就要涉及到另外一个重要的数组: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
#if defined( INTER_PAN )
StubAPS_ProcessEvent,
#endif
zcl_event_loop,
#if defined ( ZCL_KEY_ESTABLISH )
zclKeyEstablish_event_loop,
#endif
esp_event_loop
};
聪明的朋友也许已经看出来了,任务数组各成员的名称和刚才初始化过程中各个初始化函数的名称有某些契合。呵呵,不契合就怪了(好吧,这里有点废话了)。
(图1:某一时刻tasksEvents各成员的数值)
上面的图示列出的是某一时刻tasksEvents各成员的数值。可以看到tasksEvents成员值可以有多个非0值,即有多个任务需要进行处理。优先级较高的nwk_event_loop将首先被执行,然后是级别较低的Hal_ProcessEvent。
本文为与非网月光码头原创,未经允许谢绝转载。
更多内容请见:【深度分析Zigbee】Zigbee技术知多少?资深大牛对对碰
----------------------------
主讲嘉宾简 介:网名:月光码头。毕业于中国科学院电子学研究所,主要从事zigbee物联网方向的应用研究,尤其擅长TI RF芯片、和Silicon Lab MCU芯片的使用。现就职于上海理滋芯片设计公司,任研发部门经理,主要从事智能家居产品的设计开发,拥有5年多的zigbee软硬件开发经验。
------------------
推荐技术讲座:
听东北人讲天线:每周一个实例,个个经典!(CST仿真实践全包括)
【ADS学习小组】课程汇总(火烽主讲)
【HFSS学习小组】课程汇总(木木主讲)
这讲和“进程”的C语言处理相似呢
第二期啦,来学习
我来的还很早啊
报道~~~
很好
学习
我也才刚开始学习zigbee。顶
kan kan
看看是什么东东看看是什么东东看看是什么东东
为什么要研究ZStack呢
已经在学习了,很好。
学习中
好东西支持
学习zigbee
继续学习,顶
好!
不错的文章~给入门级的菜鸟们有了好的指导
hhhhhhhhhaaaaaaaaaoooooooo
学习第二弹
学习了,谢谢分享!
不错
ding
不错
我来复制到文档中看
来看看
来了,多多支持
太好了
来顶小编一下,顺便看一看附件。
好东西,一直在找相关的资料。