一、Z-Stack 协议栈目录结构:
Z-Stack 协议栈可以从 TI 官网免费下载,下载安装完成以后,会默认在 C 盘的根目录下创建 Texas Instruments 目录,该目录下的子目录就是安装的 Z-Stack 文件,并且在该子目录下创建Accessories、 Components、Documents、Projects 和 Tools 等 5个文件夹。
(1)Accessories目录:
存放了各种辅助工具,如下图所示:
其中:
- OtaServer:存放了OTA(Over The Air,空中升级)测试工具
- SerialBootTool:存放了串口升级测试工具。
(2)Components目录:
存放了Z-Stack核心源代码和链接库,打开Components目录,如下图所示:
文件名称 | 描述 |
---|---|
bsp | 板级支持包,用于适配 TI 官方开发板的硬件资源。 |
driverlib | 驱动程序链接库,存放 TI 官方未开源驱动程序的链接库。 |
hal | 硬件抽象层,存放各种驱动程序。 |
mac | 媒体介质访问控制,实现物理层通信及 IEEE 802.15.4 协议。 |
mt | 监视层,为监视协议栈各层的运行状态提供支持。 |
osal | 操作系统抽象层,是 Z-Stack 3.0.2的操作系统。 |
services | 提供一些公共的、常用的功能。 |
stack | ZigBee 协议的实现。 |
usblib | USB 链接库,芯片支持 USB(比如 CC2538)时需要用到的功能。 |
zmac | 属于 mac 层的内容。 |
(3)Documents目录:
存放了Z-Stack开发辅助的相关文档,打开Documents目录,如下图所示:
文件名称 | 描述 |
---|---|
API | 存放 Z-Stack 3.0 相关 API 的说明文档。 |
CC2530 | 存放适用于 CC2530 型号 MCU 的相关文档。 |
CC2538 | 存放适用于 CC2538 型号 MCU 的相关文档。 |
Z-Stack 3.0 Developer's Guide.pdf | Z-Stack 3.0 开发指导。 |
Z-Stack 3.0 Sample Application User's Guide.pdf | Z-Stack 3.0 应用例程指导。 |
Z-Stack OTA Upgrade User's Guide.pdf | OTA(Over The Air)空中升级说明指导。 |
(4)Projects目录:
存放了ZigBee应用例程的工程文件和源码文件,打开Projects目录,如下图所示:
其中:
- tools:存放与ZigBee例程应用相关的工具。
- zstack:存放ZigBee相关例程。
打开zstack文件夹,如下图所示:
文件夹名称 | 描述 |
---|---|
HomeAutomation | 面向家庭自动化领域的相关例程。 |
Libraries | 存放链接库文件,TI 不开放的源代码会被编译为链接库的形式提供给开发者使用。 |
OTA | OTA(Over The Air)空中升级例程。 |
Tools | 存放工程配置相关的文件。 |
Utilities | 公共文件夹。 |
ZMain | 存放主函数所在的源代码文件及系统硬件启动相关的源代码文件。 |
ZNP | ZNP(ZigBee And Processor)例程。 |
(5)其他目录:
文件名或文件夹名 | 描述 |
---|---|
Tools | 存放开发调试相关的工具。 |
EULA.pdf | 版权声明文件。 |
Getting Started Guide - CC2530.pdf | 针对 CC2530 型号 MCU 的开始向导文件。 |
Getting Started Guide - CC2538.pdf | 针对 CC2538 型号 MCU 的开始向导文件。 |
Z-Stack 3.0 Release Notes.txt | Z-Stack 3.0 发布描述文件。 |
Z-Stack 3.0 Software Development Kit Resource Guide.html | Z-Stack 3.0 开发资源向导链接文件。 |
Z-Stack Core Release Notes.txt | Z-Stack Core 发布描述文件。 |
Z-Stack_3.0.1_Manifest.html | Z-Stack 3.0 关键信息描述清单。 |
.iss | 此文件夹为隐藏文件夹,存放用于卸载 Z-Stack 3.0 的相关文件。 |
二、Z-Stack 3.0.2 工程框架:
在 Projects\Zstack\HomeAutomation 目录下,包含:进入其中一个文件夹,选择相应的 Demo 工程文件并双击,即可打开一个 Demo 工程。
(1)工程组织结构:
打开 GenericApp 演示项目工程后,IAR 软件左边出现如下图所示的 Z-Stack 协议栈目录结构:
其中:
组名称 | 说明 |
---|---|
App | 存放应用程序相关源代码文件。 |
BDB | 实现 ZigBee BDB(Base Device Behavior,设备基础行为)功能。 |
GP | 实现 ZigBee GP(Green Power,绿色能源)功能。 |
HAL | 硬件抽象层,存放各种驱动程序。 |
MAC | 媒体介质访问控制,实现物理层通信及 IEEE 802.15.4 协议。 |
MT | 监视层,为监视协议栈各层的运行状态提供支持。 |
NWK | ZigBee 网络层。 |
OSAL | 操作系统抽象层。 |
Profile | 存放 ZigBee 标准化定义及相关功能实现的源代码文件。 |
Security | 实现安全相关服务。 |
Services | 提供一些公共的、常用的功能。 |
Tools | 存放工程配置相关的文件。 |
ZDO | 存放 ZDO(ZigBee Device Object,ZigBee 设备对象)相关源代码文件。 |
ZMac | 属于 mac 层的内容。 |
ZMain | 存放主函数所在的源代码文件及系统硬件启动相关的源代码文件。 |
Output | 存放工程编译/链接时输出的文件。 |
在工程中可以选择不同的ZigBee网络设备类型。单击选项卡→选择网络设备类型,如下图所示:
之前有介绍到,ZigBee网络设备类型有3种,分别是Coordinator(协调器),Router(路由器)和EndDevice(终端设备)。
选项 | 含义 |
---|---|
CoordinatorEB | ZigBee 协调器。 |
RouterEB | ZigBee 路由器。 |
EndDeviceEB | ZigBee 终端设备。 |
EndDeviceEB-OTAClient | 支持 OTA(Over The Air)空中升级的 ZigBee 终端设备。 |
RouterEB-OTAClient | 支持 OTA(Over The Air)空中升级的 ZigBee 路由器。 |
(2)工程编译及链接:
选择Coordinator(协调器),点击重建所有:
编译及链接过程没有错误和警告。
三、Z-Stack OSAL调度原理:
(1)OSAL简介:
OSAL(Operating System Abstraction Layer,系统抽象层),可以通俗地理解为一个简化版的操作系统,为Z-Stack的正确运行提供了内存管理、中断管理和任务调度等基本功能。
(2)OSAL任务调度原理:
OSAL的任务调度其实是初始化任务池以及轮询任务池。
以SampleSwitch为例:
打开ZMain.c文件:
main函数如下:
/*********************************************************************
* @fn main
* @brief 启动后调用的第一个函数。
* @return 无关心
*/
int main(void)
{
// 禁用中断,防止在初始化过程中被打断
osal_int_disable(INTS_ALL);
// 初始化与板子相关的功能,如LED等
HAL_BOARD_INIT();
// 确保供电电压足够高以运行
zmain_vdd_check();
// 初始化板子的I/O
InitBoard(OB_COLD);
// 初始化HAL驱动程序
HalDriverInit();
// 初始化NV(非易失性)系统
osal_nv_init(NULL);
// 初始化MAC层
ZMacInit();
// 确定扩展地址
zmain_ext_addr();
#if defined ZCL_KEY_ESTABLISH
// 初始化Certicom证书信息,用于密钥建立
zmain_cert_init();
#endif
// 初始化基本的NV项
zgInit();
#ifndef NONWK
// 由于AF不是一个任务,调用其初始化例程
afInit();
#endif
// 初始化操作系统
osal_init_system();
// 允许中断
osal_int_enable(INTS_ALL);
// 最终的板子初始化
InitBoard(OB_READY);
// 显示有关此设备的信息
zmain_dev_info();
/* 在LCD上显示设备信息 */
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
#ifdef WDT_IN_PM1
/* 如果使用WDT(看门狗定时器),这是启用它的好地方 */
WatchDogEnable(WDTIMX);
#endif
osal_start_system(); // 从此处开始运行操作系统,不会返回
return 0; // 不应该到达这里。
} // main()
其中有两个关键的函数调用:
// 初始化操作系统任务池
osal_init_system();
// 轮询任务池
osal_start_system();
osal_init_system()定义如下:
/*********************************************************************
* @fn osal_init_system
*
* @brief
*
* This function initializes the "task" system by creating the
* tasks defined in the task table (OSAL_Tasks.h).
*
* @param void
*
* @return SUCCESS
*/
uint8 osal_init_system(void)
{
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// Initialize the Memory Allocation System
// 初始化内存分配系统,用于管理动态内存的分配和释放
osal_mem_init();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
// Initialize the message queue
// 初始化消息队列,用于任务间的消息传递和处理
osal_qHead = NULL;
// Initialize the timers
// 初始化定时器系统,用于处理系统中的定时任务和时间管理
osalTimerInit();
// Initialize the Power Management System
// 初始化电源管理系统,用于管理设备的功耗和电源状态
osal_pwrmgr_init();
#ifdef USE_ICALL
/* Prepare memory space for service enrollment */
// 为服务注册准备内存空间,用于ICall服务的内存管理
osal_prepare_svc_enroll();
#endif /* USE_ICALL */
// Initialize the system tasks.
// 初始化系统任务,创建任务表中定义的任务并设置任务环境
osalInitTasks();
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// Setup efficient search for the first free block of heap.
// 设置高效的堆内存搜索,优化内存分配的性能
osal_mem_kick();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
#ifdef USE_ICALL
// Initialize variables used to track timing and provide OSAL timer service
// 初始化用于跟踪时间和提供OSAL定时器服务的变量
osal_last_timestamp = (uint_least32_t)ICall_getTicks();
osal_tickperiod = (uint_least32_t)ICall_getTickPeriod();
osal_max_msecs = (uint_least32_t)ICall_getMaxMSecs();
/* Reduce ceiling considering potential latency */
// 考虑到可能的延迟,减少最大毫秒数的上限
osal_max_msecs -= 2;
#endif /* USE_ICALL */
return (SUCCESS);
}
在该函数中可以看到一个任务池初始化函数osalInitTasks(),它的作用是初始化任务池。
osal_start_system()定义如下:
/*********************************************************************
* @fn osal_start_system
*
* @brief
*
* This function is the main loop function of the task system (if
* ZBIT and UBIT are not defined). This Function doesn't return.
*
* @param void
*
* @return none
*/
void osal_start_system(void)
{
#ifdef USE_ICALL
/* Kick off timer service in order to allocate resources upfront.
* The first timeout is required to schedule next OSAL timer event
* as well. */
// 启动定时器服务,提前分配资源。第一次超时用于安排下一个OSAL定时器事件。
ICall_Errno errno = ICall_setTimer(1, osal_msec_timer_cback,
(void *)osal_msec_timer_seq,
&osal_timerid_msec_timer);
if (errno != ICALL_ERRNO_SUCCESS)
{
// 如果设置定时器失败,调用ICall_abort()进行错误处理
ICall_abort();
}
#endif /* USE_ICALL */
#if !defined(ZBIT) && !defined(UBIT)
for (;;) // 永远循环,作为任务系统的主循环
#endif
{
// 运行系统任务,处理任务调度和执行
osal_run_system();
#ifdef USE_ICALL
// 等待ICall事件,ICALL_TIMEOUT_FOREVER表示无限期等待
ICall_wait(ICALL_TIMEOUT_FOREVER);
#endif /* USE_ICALL */
}
}
在osal_start_system()函数的主循环中,循环调用了 osal_run_system()函数,该函数主要作用是轮询任务池。
osal_run_system()函数 中这个do-while循环:
它的主要作用是轮询整个任务池,检查是否有需要处理的任务。循环中只有一个条件判断,如果条件成立,就结束循环。
其中,tasksEvents 是一个 uint16 类型的数组,每个元素表示一种类型的任务也就是任务池。tasksCnt 是这个任务池的大小。
循环的运行逻辑是这样的:
- 首先,idx 的初始值为 0。
- 当 tasksEvents[idx] 的值为 0 时,表示该任务没有事情要处理,这时候条件判断不成立,进入下一次循环。
- 每执行一次循环前,idx 加 1,然后判断是否小于 tasksCnt。
- 当 tasksEvents[idx] 的值不等于 0 时,表示该任务中有事情要处理,这时候条件判断成立,于是通过 break 结束循环。
- 当循环结束后,如果整个任务池中都没有任务要处理,那么 idx 必定会 >= tasksCnt。因此,如果 idx < tasksCnt,表示现在任务池中有任务需要处理,并且 tasksEvents[idx] 就是当前需要处理的任务。因此在循环结束后,Z-Stack会 用 if (idx < tasksCnt) 语句来判断有没有任务需要处理。
(3)任务与事件:
每个任务中可能包含一系列待处理的事情,这些待处理的事情可以通俗地称为“事件”,例如一个任务中可以包含打开 LED 灯、读取温湿度和查看设备状态3 个事件(待处理的事情)。
tasksEvents 中的每个元素都是一个 uint16 类型的变量,每一个元素都表示了一个任务,并且储存了这个任务中包含的一系列事件。