0x01 Z-stack协议栈
1.Z-stack协议栈是什么?
非人话:T1公司推出的ZigBee规范的商用协议
人话:为了使Zigbee的开发更加简单高效,实现各个功能的实例框架代码
我的理解就是一个框架,我们想要实现自己的功能程序,只需要在协议栈的基础上修改或添加即可
由物理层、MAC层、网络层和应用层组成,由于Z-Stack协议栈是一个半开源的协议栈,MAC层和网络层的部分源代码是非开源的,因此我们学习的开源部分,主要包括main函数、APP层、ZDO层、NWK层和HAL层。
因为是框架,并不需要都使用到,只简单记录使用到的部分
2.协议栈具体内容
1)main函数
main函数具体做量两件事:系统初始化和调用函数Osal_start_system()来启动协议栈
协议栈的运行机制:Zmain.c中的main函数是整个协议栈的入口,其中包含了各种硬件的初始化和协议栈的初始化
直到调用osal_start_system()函数,协议栈开始真正运行
这些函数就是各种硬件的初始化和协议栈的初始化
2)osal_start_system()函数
- 调用后不再返回,意思就是运行这个函数后,这个函数后面的语句不会再执行
- 函数里面存着for死循环,这也是为什么我们自定义的初始化函数需要放在osal_start_system之前,放在后面是不会被执行的
1 | led_init(); |
3)APP层分析
Z-Stack协议栈的APP层主要功能是实现用户定义的事件,
如果要编写应用程序,涉及三个文件,用户只需要添加这几个文件,编写自己的任务处理函数就可以了
主文件–GenericApp.c:存放具体的任务事件处理函数,有两个功能
- 对应用层用户定义的任务进行初始化
- 调用事件处理函数GenericApp_ProcessEvent()
主文件的头文件—SampleApp.h
操作系统接口文件—OSAL_SampleApp.c:专门存放任务处理函数数组tasksArr[]的文件,实现Z-stack代码的公用
目前为止整个实训只需要对GenericApp.c文件操
4)任务和事件
在ZSTACK里按照代码功能来划分,分成不同的层:硬件层、网络层、应用层。每一个层都是一个任务
每个任务分配一个一个字节的唯一编号。然后每个任务要做很多事情,这个称为事件
Zigbee事件分为两类,系统定义事件和用户定义事件
- 系统类事件是协议栈已定义好的
- 用户类事件是我们用户层面(APP层)来定义的
重点:事件类号采用一个16bit的常量,只有一个bit为1,其他全为0
事件类号编码使用独热码编码,独热码是只有一个bit为1,其他全为0的一种码制,
- 提取系统类事件:events & SYS_EVENT_MSG
- 清除系统类事件:events ^ SYS_EVENT_MSG
- 由于事件类号是16bit,因此zigbee事件类只有16个,系统类事件SYS_EVENT_MSG使用0x8000,所以自行一事件类只有15个
5)Generic_ProcessEvent()函数
原型:UINT16 Generic_ProcessEvent( byte task_id, UINT16 events )
事件处理函数,它会把events包含的事件进行处理
两个参数:task_id任务号和events事件类号
事件处理函数首先根据事件类号来判断是何种类型事件,然后根据任务号得到消息指针pMsg,最后根据消息指针结构里的事件号pMsg->event来具体处理事件,event为8bit的常量,系统event在ZComDef.h里定义
6)osal_set_event()函数
设置任务事件函数
uint8 osal_set_event(uint8 task_id,uint16 event_flag)
- @task_id:任务编号,应用层为GenericApp_TaskID
- @event_flag:事件编号,就是在GenericApp.h中定义的宏
7)osal_start_timerEx(函数)
延时设置任务事件函数
uint8 osal_start_timerEx( uint taskID,uint16 event_flag,uint16 timeout_value);
- @task_id:任务编号,应用层为GenericApp_TaskID
- @event_flag:事件编号,就是在GenericApp.h中定义的宏
- @timeout_value:延时时间,单位为ms
注意:该函数调用后会立马返回,这个延时功能是用定时器实现的。该函数调用多次不会多次添加事件,只是重新设置延时时间而已。
0x02 协议栈工程
1.协议栈模块添加
使用时候只需要按照第二行展示的路径打开工作空间便可以了,打开的工作空间便是协议栈的框架,我们只需要在此基础上进行修改即可
Z-Stack协议软件包中提供了3个典型的用户示例工程,如下图,后续使用的是通用用户示例工程
当我们切换选项卡时所有的配置需要重新进行设置
2.功能模块的添加
1).添加led.c 和led.h
- 1)把led.c 和led.h 复制到Source目录中
- 2)在App文件组中单击右键添加.c文件,.h文件
2).添加头文件
在Zmain.c和GenericApp.c中添加包含头文件命令
1
3).添加函数
LED初始化函数放到main函数末尾osal_start_system()的前面
其他的初始化或功能函数也是如此,只能放前面,不能放后面,原因前面有介绍
4).其他操作
编译若发现错误可能是由于IAR编译器语法要求比较严格,需要Options–>C/C++ Compiler把 Require prototype勾去掉
在GenericApp.c文件中的GenericApp_ProcessEvent函数中的switch语句的最后一个case进行修改,用if else使模式在不同的工作状态下,亮不同的灯。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19case ZDO_STATE_CHANGE:
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if (GenericApp_NwkState == DEV_ZB_COORD) // 网络状态变成协调器
{
led_ctrl(LED0,LED_ON);
led_ctrl(LED1,LED_ON);
osal_set_event(GenericApp_TaskID,GENERICAPP_MY_EVT1);
}
else if(GenericApp_NwkState == DEV_ROUTER) // 网络状态变成路由器
{
led_ctrl(LED0,LED_ON);
led_ctrl(LED1,LED_OFF);
}
else if(GenericApp_NwkState == DEV_END_DEVICE) // 网络状态变成网络终端
{
led_ctrl(LED0,LED_OFF);
led_ctrl(LED1,LED_ON);
}
break;去掉f8w2530.xcl文件中Include these two lines when generating a .hex file for banked code model: 后面两行
3.事件的定义和添加
1).定义事件宏
在GenericApp.h中定义事件的宏,也就是定义事件的16位bit编码
1
2
// 只能有一位是1,其余的都是0
2)定义事件要做的事情
在GenericApp.c文件中的GenericApp_ProcessEvent()函数定义事件要做的事情
在事件处理函数中添加事件处理代码
1 | if ( events & GENERICAPP_MY_EVT2 ) |
1 | if (GenericApp_NwkState == DEV_ZB_COORD) // 网络状态变成协调器 |