企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
* 导师视频讲解:[**去听课**](https://www.bilibili.com/video/BV1k34y1D7Vz?p=27) >[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/zigbee) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> 本节课的内容是后续课程的基础,希望大家能够好好学习,为后续课程打下良好的基础。 <br/> ## **事件的类型与编码**: 读者可以发现每个层次的事件处理函数的参数都包含1个task id和1个events参数,例如: * MAC层事件处理函数,如图所示。 ![](https://img.kancloud.cn/af/53/af535ca140b59c7f9bedb4112e9ce870_1378x691.png =400x) ### * 网络层事件处理函数,如图所示。 ![](https://img.kancloud.cn/05/f2/05f2dcc8568d4f756e09d165721b703b_1384x666.png =400x) ### * 应用层事件处理函数,如图所示。 ![](https://img.kancloud.cn/7f/17/7f17ec477550183a9ecd7fd5dc55ee89_1380x737.png =400x) 以应用层事件处理函数为例,它的第2个参数UINT16 events表示了一个事件集合,其中包含了0个或多个待处理的事件。然而,events是一个16位的变量,它是怎么样表示一个事件集合的呢? ### 答案是Z-Stack 3.0采用了独热码(one-hot code)的方式对事件类型进行编码。 ### **events的分类** 在讲解独热码之前,先来了解一下events的分类。 * events的最高位为1时,表示这是系统事件集合,即events中的事件全是系统事件。 * events的最高位为0时,表示这是用户事件集合,即events中的事件全是用户事件。 用户事件可以由开发者自行定义其含义,以及相应的处理。 ### **使用独热码** 采用独热码的方式,把所有的**用户事件编码**列举出来,并从中分析独热码的规律,见下表。 | 2进制编码 | 16进制编码 | 事件名称 | | --- | --- | --- | | 0000 0000 0000 0001 | 0x00 01 | 用户事件A | | 0000 0000 0000 0010 | 0x00 02 |用户事件B | | 0000 0000 0000 0100 | 0x00 04 | 用户事件C | | 0000 0000 0000 1000 | 0x00 08 | 用户事件D | | 0000 0000 0001 0000 | 0x00 10 | 用户事件E | | 0000 0000 0010 0000 | 0x00 20 | 用户事件F | | 0000 0000 0100 0000 | 0x00 40 | 用户事件G | | 0000 0000 1000 0000 | 0x00 80 | 用户事件H | | 0000 0001 0000 0000 | 0x01 00 | 用户事件I | | 0000 0010 0000 0000 | 0x02 00 | 用户事件J | | 0000 0100 0000 0000 | 0x04 00 | 用户事件K | | 0000 1000 0000 0000 | 0x08 00 | 用户事件L | | 0001 0000 0000 0000 | 0x10 00 | 用户事件M | | 0010 0000 0000 0000 | 0x20 00 | 用户事件N | | 0100 0000 0000 0000 | 0x40 00 | 用户事件O | >[info] 其中事件名称可以根据实际需命名,例如开灯事件、关灯事件或者发送警告事件等。 ### 从这些编码中,可以得出2个规律: 1. 除了用于表示系统事件或者用户事件的最高位,其他15个比特位中,只有1位为1,其他位均为0。 2. 使用15个比特位表示15种用户事件。 >[info] 这两个规律也是独热码的规律。 利用规律1,可以很容易地理解为什么events可以表示一个事件集合。现在假设events的值为0000 0000 0101 0101,其中的右起第1、3、5和7位为1,于是可以理解为事件集合events包含了用户事件A、C、E和G。 ### 利用规律2,可以得到events最多可以包含15种用户事件。 <br/> ## **定义用户事件** 可以使用以下方法在zcl\_samplesw.h文件中定义一个用户事件。 1.定义事件名称和对应的编码。 ``` #define SAMPLEAPP_TEST_EVT 0x0040 ``` 2.把它复制到zcl\_samplesw.h文件中的如图所示位置。 ![](https://img.kancloud.cn/9f/4d/9f4d135f1938a281576ec14535faf839_1392x1011.png) <br/> ## **处理用户事件** 可以在zcl\_samplesw.c文件中的应用层事件处理函数中添加相关的处理,代码如下: ``` uint16 zclSampleSw_event_loop( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter //用户事件:SAMPLESW_TOGGLE_TEST_EVT if( events & SAMPLESW_TOGGLE_TEST_EVT ) { osal_start_timerEx(zclSampleSw_TaskID,SAMPLESW_TOGGLE_TEST_EVT,500); zclGeneral_SendOnOff_CmdToggle( SAMPLESW_ENDPOINT, &zclSampleSw_DstAddr, FALSE, 0 ); //消除已经处理的事件然后返回未处理的事件 return (events ^ SAMPLESW_TOGGLE_TEST_EVT); } //SYS_EVENT_MSG:0x8000表示系统事件,也就是说检测uint16最高位 if ( events & SYS_EVENT_MSG ) { //省略系统事件的处理代码 ......... // 消除系统事件标识然后返回未处理的事件 return (events ^ SYS_EVENT_MSG); } #if ZG_BUILD_ENDDEVICE_TYPE //用户事件:SAMPLEAPP_END_DEVICE_REJOIN_EVT if ( events & SAMPLEAPP_END_DEVICE_REJOIN_EVT ) { bdb_ZedAttemptRecoverNwk(); return ( events ^ SAMPLEAPP_END_DEVICE_REJOIN_EVT ); } #endif //用户事件:SAMPLEAPP_LCD_AUTO_UPDATE_EVT if ( events & SAMPLEAPP_LCD_AUTO_UPDATE_EVT ) { UI_UpdateLcd(); return ( events ^ SAMPLEAPP_LCD_AUTO_UPDATE_EVT ); } //用户事件:SAMPLEAPP_KEY_AUTO_REPEAT_EVT if ( events & SAMPLEAPP_KEY_AUTO_REPEAT_EVT ) { UI_MainStateMachine(UI_KEY_AUTO_PRESSED); return ( events ^ SAMPLEAPP_KEY_AUTO_REPEAT_EVT ); } // 处理刚才自己定义的用户事件:SAMPLEAPP_TEST_EVT if ( events & SAMPLEAPP_TEST_EVT ) { printf("Hello World!\r\n"); //消除已经处理的事件然后返回未处理的事件 return ( events ^ SAMPLEAPP_TEST_EVT ); } // Discard unknown events return 0; } ``` 通过前面讲解可以了解到,每一种用户事件类型编码中只有1位为1,其他比特位为0。SAMPLEAPP\_TEST\_EVT的事件类型编码为0x0040,其二进制数为:0000 0000 0000 0100。这个编码的右起第3为1,其余位为0。 ### 于是上述代码利用events & SAMPLEAPP_TEST_EVT让事件集合参数events与预定义的事件类型SAMPLEAPP_TEST_EVT做与运算,判断events中的右起第3位是否为1。如果为1,那么events & SAMPLEAPP_TEST_EVT的值为1,这表示事件集合参数events包含SAMPLEAPP_TEST_EVT这个事件,因此程序执行对应的处理代码,即执行: ```  printf("Hello World!\r\n");     ``` ### 接着,代码中利用events ^ SAMPLEAPP_TEST_EVT把events中的第3位清0,然后把这个值作为函数的返回值,表示events中的这个事件已经被处理了。 >[warning] 这段代码中用到了 & 和 ^ 运算,如果不了解这两种运算,需要先补习一下。 <br/> ## **触发用户事件** 前面已经定义好事件类型和对应的处理方式了,但是需要在OSAL中触发该事件后,OSAL才会执行对应的处理代码。 ### OSAL提供了专门的API来触发事件。展开OSAL层,可以找到OSAL\_Timers.h文件,如图所示。 ![](https://img.kancloud.cn/3b/1d/3b1d72df37d3aec4adf70dbbd42e136b_280x728.png =250x) ### 在OSAL\_Timers.h文件中,可以找到触发事件的API,函数声明如下: ``` uint8 osal_start_timerEx(uint8 task_id,uint16 event_id,uint32 timeout_value); ``` 该函数有三个参数,其说明如下: * task\_id:任务ID,用于标记这个事件是属于哪一个层次的任务。 * event\_id:事件ID,用于标记这个事件的类型。 * timeout\_value:表示多少毫秒后才处理这个事件。 如果希望在触发事件的3s后处理刚才自定义的事件,可在应用层初始化函数zclSampleSw\_Init()的末尾位置添加如下代码: ``` osal_start_timerEx( zclSampleSw_TaskID,//标记本事件属于应用层任务 SAMPLEAPP_TEST_EVT,//标记本事件的类型 3000);//表示3000ms后才处理这个事件 ``` 其中,zclSampleSw\_TaskID是一个全局变量,用于标记这个事件是属于应用层任务的。 ### 添加代码后的效果如图所示。 ![](https://img.kancloud.cn/7d/d7/7dd706f032756037bc5282adea11abc3_738x270.png =600x) <br/> ## **调试仿真** 右击工程名称,选择Options,如图所示。 ![](https://img.kancloud.cn/aa/f4/aaf4d84e70bc77dce45b2870ca8e5c70_1000x585.png =500x) ### 选择Debugger,然后在Driver选项卡中选择Texas Instruments,最后单击OK按钮完成设置,如图所示。 ![](https://img.kancloud.cn/46/6c/466ca2a3e4e0fc7e3b3f341c08f2ac01_1000x786.png =400x) ### 使用仿真器把配套的ZigBee开发板连接到电脑。 ### 单击Download and Debug按钮进行程序的编译、链接和下载并进入仿真模式,如图所示。 ![](https://img.kancloud.cn/81/16/81166ae4ef8ce78720f184d22911cd5b_1000x570.png =500x) ### 进入仿真模式之后,单击Go按钮运行程序,如图所示。 ![](https://img.kancloud.cn/d7/61/d76127c40858f031348f5bd4856909ae_2560x827.png =200x) ### 程序在运行3后秒会在Terminal I/O窗口中输出“Hello World!”,如图所示。 ### ![](https://img.kancloud.cn/fe/24/fe2421c96cb6508d6bd9b0e8244ff447_1000x602.png =200x) <br/> <br/> ## **项目定制** * 如需项目定制开发,可扫码添加项目经理好友(注明“**项目定制**”) * 定制范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等IoT技术方案 * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/ca/73/ca739f92cab220a3059378642e3bd502_430x430.png =200x) * 非项目定制**勿扰**,此处**非**技术支持