zigbee协议栈 任务、事件与轮询机制
typedef unsigned char uint8 只占一个字节,即二进制的8位,0b00000000,16进制的两位0x00;
typedef unsigned short uint16 只占两个字节,即二进制的16位,0b0000000000000000,16进制的四位 0x0000
协议栈中有三个变量至关重要:
l tasksCnt保存了任务的总个数
uint8 tasksCnt
l tasksEvents这是一个指针(可以看做数组),作为事件表,数组的索引是任务ID号,每一元素对应了该任务下的所有事件,这个事件可以拆分为小事件;
uint16 *tasksEvents
l tasksArr——这是一个数组,数组中的每一项都是一个函数指针,指向了任务事件的处理函数。数组的索引是任务的ID号,该ID号下的元素就是对应任务的事件处理函数,事件处理函数利用switch将该任务下的所有事件处理;
pTaskEventHandlerFntasksArr[] ,pTaskEventHandlerFn 是函数指针
typedef unsignedshort (*pTaskEventHandlerFn) (unsigned char task_id, unsigned short event)
表明,pTaskEventHandlerFn是一个指向返回值为(unsigned short)形参为(unsigned char task_id ,unsigned short event)的函数的指针
tasksEvents中的元素为一个16位二进制数,zigbee协议栈用一位二进制来定义事件,为1表示有事件,为0表示无事件,如任务ZDAppTaskID下的事件为
事件 |
十六进制 |
二进制 |
ZDO_NETWORK_INIT |
0x0001 |
0b0000000000000001 |
ZDO_NETWORK_START |
0x0002 |
0b0000000000000010 |
ZDO_DEVIEC_RESET |
0x0004 |
0b0000000000000100 |
ZDO_COMMAND_CNF |
0x0008 |
0b0000000000001000 |
ZDO_START_CHANGE_EVT |
0x0010 |
0b0000000000010000 |
ZDO_ROUTER_START |
0x0020 |
0b0000000000100000 |
ZDO_NEW_DEVICE |
0x0040 |
0b0000000001000000 |
ZDO_DEVICE_AUTH |
0x0080 |
0b0000000010000000 |
ZDO_SCEMGR_EVENT |
0x0100 |
0b0000000100000000 |
ZDO_NWK_UPDATE_NV |
0x0200 |
0b0000001000000000 |
ZDO_FRAMECOUNTER_CHANGE |
0x0400 |
0b0000010000000000 |
如上图,这样二进制的每一位的1可以定义为一个事件,理论上一个任务下可以定义16个事件。这样的好处是,事件与事件之间可以用二进制加法处理即异或算法相加。
比如:tasksEvents[ZDAppTaskID]=0x0003 ,由于 0x0003=0x0001^0x0002,所以可以看做ZDO_NETWORK_INT+ZDO_NETWORK_START,所以此时该任务下的事件有两个,即ZDO_NETWORK_INT和ZDO_NETWORK_START。提取的时候可以利用与运算来提取。 “与”运算能用来判断二进制数的某一位是否为1。由于二进制的减法运算与加法运算相同,所以也可以通过加法异或运算来清零某一已经处理过的事件。
全zigbee协议栈最重要的就是 void osal_start_system( void )函数,整个轮询机制也在这个函数中被完成
第5行,定义了一个变量idx,用来标识任务,(如任务0),用来在事件表和函数表中索引;
第6、7行,更新系统时钟,同时查看硬件方面是否有事件发生,如串口是否接收到数据、是否有按键按下等信息,这部分内容在此暂不考虑;
第9~15行,idx从零增大到tasksCnt,依次遍历事件表,查看哪个任务idx对应的事件不为空,当找到任务idx的事件不为空时停止遍历,转而去调用该任务的事件处理函数,不妨设此时的idx = 8;
第19~22行,将该任务8的事件取出来放到events变量中,由于事件值被取出来了,所以对应事件表中的元素值要清零,即tasksEvents[8] = 0;
第25行,由于任务事件处理函数表的元素索引与任务事件表的索引一一对应,所以直接将8作为索引带入任务事件处理函数表就可以调用任务8的事件处理函数(tasksArr[8])(8,events),而且由于函数表中的事件处理函数是以函数指针的形式给出的,所以还需要填写形参,分别是任务id号8,和从任务8的事件events。形参中有events天经地义,因为需要分辨events中含有哪些具体事件,形参中为什么有任务id号呢?是因为,在处理任务的时候可能需要调用其他与任务绑定的函数。同时,任务处理函数的返回值也是任务值,UINT16,返回的是未被处理的具体事件。
第32行,将任务8未被处理的具体事件放回事件表中任务8对应的事件元素中。
然而,以上的机制可以解释Zigbee是怎样处理一个任务下面的事件的,但处理后的事件表是被清零的,那是谁来给任务的这些事件来置一呢?
osal_set_event(uint8 task_id , uint16 event_flag ) ;由它来给事件表中的元素赋值。
函数原型为:
uint8osal_set_event( uint8 task_id , uint16 event_flag )
{
if( task_id < tasksCnt)
{
halIntState_t intState ;
HAL_ENTER_CRITICALL_SECTION( inState ) ; //关中断
tasksEvents[task_id] |= event_falg ; //给任务task_id设置事件event_flag
HAL_EXTI_CRITICALL_SECTION(inState ) ; //开中断
return( SUCCESS )
}
else
{
return( INVALID_TASK )
}
}
如,原来的tasksEvents[ZDAppTaskID] = 0x0000,即此任务下无事件可以处理,当我调用
osal_set_event(uint8 ZDAppTaskID , ZDO_NETWORK_INIT )后 tasksEvents[ZDAppTaskID] = 0x0001,这样当进入轮询时就会调用事件处理函数处理该事件。
知道了任务下的事件是如何被设置、处理的,我们知道任务ID是将事件表和事件处理函数联系起来的关键,那么任务本身是怎样被设置的呢?
添加任务的关键是
l 新任务的初始化函数
l 新任务的事件处理函数
如要添加任务GenericApp则其初始化函数GenericApp_Init( taskID ),事件处理函数GenericApp_ProcessEvent应该分别放在osalInitTasks函数、事件处理函数表tasksArr[]的最后,而tasksEvent[]的大小会随着tasksArr[]的大小一起变化,即当在tasksArr[]最后一位加入GenericApp_ProcessEvent时,tasksEvent[]也随着增加一个元素用来安放对应的事件。
为了方便看出原理,将taskArr[]、osalInitTasks()简化处理得到
值得一提的是,taskArr[]是先于tasksEvents[]声明定义的,所以可以根据taskArr[]的长度来设置tasksEvents[]的长度与任务相匹配。
由osalInitTask(),任务ID号从0开始,按先后顺序分别分配给各个任务,所以这个ID号与数组的索引也是相匹配的,设备必须的任务优先,从上图看到,用户自定义的任务ID应该是8。正是借助这个ID号将任务事件表中的事件元素和事件处理函数表中的事件处理函数对应了起来。
2018年1月22日星期一
武汉大学青楼
本文参考自:《ZigBee无线传感器网络设计与实现》 王小强等人编著化学工业出版社
zigbee协议栈 任务、事件与轮询机制相关推荐
- Node中的事件轮询机制
文章目录 2 node中的事件循环模型 2-1 一些属性 2-2 循环模型 node事件循环总共有==六个阶段== process.nextTick()函数 __实例__ 2 node中的事件循环模型 ...
- 轮询机制php,JS事件轮询机制讲解
JS是单线程语言,深入理解JS里的Event Loop,本文主要和大家分享JS事件轮询机制,希望能帮助到大家. JS的执行机制(一): 1.首先判断JS是同步还是异步,同步就进入主进程,异步就进入ev ...
- 事件轮询机制和promise(怪异机制?)
事件轮询机制 任务队列分为 宏任务队列 微任务队列 当碰上异步代码时,会将异步代码传给web API 由web API 将异步代码放到任务队列中,script整体代码.setTimeout.set ...
- nodejs的事件轮询机制
/* 面试题:nodejs的事件轮询机制*/setTimeout(function() {console.log('setTimeout()') }, 0)setImmediate(function( ...
- android全局轮询机制,Android轮询机制
在消息的获取上是选择轮询还是推送得根据实际的业务需要来技术选型,例如对消息实时性比较高的需求,比如微博新通知或新闻等那就最好是用推送了.但如果只是一般的消息检测比如更新检查,可能是半个小时或一个小时一 ...
- 【状态保持】Cache 基于SQL 数据库 的缓存依赖 轮询机制详解
首先声明一下如果您还不了解什么是Cache(缓存)请您先搜一下有关信息然后再看这篇文章. 当数据库中的信息发生变化的时候,应用程序能够获取变化的通知是缓存依赖得以实现的基础.应用程序可以通过两种途径获 ...
- (原创)Linux设备轮询机制分析
一. 设备轮询机制的基本思想 所谓的设备轮询机制实际上就是利用网卡驱动程序提供的NAPI机制加快网卡处理数据包的速度,因为在大流量的网络环境当中,标准的网卡中断加上逐层的数据拷贝和系统调用会占用大量的 ...
- MCDF中arbiter的轮询机制
更新的MCDF的仲裁器采用的是Round Robin轮询的一个仲裁机制,简单来说就是最先被授权的通道在下一次仲裁中优先级会变为最低,这样可以保证每个通道都能够获得授权,而不是一个通道长时间连续的进行数 ...
- 码支付如何对接网站_第四方聚合支付特色之一的轮询机制是怎么防风控的?
简单地说,聚合支付是融合了支付宝.微信支付.花呗.翼支付等多种支付方式的一种"包容性"支付工具.做支付系统一定不能脱离实际业务场景,更不能照搬其他公司方案. 做聚合支付的服务商越来 ...
- STM32基于时间片轮询机制
1. 基于时间片的轮询调度算法(仅局限单核CPU芯片): 利用定时器为每个任务函数设定执行时间间隔,保证使用频率高的函数多次被调用,提高单核芯片的资源利用率.如果只是简单地将A.B两个函数放在whil ...
最新文章
- 新人新博客新学习家园
- 设计模式原则(单一、开放封闭、里氏代换、依赖倒转、迪米特法则五大原则)...
- MATLAB机器学习系列-8 极限学习机(Extreme Learning Machine, ELM)原理及其代码实现
- android:descendantFocusability用法简析
- php 后退按钮事件,php – 后退按钮的会话问题
- Go语言实战 : API服务器 (4) 配置文件读取及连接数据库
- asp判断是否移动端_asp判断用户端是电脑访问还是移动设备方法
- [并查集][排序][dfs][启发式合并] JZOJ P3635 Peaks
- oracle中affirm,2.Oracle Data Guard 参数介绍
- DeepMind登上Science:“和AI相比,人类都是猪队友”,团战称霸雷神之锤3
- 虚拟机里linux7关不了,虚拟机上CentOS 7关闭防火墙操作
- TortoiseSVN文件夹没有绿色对号(√)
- windows7 快捷方式 图标显示异常的解决
- ftl不存在为真_判断一件书法作品是否具备收藏价值可以归纳为四个字:真、优、高、古。...
- 全同态加密(FHE):BV方案、密钥切换、模约化、自举
- 热烈庆祝《Java多线程》上线啦!
- 计算机开机怎么设置网络连接,电脑怎么设置开机自动连接宽带
- 压缩感知稀疏基之离散余弦变换(DCT)和离散正弦变换(DST)
- 贷款减值准备和折现回拨
- 如何使用轻量应用服务器自带的Cloudreve应用镜像搭建属于自己的云网盘?