安装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里存放起来。任务间的通信可以通过改变相应任务的事件信息来实现.