实训Day03

2021-05-26

实训, Z-stack

0x01 Z-stack协议栈

1.Z-stack协议栈是什么?

  • 非人话:T1公司推出的ZigBee规范的商用协议

  • 人话:为了使Zigbee的开发更加简单高效,实现各个功能的实例框架代码

    我的理解就是一个框架,我们想要实现自己的功能程序,只需要在协议栈的基础上修改或添加即可

  • 由物理层、MAC层、网络层和应用层组成,由于Z-Stack协议栈是一个半开源的协议栈,MAC层和网络层的部分源代码是非开源的,因此我们学习的开源部分,主要包括main函数、APP层、ZDO层、NWK层和HAL层

image-20210527234143880

因为是框架,并不需要都使用到,只简单记录使用到的部分

2.协议栈具体内容

1)main函数

  • main函数具体做量两件事:系统初始化和调用函数Osal_start_system()来启动协议栈

  • 协议栈的运行机制:Zmain.c中的main函数是整个协议栈的入口,其中包含了各种硬件的初始化和协议栈的初始化

  • 直到调用osal_start_system()函数,协议栈开始真正运行

image-20210527234849563

这些函数就是各种硬件的初始化和协议栈的初始化

2)osal_start_system()函数

  • 调用后不再返回,意思就是运行这个函数后,这个函数后面的语句不会再执行
  • 函数里面存着for死循环,这也是为什么我们自定义的初始化函数需要放在osal_start_system之前,放在后面是不会被执行的
1
2
3
4
led_init();
exit_init();
// 执行完上面的函数之后开始调用osal_start_system函数,协议栈正式运行
osal_start_system(); // No Return from here

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.协议栈模块添加

image-20210528001010246

  • 使用时候只需要按照第二行展示的路径打开工作空间便可以了,打开的工作空间便是协议栈的框架,我们只需要在此基础上进行修改即可

  • Z-Stack协议软件包中提供了3个典型的用户示例工程,如下图,后续使用的是通用用户示例工程

    image-20210528001421360

  • 当我们切换选项卡时所有的配置需要重新进行设置

image-20210528001610273

2.功能模块的添加

1).添加led.c 和led.h
  • 1)把led.c 和led.h 复制到Source目录中
  • 2)在App文件组中单击右键添加.c文件,.h文件
2).添加头文件
  • 在Zmain.c和GenericApp.c中添加包含头文件命令

    1
    #include "led_key.h"
3).添加函数
  • LED初始化函数放到main函数末尾osal_start_system()的前面

    其他的初始化或功能函数也是如此,只能放前面,不能放后面,原因前面有介绍

4).其他操作
  • 编译若发现错误可能是由于IAR编译器语法要求比较严格,需要Options–>C/C++ Compiler把 Require prototype勾去掉

    image-20210528002318607

  • 在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
    19
    case 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: 后面两行

    image-20210528002644649

3.事件的定义和添加

1).定义事件宏
  • 在GenericApp.h中定义事件的宏,也就是定义事件的16位bit编码

    1
    2
    #define GenericApp_MY_EVT           0X0004
    // 只能有一位是1,其余的都是0
2)定义事件要做的事情
  • 在GenericApp.c文件中的GenericApp_ProcessEvent()函数定义事件要做的事情

    在事件处理函数中添加事件处理代码

1
2
3
4
5
6
7
8
9
10
11
if ( events & GENERICAPP_MY_EVT2 )
{
// 事件要做的事
P1_0 = !P1_0;
P1_1 = !P1_1;
unsigned char str[32] = {"hello,are you OK?\r\n"};
HalUARTWrite(HAL_UART_PORT_0,str,strlen((char *)str));
// osal_start_timerEx(GenericApp_TaskID,GENERICAPP_MY_EVT1,1000);
// return unprocessed events
return (events ^ GENERICAPP_MY_EVT2); // 清零
}
1
2
3
4
if (GenericApp_NwkState == DEV_ZB_COORD) // 网络状态变成协调器
{
osal_set_event(GenericApp_TaskID,GENERICAPP_MY_EVT1);
}

附录

ZigBee学习笔记八-浅析协议栈

ZigBee 事件相关