文章目录
一、Zigbee协议
想要了解Zigbee协议栈,首先要了解Zigbee协议。ZigBee的协议分别为下面两部分。
- IEEE 802.15.4定义的PHY(Physical Layer,物理层)和MAC(Media Access Control Layer,介质访问控制层)技术规范。
- ZigBee联盟(Zigbee Alliance)定义了NWK(Network Layer,网络层)、APS(Application Support Sub-Layer,应用程序支持子层)、APL(Application Layer,应用层)技术规范 。具体构成如下图所示。
Zigbee协议栈就是把各个层(Layer)集合在一起,以函数的形式实现,并提供用户API(Application Programming Interface,应用程序接口),用户只需调用API即可实现相应功能,而无需关心协议栈底层实现方法。
Zigbee无线网络协议层
二、Z-stack
Z-satck是挪威公司Chipcon(之后被TI收购)推出的商业级协议栈软件,同时提供一个名为OSAL(Operation System Abstract Layer,操作系统抽象层)的协议栈调度程序,它是一个小型的操作系统,采用任务轮询的方法运行。
三、TI Sample程序解析
安装了TI提供的Z-stack(里面包括各种开发文档和Demo)后,打开安装目录下…\Texas Instruments\ZStack-CC2530-2.5.1a\Projects\zstack\Samples\SampleApp\CC2530DB的项目。可以看到项目的架构如下所示,用户主要关心的程序部分是APP和ZMain目录下的文件。
(一)、main()
程序总是由main函数开始,所以我们就从这个地方开始吧。
/*********************************************************************
* @fn main
* @brief First function called after startup. 启动文件之后第一个被调用的函数
* @return don't care
*/
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 确保电压足够(2.0V以上)设备正常运行
zmain_vdd_check();
// Initialize board I/O 处理复位信号
InitBoard( OB_COLD );
// Initialze HAL drivers 初始化硬件抽象层驱动 TIMER/ADC/DMA/AES/LCD/LED/UART/KEY/SPI/HID
HalDriverInit();
// Initialize NV System 初始化NV(Non-Volatile)服务,即初始化闪存(Flash)
osal_nv_init( NULL );
// Initialize the MAC 初始化介质访问控制层
ZMacInit();
// Determine the extended address 从NV中获取存于其中的IEEE 64位地址并对设备进行配置
zmain_ext_addr();
#if defined ZCL_KEY_ESTABLISH
// Initialize the Certicom certificate information. 如果有证书的话,初始化认证证书信息
zmain_cert_init();
#endif
// Initialize basic NV items 初始化NV变量 包括初始化扩展的PAN ID作为设备地址等
zgInit();
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine 如果AF不是一个任务,则调用初始化程序
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 如果有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 执行操作系统,且不会返回数值 因为里面有for(;;)
return 0; // Shouldn't get here.
} // main()
接下来主要看OSAL系统初始化函数osal_init_system()和系统开启函数osal_start_system()。
(二)、osal_init_system()
该函数被main() 函数内部调用,它通过OSAL_Tasks.h中定义的任务表来初始化系统任务。
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()
此函数在上面的函数osal_init_system() 内部被调用,它的作用是为每个任务调用初始化函数。
void osalInitTasks( void )
{
uint8 taskID = 0;//任务ID 相当于任务优先级
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//分配内存,返回指向缓冲区的指针
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));//初始化内存
macTaskInit( taskID++ );//MAC任务优先级最高 优先级为0
nwk_init( taskID++ );//网络层 优先级为1
Hal_Init( taskID++ );//硬件抽象层 优先级为2 用户需要考虑修改!!!(如底层定时器设置等)
#if defined( MT_TASK )
MT_TaskInit( taskID++ );//Z-stack提供的各种驱动的初始化 优先级为3
#endif
APS_Init( taskID++ );//应用支持子层 优先级为4
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_Init( taskID++ );//应用支持子层碎片整理 优先级为5 ?
#endif
ZDApp_Init( taskID++ );//Zigbee Device 应用初始化 优先级为6 用户需要考虑修改!!!(如设备地址参数、按键、LED灯等)
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );//网络层管理任务 优先级为7
#endif
SampleApp_Init( taskID );//例程应用初始化 优先级为8 用户需要考虑修改!!!(如是否配置设备为协调器/路由器、配置设备端点Endpoint相关信息等)
}
初始化完任务之后主要应该关注的就是系统运行过程中发生什么事情了。
(四)、osal_start_system()
这个函数是任务系统的主循环函数(如果没有定义ZBIT和UBIT)。这个函数没有返回。
其中ZBIT和UBIT是用于TI内部测试所使用的一个宏定义,默认下是无定义的。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop 注意这个死循环后面是没有;的哦,即它会一直执行下面的osal_run_system()函数
#endif
{
osal_run_system();//无定义上面两个宏定义的话就一直运行系统
}
}
接下来应该关注的是osal_run_system()函数实现的内容。
(五)、osal_run_system()
这个是任务系统轮询的主要函数,它会查找将要发生的任务并通过taskEvents表来调用task_event_processor()函数,该任务至少有一个事件挂起。如果没有挂起事件(所有任务),这个函数将使处理器进入休眠状态。
void osal_run_system( void )
{
uint8 idx = 0;
osalTimeUpdate();//这里扫描(320us/次)哪个时间被触发了,然后置位相应的标志位
Hal_ProcessPoll();
do {
if (tasksEvents[idx]) // Task is highest priority that is ready. 高优先级任务就绪
{
break;
}
} while (++idx < tasksCnt);//得到待处理的最高优先级任务索引号index
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;//保存处于就绪状态的任务ID
events = (tasksArr[idx])( idx, events );//通过指针调用任务处理函数 此tasksArr表中的顺序必须与osalInitTask中的任务初始化调用相同。
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
}
通过下面的程序可以看到tasksArr[]是定义在osalInitTask函数内部的开头部分,而且它内部函数的位置与osalInitTasks内部函数优先级初始化一致,通过taskID将任务初始化和任务调用一一对应。所以如果要修改某个优先级,则tasksArr数组内部函数的位置也要相应改变。
// The order in this table must be identical to the task initialization calls below in osalInitTask. 此表中的顺序必须与下面osalInitTask中的任务初始化调用相同。
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
};
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
uint16 *tasksEvents;
/*********************************************************************
* FUNCTIONS
*********************************************************************/
/*********************************************************************
* @fn osalInitTasks
*
* @brief This function invokes the initialization function for each task.
*
* @param void
*
* @return none
*/
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
SampleApp_Init( taskID );
}
四、总结
OSAL系统运行过程如下程序流程图
参考链接:
Z-stack API
Z-stack Sample Applications
Z-stack Developer’s Guild
ZigBee协议栈Zstack介绍